import { aiGenerationClient } from "../../api/aiGenerationClient";

export interface PersistedWorkbenchResultAsset {
  url: string;
  originalUrl: string;
  ossKey?: string;
  mimeType?: string;
}

interface PersistWorkbenchResultAssetInput {
  title: string;
  sourceUrl: string;
  resultType: "image" | "video";
  taskId?: string;
  prompt?: string;
  originalUrl?: string;
  existingOssKey?: string | null;
  mimeType?: string;
  client?: WorkbenchResultPersistenceClient;
}

interface DownloadedTaskAsset {
  blob: Blob;
  filename?: string;
  contentType?: string;
}

interface FetchLikeResponse {
  ok: boolean;
  status: number;
  headers: {
    get(name: string): string | null;
  };
  blob(): Promise<Blob>;
}

interface WorkbenchResultPersistenceClient {
  downloadTaskResult(taskId: string): Promise<DownloadedTaskAsset>;
  uploadAsset(input: {
    dataUrl: string;
    name?: string;
    mimeType?: string;
    scope?: string;
  }): Promise<{ url: string; signedUrl?: string; ossKey?: string }>;
  uploadAssetByUrl(input: {
    sourceUrl: string;
    name?: string;
    mimeType?: string;
    scope?: string;
  }): Promise<{ url: string; signedUrl?: string; ossKey?: string }>;
  fetchAsset?: (url: string) => Promise<FetchLikeResponse>;
}

function getDefaultClient(): WorkbenchResultPersistenceClient {
  return {
    downloadTaskResult: (taskId) => aiGenerationClient.downloadTaskResult(taskId),
    uploadAsset: (input) => aiGenerationClient.uploadAsset(input),
    uploadAssetByUrl: (input) => aiGenerationClient.uploadAssetByUrl(input),
    fetchAsset: (url) => fetch(url, { credentials: "omit" }),
  };
}

function getGeneratedResultOssKey(url: string): string | null {
  try {
    const key = decodeURIComponent(new URL(url, globalThis.location?.href || "http://localhost").pathname.replace(/^\/+/, ""));
    return /^users\/[^/]+\/generation-results\/.+/i.test(key) ? key : null;
  } catch {
    return null;
  }
}

function createFallbackResult(input: PersistWorkbenchResultAssetInput): PersistedWorkbenchResultAsset {
  return {
    url: input.sourceUrl,
    originalUrl: input.originalUrl || input.sourceUrl,
    ossKey: input.existingOssKey || getGeneratedResultOssKey(input.sourceUrl) || undefined,
    mimeType: input.mimeType,
  };
}

function getResultExtension(url: string, contentType: string | undefined, resultType: "image" | "video") {
  const mime = (contentType || "").split(";")[0]?.trim().toLowerCase();
  if (mime === "image/jpeg") return "jpg";
  if (mime === "image/png") return "png";
  if (mime === "image/webp") return "webp";
  if (mime === "image/gif") return "gif";
  if (mime === "video/webm") return "webm";
  if (mime === "video/quicktime") return "mov";
  if (mime === "video/mp4") return "mp4";

  try {
    const pathname = new URL(url, globalThis.location?.href || "http://localhost").pathname;
    const matched = pathname.match(/\.([a-z0-9]{2,5})$/i);
    if (matched?.[1]) return matched[1].toLowerCase();
  } catch {
    // Fall through to media-type fallback.
  }

  return resultType === "video" ? "mp4" : "png";
}

export function buildWorkbenchResultAssetName(
  title: string,
  sourceUrl: string,
  resultType: "image" | "video",
  contentType?: string,
): string {
  const normalized = title
    .trim()
    .replace(/[\\/:*?"<>|]+/g, "-")
    .replace(/\s+/g, " ")
    .slice(0, 80)
    .trim();
  const base = normalized || (resultType === "video" ? "generated-video" : "generated-image");
  return `${base}.${getResultExtension(sourceUrl, contentType, resultType)}`;
}

function blobToDataUrl(blob: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(String(reader.result || ""));
    reader.onerror = () => reject(reader.error || new Error("资源读取失败"));
    reader.readAsDataURL(blob);
  });
}

async function assertMediaBlobIsUsable(blob: Blob, resultType: "image" | "video") {
  const mime = String(blob.type || "").toLowerCase();
  if (/^(?:application|text)\/(?:json|xml|html|plain)|\+xml/.test(mime)) {
    throw new Error("结果资源已过期或返回了错误内容，请重新生成。");
  }

  const header = new Uint8Array(await blob.slice(0, 16).arrayBuffer());
  const asText = new TextDecoder("utf-8", { fatal: false }).decode(header);
  const isPng = header[0] === 0x89 && header[1] === 0x50 && header[2] === 0x4e && header[3] === 0x47;
  const isJpeg = header[0] === 0xff && header[1] === 0xd8 && header[2] === 0xff;
  const isGif = asText.startsWith("GIF8");
  const isWebp = asText.startsWith("RIFF") && asText.slice(8, 12) === "WEBP";
  const isMp4 = asText.slice(4, 8) === "ftyp";
  const looksLikeErrorDocument = asText.startsWith("<?xml") || asText.startsWith("<Error") || asText.startsWith("{\"") || asText.startsWith("<!DO");
  if (looksLikeErrorDocument) throw new Error("结果资源已过期或返回了错误内容，请重新生成。");

  const hasExpectedMagic =
    resultType === "video"
      ? isMp4 || mime.startsWith("video/")
      : isPng || isJpeg || isGif || isWebp || mime.startsWith("image/");
  if (!hasExpectedMagic) throw new Error("未收到有效的媒体文件。");
}

async function downloadResultBlob(
  input: PersistWorkbenchResultAssetInput,
  client: WorkbenchResultPersistenceClient,
): Promise<{ blob: Blob; contentType?: string; filename?: string }> {
  if (input.taskId) {
    try {
      const result = await client.downloadTaskResult(input.taskId);
      return {
        blob: result.blob,
        contentType: result.contentType || result.blob.type || undefined,
        filename: result.filename,
      };
    } catch {
      // Some older tasks may not support the proxy download route; fall back to the URL while it is still valid.
    }
  }

  const fetchAsset = client.fetchAsset || ((url: string) => fetch(url, { credentials: "omit" }));
  const response = await fetchAsset(input.sourceUrl);
  if (!response.ok) throw new Error(`结果资源请求失败：${response.status}`);
  const blob = await response.blob();
  return {
    blob,
    contentType: blob.type || response.headers.get("content-type") || undefined,
  };
}

export async function persistWorkbenchResultAsset(input: PersistWorkbenchResultAssetInput): Promise<PersistedWorkbenchResultAsset> {
  const fallbackResult = createFallbackResult(input);
  if (fallbackResult.ossKey) return fallbackResult;

  const client = input.client || getDefaultClient();
  const mimeType = input.mimeType || (input.resultType === "video" ? "video/mp4" : "image/png");
  const name = buildWorkbenchResultAssetName(input.title, input.sourceUrl, input.resultType, mimeType);

  // Try server-side URL-to-OSS first (no base64, no client download)
  try {
    const uploaded = await client.uploadAssetByUrl({
      sourceUrl: input.sourceUrl,
      name,
      mimeType,
      scope: "workbench-result",
    });
    return {
      url: uploaded.url,
      originalUrl: input.sourceUrl,
      ossKey: uploaded.ossKey,
      mimeType,
    };
  } catch (urlError) {
    console.warn("[workbench] URL upload failed, falling back to download+base64:", urlError instanceof Error ? urlError.message : urlError);
  }

  // Fallback: download → base64 → upload
  try {
  const downloaded = await downloadResultBlob(input, client);
  if (!downloaded.blob.size) throw new Error("结果资源内容为空");
  await assertMediaBlobIsUsable(downloaded.blob, input.resultType);

  const fallbackMime = downloaded.contentType || downloaded.blob.type || mimeType;
  const fallbackName = downloaded.filename || name;
  const dataUrl = await blobToDataUrl(downloaded.blob);
  const uploaded = await client.uploadAsset({
    dataUrl,
    name: fallbackName,
    mimeType: fallbackMime,
    scope: "workbench-result",
  });

  return {
    url: uploaded.url,
    originalUrl: input.sourceUrl,
    ossKey: uploaded.ossKey,
    mimeType: fallbackMime,
  };
  } catch (error) {
    const msg = error instanceof Error ? error.message : String(error || "");
    if (/413|too large/i.test(msg)) {
      console.warn("[workbench] OSS upload rejected (file too large), using temporary URL:", msg);
    } else {
      console.warn("[workbench] result persistence fallback:", error);
    }
    return fallbackResult;
  }
}
