import {
  REFERENCE_IMAGE_COMPRESS_THRESHOLD,
  REFERENCE_IMAGE_MAX_DIMENSION,
  REFERENCE_IMAGE_INITIAL_QUALITY,
  REFERENCE_IMAGE_MIN_QUALITY,
  type WorkbenchMode,
  type ReferenceKind,
  type ReferenceItem,
} from "./workbenchConstants";
import { resolvePreUploadedUrl } from "../../api/referenceUploadService";

export function getRatioOptionClassName(value: string) {
  return `ai-workbench-ratio-option__preview--${value.replace(":", "-")}`;
}

export function getSettingsGridColumnsClassName(columns: 2 | 3 | 4 = 3) {
  return `ai-workbench-settings-panel__grid--cols-${columns}`;
}

export function getReferenceAccept(mode: WorkbenchMode, videoFrameMode?: string) {
  if (mode === "chat") return ".docx,.txt,.md,.xlsx,.xls,.png,.jpg,.jpeg,.gif,.webp";
  if (mode === "image") return "image/*";
  if (videoFrameMode === "start-end") return "image/*";
  return "image/*,video/mp4,video/quicktime,video/webm,video/x-msvideo,.mp4,.mov,.webm,.avi,audio/mpeg,audio/mp3,audio/wav,audio/x-wav,.mp3,.wav";
}

export function getReferenceUploadLabel(mode: WorkbenchMode) {
  if (mode === "video") return "参考内容";
  if (mode === "image") return "参考图";
  return "附件";
}

export function getReferenceLimit(mode: WorkbenchMode, videoFrameMode?: string) {
  if (mode === "video" && videoFrameMode === "start-end") return 2;
  if (mode === "video") return 12;
  if (mode === "image") return 9;
  return 4;
}

export function getReferenceKindLabel(kind: ReferenceKind) {
  if (kind === "image") return "图片";
  if (kind === "video") return "视频";
  if (kind === "audio") return "音频";
  return "附件";
}

export function getReferenceEmptyCopy(mode: WorkbenchMode) {
  if (mode === "video") return "上传最多12个参考素材，首尾帧模式仅保留2张图片，输入文字或 @ 引用内容，自由组合图、文、音、视频多元素";
  if (mode === "image") return "最多上传9张参考图，输入文字或 @ 引用内容，控制角色、风格和构图";
  return "上传附件后可用 @ 引用，帮助 Agent 读取上下文";
}

export function hexToRgbTriplet(hex: string) {
  const normalized = hex.replace("#", "");
  const full = normalized.length === 3
    ? normalized
        .split("")
        .map((char) => `${char}${char}`)
        .join("")
    : normalized;

  const value = Number.parseInt(full, 16);
  const r = (value >> 16) & 255;
  const g = (value >> 8) & 255;
  const b = value & 255;
  return `${r}, ${g}, ${b}`;
}

export function inferReferenceKind(file: File, mode: WorkbenchMode): ReferenceKind {
  if (file.type.startsWith("image/")) return "image";
  if (file.type.startsWith("video/")) return "video";
  if (file.type.startsWith("audio/")) return "audio";
  return mode === "chat" ? "file" : "image";
}

export function disposeReferencePreview(item: Pick<ReferenceItem, "previewUrl">) {
  if (item.previewUrl) URL.revokeObjectURL(item.previewUrl);
}

export function fileToDataUrl(file: File) {
  return new Promise<string>((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      if (typeof reader.result === "string") {
        resolve(reader.result);
      } else {
        reject(new Error("Unable to read reference file"));
      }
    };
    reader.onerror = () => reject(reader.error || new Error("Unable to read reference file"));
    reader.readAsDataURL(file);
  });
}

export function bytesToHex(buffer: ArrayBuffer) {
  return Array.from(new Uint8Array(buffer))
    .map((byte) => byte.toString(16).padStart(2, "0"))
    .join("");
}

export async function buildReferenceFingerprint(file: File, kind: ReferenceKind) {
  if (kind === "image" && window.crypto?.subtle) {
    const digest = await window.crypto.subtle.digest("SHA-256", await file.arrayBuffer());
    return `image:${bytesToHex(digest)}`;
  }
  return `${kind}:${file.name}:${file.size}:${file.lastModified}:${file.type}`;
}

export function canCompressReferenceImage(file: File) {
  return (
    file.size > REFERENCE_IMAGE_COMPRESS_THRESHOLD &&
    file.type.startsWith("image/") &&
    !/svg|gif/i.test(file.type)
  );
}

function canvasToBlob(canvas: HTMLCanvasElement, type: string, quality: number) {
  return new Promise<Blob | null>((resolve) => {
    canvas.toBlob(resolve, type, quality);
  });
}

function getCompressedImageName(fileName: string) {
  const baseName = fileName.replace(/\.[^.]+$/, "");
  return `${baseName || "reference"}.jpg`;
}

export async function compressReferenceImageIfNeeded(file: File) {
  if (!canCompressReferenceImage(file)) {
    return { file, compressed: false };
  }

  try {
    const bitmap = await createImageBitmap(file);
    const scale = Math.min(1, REFERENCE_IMAGE_MAX_DIMENSION / Math.max(bitmap.width, bitmap.height));
    let width = Math.max(1, Math.round(bitmap.width * scale));
    let height = Math.max(1, Math.round(bitmap.height * scale));
    const canvas = document.createElement("canvas");
    const context = canvas.getContext("2d");
    if (!context) {
      bitmap.close();
      return { file, compressed: false };
    }

    const render = () => {
      canvas.width = width;
      canvas.height = height;
      context.fillStyle = "#ffffff";
      context.fillRect(0, 0, width, height);
      context.drawImage(bitmap, 0, 0, width, height);
    };

    const encode = async () => {
      let quality = REFERENCE_IMAGE_INITIAL_QUALITY;
      let nextBlob = await canvasToBlob(canvas, "image/jpeg", quality);
      while (nextBlob && nextBlob.size > REFERENCE_IMAGE_COMPRESS_THRESHOLD && quality > REFERENCE_IMAGE_MIN_QUALITY) {
        quality = Math.max(REFERENCE_IMAGE_MIN_QUALITY, quality - 0.08);
        nextBlob = await canvasToBlob(canvas, "image/jpeg", quality);
      }
      return nextBlob;
    };

    render();
    let blob = await encode();
    while (blob && blob.size > REFERENCE_IMAGE_COMPRESS_THRESHOLD && Math.max(width, height) > 960) {
      width = Math.max(1, Math.round(width * 0.82));
      height = Math.max(1, Math.round(height * 0.82));
      render();
      blob = await encode();
    }
    bitmap.close();

    if (!blob || blob.size >= file.size) {
      return { file, compressed: false };
    }

    return {
      file: new File([blob], getCompressedImageName(file.name), {
        type: "image/jpeg",
        lastModified: file.lastModified,
      }),
      compressed: true,
    };
  } catch {
    return { file, compressed: false };
  }
}

export function buildReferenceToken(kind: ReferenceKind, index: number) {
  if (kind === "image") return `@图片${index}`;
  if (kind === "video") return `@视频${index}`;
  if (kind === "audio") return `@音频${index}`;
  return `@附件${index}`;
}

export async function resolveReferenceUrls(items: ReferenceItem[]): Promise<string[]> {
  const tasks = items.map(async (item) => {
    if (item.remoteUrl) return item.remoteUrl;
    if (!item.file) {
      if (item.previewUrl && /^https?:\/\//i.test(item.previewUrl)) {
        return item.previewUrl;
      }
      return null;
    }
    const url = await resolvePreUploadedUrl(item.file, item.name, item.fingerprint);
    if (url) {
      item.remoteUrl = url;
      return url;
    }
    return null;
  });
  const results = await Promise.all(tasks);
  return results.filter((url): url is string => url !== null);
}
