/**
 * Web 端：Tus 分片上传（与 Apifox / uni 版协议对齐）；独立审核走上传服务 /upload/simple 等，不再调用 verify 审核域
 */
import { useUserStore } from "@/stores/modules/user";
import { AI_UPLOAD_FILES_PUBLIC_BASE, BASE_AI_URL, BASE_DEV_UPLOAD_SIMPLE } from "@/utils/config";

const TUS_VERSION = "1.0.0";
const TUS_CHUNK_SIZE = 1024 * 1024;

function authHeader(): string {
  try {
    return useUserStore().token || "";
  } catch {
    return "";
  }
}

function pickPayload(res: unknown): unknown {
  if (res == null) return null;
  if (typeof res === "object" && !Array.isArray(res) && (res as any).data !== undefined) {
    return (res as any).data;
  }
  return res;
}

function headerGet(headers: Headers, name: string): string {
  const v = headers.get(name) || headers.get(name.toLowerCase());
  return v ? String(v).trim() : "";
}

function pickUploadIdFromLocation(loc: string): string {
  if (!loc) return "";
  const m = String(loc).match(/\/files\/([^/?#]+)/);
  return m ? decodeURIComponent(m[1]) : "";
}

export function pickUploadIdFromResponse(res: Response, body?: unknown): string {
  const h = res.headers;
  const idHdr = headerGet(h, "upload-id") || headerGet(h, "x-upload-id");
  if (idHdr) return idHdr;
  const fromLoc = pickUploadIdFromLocation(headerGet(h, "location"));
  if (fromLoc) return fromLoc;

  const d = pickPayload(body ?? null);
  const inner = d && typeof d === "object" && (d as any).data !== undefined ? (d as any).data : d;
  const x = inner && typeof inner === "object" ? (inner as Record<string, unknown>) : {};
  const id =
    (x.upload_id as string) || (x.uploadId as string) || (x.id as string) || (typeof inner === "string" ? inner : "") || "";
  return id || "";
}

function pickFileUrlFromBody(body: unknown): string {
  const fromObj = (o: unknown): string => {
    if (!o || typeof o !== "object" || Array.isArray(o)) return "";
    const r = o as Record<string, string>;
    return r.url || r.file_url || r.fileUrl || r.access_url || r.accessUrl || r.ossUrl || r.cdnUrl || "";
  };
  const d = pickPayload(body);
  let inner = d && typeof d === "object" && (d as any).data !== undefined ? (d as any).data : d;
  let u = fromObj(inner);
  if (u) return u;
  if (inner && typeof inner === "object" && (inner as any).data !== undefined) {
    u = fromObj((inner as any).data);
  }
  if (u) return u;
  if (typeof inner === "string" && inner.startsWith("http")) return inner;
  return typeof d === "string" && d.startsWith("http") ? d : "";
}

/** 从 simple 上传或列表接口的 data 中取封面地址 */
function pickCoverUrlFromBody(body: unknown): string {
  const d = pickPayload(body);
  let cur: any = d;
  if (cur && typeof cur === "object" && cur.data !== undefined && !Array.isArray(cur.data)) {
    cur = cur.data;
  }
  if (!cur || typeof cur !== "object") return "";
  let cover = String(cur.cover || cur.coverUrl || cur.poster || cur.thumbnailUrl || cur.thumbnail || "").trim();
  const imgUrlRaw = cur.imgUrl;
  if (!cover && imgUrlRaw != null) {
    if (typeof imgUrlRaw === "object" && imgUrlRaw !== null) {
      const o = imgUrlRaw as Record<string, string>;
      cover = String(o.cover || o.coverUrl || o.poster || "").trim();
    } else if (typeof imgUrlRaw === "string") {
      const s = imgUrlRaw.trim();
      if (s.startsWith("{")) {
        try {
          const o = JSON.parse(s) as Record<string, string>;
          cover = String(o.cover || o.coverUrl || o.poster || "").trim();
        } catch {
          /* ignore */
        }
      }
    }
  }
  return cover;
}

/** simple 上传返回的 fileUrl 可能为 JSON 字符串 {"cover","video"} */
function normalizeSimpleUploadUrls(body: unknown, fileUrl: string): { fileUrl: string; coverUrl?: string } {
  let url = (fileUrl || "").trim();
  let coverUrl = pickCoverUrlFromBody(body);
  if (url.startsWith("{")) {
    try {
      const o = JSON.parse(url) as Record<string, string>;
      if (o && typeof o === "object") {
        const v = String(o.video || o.videoUrl || o.url || "").trim();
        const c = String(o.cover || o.coverUrl || o.poster || "").trim();
        if (v) url = v;
        if (c && !coverUrl) coverUrl = c;
      }
    } catch {
      /* keep url as-is */
    }
  }
  return { fileUrl: url, coverUrl: coverUrl || undefined };
}

const DEV_SIMPLE_UPLOAD_PATH = "/upload/simple";

function buildDevSimpleUploadRequestUrl(): string {
  const base = String(BASE_DEV_UPLOAD_SIMPLE || "").replace(/\/$/, "");
  if (base) {
    return `${base}${DEV_SIMPLE_UPLOAD_PATH}`;
  }
  return DEV_SIMPLE_UPLOAD_PATH;
}

/**
 * 官方相册等：multipart 直传（替代 Tus `POST .../upload`）
 * POST `/dev-upload-ailien/upload/simple`，表单字段 `file`
 */
export async function uploadFileViaDevSimpleEndpoint(file: File): Promise<{ fileUrl: string; coverUrl?: string }> {
  const reqUrl = buildDevSimpleUploadRequestUrl();
  const fd = new FormData();
  fd.append("file", file, file.name);

  const res = await fetch(reqUrl, {
    method: "POST",
    headers: {
      Authorization: authHeader()
    },
    body: fd,
    credentials: "omit"
  });

  let body: unknown = null;
  const ct = res.headers.get("content-type") || "";
  if (ct.includes("application/json")) {
    try {
      body = await res.json();
    } catch {
      body = null;
    }
  } else {
    const t = await res.text();
    try {
      body = t ? JSON.parse(t) : null;
    } catch {
      body = t ? { raw: t } : null;
    }
  }

  if (res.status < 200 || res.status >= 300) {
    const msg =
      body && typeof body === "object" && (body as any).msg != null ? String((body as any).msg) : `上传失败(${res.status})`;
    throw new Error(msg);
  }
  if (body && typeof body === "object" && (body as any).code !== undefined) {
    const c = (body as any).code;
    if (c !== 200 && c !== 0) {
      throw new Error((body as any).msg || (body as any).message || "上传失败");
    }
  }

  const rawUrl = pickFileUrlFromBody(body);
  if (!rawUrl) {
    throw new Error("上传完成但未返回文件地址");
  }
  return normalizeSimpleUploadUrls(body, rawUrl);
}

/** 创建上传会话 POST /upload */
export async function createUploadSession(data: {
  filename: string;
  size: number;
}): Promise<{ response: Response; body: unknown }> {
  const res = await fetch(`${BASE_AI_URL}/upload`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Tus-Resumable": TUS_VERSION,
      Authorization: authHeader()
    },
    body: JSON.stringify(data)
  });
  let body: unknown = null;
  const ct = res.headers.get("content-type") || "";
  if (ct.includes("application/json")) {
    try {
      body = await res.json();
    } catch {
      body = null;
    }
  } else {
    const t = await res.text();
    body = t || null;
  }
  if (res.status < 200 || res.status >= 300) {
    const msg =
      body && typeof body === "object" && (body as any).message
        ? String((body as any).message)
        : `创建上传会话失败(${res.status})`;
    throw new Error(msg);
  }
  if (body && typeof body === "object" && (body as any).code !== undefined) {
    const c = (body as any).code;
    if (c !== 200 && c !== 0) {
      throw new Error((body as any).msg || (body as any).message || "创建上传会话失败");
    }
  }
  return { response: res, body };
}

/** PATCH 二进制分片 */
export async function patchBinaryToUpload(uploadId: string, arrayBuffer: ArrayBuffer, uploadOffset = 0): Promise<Response> {
  const url = `${BASE_AI_URL}/files/${encodeURIComponent(uploadId)}`;
  const res = await fetch(url, {
    method: "PATCH",
    headers: {
      Authorization: authHeader(),
      "Tus-Resumable": TUS_VERSION,
      "Content-Type": "application/offset+octet-stream",
      "Upload-Offset": String(uploadOffset)
    },
    body: arrayBuffer
  });
  if (res.status >= 200 && res.status < 300) {
    return res;
  }
  const t = await res.text().catch(() => "");
  throw new Error(`上传失败(${res.status})${t ? ` ${t.slice(0, 200)}` : ""}`);
}

export async function patchBinaryToUploadChunked(
  uploadId: string,
  arrayBuffer: ArrayBuffer,
  onProgress?: (ratio: number) => void
): Promise<void> {
  const total = arrayBuffer.byteLength;
  if (total === 0) {
    await patchBinaryToUpload(uploadId, new ArrayBuffer(0), 0);
    onProgress?.(1);
    return;
  }
  let offset = 0;
  while (offset < total) {
    const end = Math.min(offset + TUS_CHUNK_SIZE, total);
    const chunk = arrayBuffer.slice(offset, end);
    await patchBinaryToUpload(uploadId, chunk, offset);
    offset = end;
    onProgress?.(offset / total);
  }
}

/** HEAD /files/{id} */
export async function getUploadProgress(uploadId: string): Promise<Response> {
  return fetch(`${BASE_AI_URL}/files/${encodeURIComponent(uploadId)}`, {
    method: "HEAD",
    headers: {
      Authorization: authHeader(),
      "Tus-Resumable": TUS_VERSION,
      "Content-Type": "application/octet-stream"
    }
  });
}

/** DELETE /files/{id} */
export async function deleteUploadSession(uploadId: string): Promise<void> {
  await fetch(`${BASE_AI_URL}/files/${encodeURIComponent(uploadId)}`, {
    method: "DELETE",
    headers: {
      Authorization: authHeader(),
      "Tus-Resumable": TUS_VERSION
    }
  });
}

/** POST /upload/{id}/finalize */
export async function finalizeUploadSession(uploadId: string, data: Record<string, unknown> = {}): Promise<unknown> {
  const res = await fetch(`${BASE_AI_URL}/upload/${encodeURIComponent(uploadId)}/finalize`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Tus-Resumable": TUS_VERSION,
      Authorization: authHeader()
    },
    body: JSON.stringify(data)
  });
  let body: unknown = null;
  const ct = res.headers.get("content-type") || "";
  if (ct.includes("application/json")) {
    try {
      body = await res.json();
    } catch {
      body = null;
    }
  } else {
    body = await res.text();
  }
  if (res.status < 200 || res.status >= 300) {
    throw new Error(`上传完成确认失败(${res.status})`);
  }
  if (body && typeof body === "object" && (body as any).code !== undefined) {
    const c = (body as any).code;
    if (c !== 200 && c !== 0) {
      throw new Error((body as any).msg || (body as any).message || "上传完成确认失败");
    }
  }
  return body;
}

/**
 * 是否为视频（MIME 或后缀），用于 Tus 上传命名等
 */
export function isLikelyVideoFileForAiUpload(file: File): boolean {
  if (!(file instanceof File)) return false;
  if (typeof file.type === "string" && file.type.startsWith("video/")) return true;
  const n = file.name || "";
  return /\.(mp4|m4v|webm|ogg|mov)(\?.*)?$/i.test(n);
}

/**
 * Tus 上传 → finalize → 返回公网 URL（图片/视频同一套上传；不含审核）
 */
async function uploadViaTusToPublicUrl(file: File, onProgress?: (p: number) => void): Promise<string> {
  const report = (n: number) => onProgress?.(Math.min(100, Math.max(0, Math.round(n))));
  report(5);
  const buf = await file.arrayBuffer();
  const size = buf.byteLength;
  report(18);

  const defaultName = isLikelyVideoFileForAiUpload(file) ? `video_${Date.now()}.mp4` : `img_${Date.now()}.jpg`;
  const { response: sessionRes, body: sessionBody } = await createUploadSession({
    filename: file.name || defaultName,
    size
  });
  const uploadId = pickUploadIdFromResponse(sessionRes, sessionBody);
  if (!uploadId) {
    throw new Error("创建上传会话失败：未返回 upload_id");
  }

  const publicUrl = `${AI_UPLOAD_FILES_PUBLIC_BASE}/${uploadId}`;

  report(35);
  await patchBinaryToUploadChunked(uploadId, buf, ratio => {
    report(35 + ratio * (82 - 35));
  });
  report(82);

  const finalizeBody = await finalizeUploadSession(uploadId, {});
  report(90);

  const urlFromApi = pickFileUrlFromBody(finalizeBody);
  const fileUrl = urlFromApi || publicUrl;
  if (!fileUrl) {
    throw new Error("上传完成确认失败：未返回文件地址");
  }
  return fileUrl;
}

export interface UploadFileViaAiOptions {
  /** 0～100 */
  onProgress?: (progress: number) => void;
  /** 已废弃；保留仅为兼容旧调用，不再触发独立审核服务 */
  skipModerate?: boolean;
}

/**
 * 图片与视频：Tus 上传 → finalize 得访问 URL（不再调用 verify / ai-moderate）
 */
export async function uploadFileViaAi(file: File, options: UploadFileViaAiOptions = {}): Promise<string> {
  const { onProgress } = options;

  const report = (p: number) => {
    onProgress?.(Math.min(100, Math.max(0, Math.round(p))));
  };

  const fileUrl = await uploadViaTusToPublicUrl(file, onProgress);
  report(100);
  return fileUrl;
}

/** 多文件顺序上传，整体进度 0～100 */
export async function uploadFilesViaAi(files: File[], options: UploadFileViaAiOptions = {}): Promise<string[]> {
  const list = files.filter(f => f instanceof File);
  if (!list.length) return [];

  const n = list.length;
  const urls: string[] = [];
  for (let i = 0; i < n; i++) {
    const u = await uploadFileViaAi(list[i], {
      ...options,
      onProgress: p => {
        const overall = (i / n) * 100 + (p / 100) * (100 / n);
        options.onProgress?.(overall);
      }
    });
    urls.push(u);
  }
  return urls;
}

/** 与 uni 版 aiApi 命名对齐的聚合对象，便于按需解构 */
export const aiImageApi = {
  createUploadSession: (data: { filename: string; size: number }) => createUploadSession(data),
  patchBinaryToUpload,
  getUploadProgress,
  deleteUploadSession,
  finalizeUploadSession,
  uploadFileViaAi,
  uploadFilesViaAi,
  uploadFileViaDevSimpleEndpoint
};

export default aiImageApi;
