import { useUserStore } from "@/stores/modules/user";
import { ElMessage } from "element-plus";
import { AI_UPLOAD_FILES_PUBLIC_BASE, BASE_AI_URL } from "@/utils/config";

/** 非 TUS 简单上传接口路径（与 Apifox「上传文件-非TUS」一致；开发环境经 VITE_PROXY /ai-upload 转发） */
const SIMPLE_UPLOAD_PATH = "/upload/simple";

/** 从路径或 fileType 解析文件后缀（如 jpg、mp4），用于生成带格式的文件名 */
export function getFileExtension(filePath, fileType) {
  const pathPart = (filePath || "").split("?")[0];
  const lastSegment = pathPart.split("/").pop() || "";
  const dot = lastSegment.lastIndexOf(".");
  if (dot > 0) {
    const ext = lastSegment.slice(dot + 1).toLowerCase();
    if (/^[a-z0-9]+$/i.test(ext) && ext.length <= 5) return ext;
  }
  if (fileType === "video") return "mp4";
  if (fileType === "image") return "jpg";
  return "jpg";
}

/**
 * 将入参统一为 File[]
 * @param {File | File[] | FileList} input
 * @returns {File[]}
 */
function normalizeFiles(input) {
  if (input == null) return [];
  if (input instanceof File) return [input];
  if (typeof FileList !== "undefined" && input instanceof FileList) {
    return Array.from(input);
  }
  if (Array.isArray(input)) {
    return input.filter(item => item instanceof File);
  }
  throw new Error("请传入 File、File[] 或 FileList");
}

/**
 * 常见「可直接用于 img/video src」的字段（含蛇形命名）
 * 注意：勿把 upload_id / filename 放前面——/upload/simple 会返回 upload_id 与完整 URL 并存，
 * 若先按「裸 id」拼到 /files/ 会得到错误地址，图片会裂图。
 */
const URL_LIKE_KEYS = [
  "download_url",
  "downloadUrl",
  "preview_url",
  "previewUrl",
  "url",
  "fileUrl",
  "file_url",
  "accessUrl",
  "access_url",
  "ossUrl",
  "cdnUrl",
  "path",
  "filePath",
  "file_path",
  "objectKey",
  "object_key",
  "key",
  "location",
  "href"
];

/**
 * 将接口返回的路径或 id 规范为可访问 URL
 * @param {string} raw
 * @returns {string}
 */
function normalizeToFileUrl(raw) {
  const t = String(raw ?? "").trim();
  if (!t) return "";
  if (/^https?:\/\//i.test(t)) return t;
  if (t.startsWith("//")) return `https:${t}`;

  const filesBase = String(AI_UPLOAD_FILES_PUBLIC_BASE || "").replace(/\/$/, "");
  if (filesBase.startsWith("http")) {
    try {
      if (t.startsWith("/")) {
        return new URL(t, new URL(`${filesBase}/`).origin).href;
      }
      if (/^files\//i.test(t)) {
        return new URL(`/${t.replace(/^\/+/, "")}`, new URL(`${filesBase}/`).origin).href;
      }
      // 相对路径 dir/file.ext（无协议）
      if (t.includes("/") && !t.startsWith("/") && !/^[a-z]+:/i.test(t)) {
        return `${filesBase}/${t.replace(/^\/+/, "")}`;
      }
      // 仅返回 uuid/文件名 等，按「对外文件基址 + 片段」拼接
      if (!t.includes("/") && !t.includes("\\") && t.length <= 512) {
        return `${filesBase}/${encodeURIComponent(t)}`;
      }
    } catch {
      /* ignore */
    }
  }
  return "";
}

/**
 * 从对象上按已知字段取「可能是地址」的字符串
 * @param {unknown} o
 * @returns {string}
 */
function pickUrlLikeFromObject(o) {
  if (!o || typeof o !== "object" || Array.isArray(o)) return "";
  const r = /** @type {Record<string, unknown>} */ (o);
  for (const k of URL_LIKE_KEYS) {
    const v = r[k];
    if (typeof v === "string" && v.trim()) {
      const abs = normalizeToFileUrl(v);
      if (abs) return abs;
    }
  }
  return "";
}

/**
 * 深度查找第一个 http(s) 字符串
 * @param {unknown} val
 * @param {number} depth
 * @param {number} maxDepth
 * @returns {string}
 */
function deepFindHttpUrl(val, depth = 0, maxDepth = 8) {
  if (depth > maxDepth || val == null) return "";
  if (typeof val === "string") {
    const t = val.trim();
    return /^https?:\/\//i.test(t) ? t : "";
  }
  if (Array.isArray(val)) {
    for (const item of val) {
      const u = deepFindHttpUrl(item, depth + 1, maxDepth);
      if (u) return u;
    }
    return "";
  }
  if (typeof val === "object") {
    for (const k of Object.keys(val)) {
      const u = deepFindHttpUrl(/** @type {Record<string, unknown>} */ (val)[k], depth + 1, maxDepth);
      if (u) return u;
    }
  }
  return "";
}

/**
 * 从 JSON 体中解析文件访问地址（兼容多种后端约定）
 * @param {unknown} body
 * @returns {string}
 */
function pickUrlFromJsonBody(body) {
  if (body == null) return "";

  if (typeof body === "string") {
    return normalizeToFileUrl(body) || (body.trim().startsWith("http") ? body.trim() : "");
  }

  if (typeof body !== "object" || Array.isArray(body)) {
    return deepFindHttpUrl(body);
  }

  const b = /** @type {Record<string, unknown>} */ (body);
  const unwrap = v => {
    if (v == null) return null;
    if (typeof v === "object" && !Array.isArray(v) && /** @type {Record<string, unknown>} */ (v).data !== undefined) {
      return /** @type {Record<string, unknown>} */ (v).data;
    }
    return v;
  };

  const candidates = [unwrap(b.data), b.data, b.result, unwrap(b.result), b.payload, b.body, b];

  for (const d of candidates) {
    if (d == null) continue;
    if (typeof d === "string") {
      const u = normalizeToFileUrl(d) || (d.trim().startsWith("http") ? d.trim() : "");
      if (u) return u;
    } else if (typeof d === "object") {
      let u = pickUrlLikeFromObject(d);
      if (u) return u;
      u = deepFindHttpUrl(d);
      if (u) return u;
    }
  }

  return deepFindHttpUrl(body);
}

/** 审核服务返回的违规大类 → 面向用户的短说明（与业务文案风格一致） */
const VIOLATION_CATEGORY_USER_HINT = {
  GAMBLING: "图片涉及赌博等违规内容",
  PORNOGRAPHY: "图片涉及色情等违规内容",
  PORN: "图片涉及色情等违规内容",
  ADULT: "图片涉及色情等违规内容",
  SEXUAL: "图片涉及色情等违规内容",
  DRUGS: "图片涉及毒品等违规内容",
  DRUG: "图片涉及毒品等违规内容",
  VIOLENCE: "图片涉及暴力、血腥等违规内容",
  GORE: "图片涉及暴力、血腥等违规内容",
  POLITICAL: "图片涉及不当政治或敏感信息",
  POLITICS: "图片涉及不当政治或敏感信息",
  SPAM: "图片涉及违规推广或垃圾信息",
  ABUSE: "图片涉及辱骂、人身攻击等不文明内容",
  ILLEGAL: "图片含违法违规内容"
};

/**
 * 根据 reason / 关键词生成友好提示（与 aiImageUpload 中审核话术对齐）
 * @param {string} raw
 * @returns {string}
 */
/** 仅返回「原因」短句，后缀由 formatSimpleUploadModerationMessage 统一拼接 */
function mapSimpleModerationReasonToTip(raw) {
  const s = raw == null ? "" : String(raw).trim();
  if (!s) return "";
  if (/赌|赌博|casino|gambl|GAMBLING/i.test(s)) return "图片涉及赌博等违规内容";
  if (/色情|淫秽|porn|sex|PORNOGRAPHY|PORN/i.test(s)) return "图片涉及色情等违规内容";
  if (/毒品|涉毒|drug|DRUG/i.test(s)) return "图片涉及毒品等违规内容";
  if (/暴力|血腥|violence|gore|VIOLENCE/i.test(s)) return "图片涉及暴力、血腥等违规内容";
  if (/辱骂|谩骂|人身攻击|abuse|insult/i.test(s)) return "图片涉及不文明内容";
  if (/政治|谣言/i.test(s)) return "图片涉及违规信息";
  if (/广告|spam|营销/i.test(s)) return "图片涉及违规推广内容";
  if (/违法|违禁|illegal/i.test(s)) return "图片含违法违规内容";
  if (/黄赌毒/i.test(s)) return "请勿上传涉黄、涉赌、涉毒等违规内容";
  if (/审核|moderat|violation|flagged|VLM/i.test(s)) return "图片未通过内容审核";
  return "";
}

/**
 * 解析 /upload/simple 审核未通过等响应（saved: false + moderation）
 * @param {unknown} parsed
 * @returns {string} 非空则可直接作为 ElMessage / Error 文案
 */
function formatSimpleUploadModerationMessage(parsed) {
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return "";
  const p = /** @type {Record<string, unknown>} */ (parsed);
  const notSaved = p.saved === false;
  const mod = p.moderation;
  const hasModeration = mod != null && typeof mod === "object";
  let flagged = false;
  if (hasModeration) {
    const results = /** @type {Record<string, unknown>} */ (mod).results;
    if (Array.isArray(results) && results[0] && typeof results[0] === "object") {
      flagged = /** @type {Record<string, unknown>} */ (results[0]).flagged === true;
    }
  }
  if (!notSaved && !flagged) return "";

  const apiMsg = String(p.message ?? p.msg ?? "").trim();

  let firstResult = null;
  if (hasModeration) {
    const results = /** @type {Record<string, unknown>} */ (mod).results;
    if (Array.isArray(results) && results[0] && typeof results[0] === "object") {
      firstResult = /** @type {Record<string, unknown>} */ (results[0]);
    }
  }

  let reason = "";
  let firstCategory = "";
  if (firstResult) {
    reason = String(firstResult.reason ?? "").trim();
    const cats = firstResult.violation_categories;
    if (Array.isArray(cats) && cats[0] && typeof cats[0] === "object") {
      firstCategory = String(/** @type {Record<string, unknown>} */ (cats[0]).category ?? "")
        .trim()
        .toUpperCase();
    }
  }

  const suffix = "未通过审核，文件未保存。请更换后重试";

  if (firstCategory && VIOLATION_CATEGORY_USER_HINT[firstCategory]) {
    return `${VIOLATION_CATEGORY_USER_HINT[firstCategory]}，${suffix}`;
  }

  const fromReason = mapSimpleModerationReasonToTip(reason);
  if (fromReason) {
    return `${fromReason.replace(/。$/, "")}，${suffix}`;
  }

  if (apiMsg) {
    if (/请更换|更换后|重新选择|换一张/i.test(apiMsg)) return apiMsg;
    if (/未保存|未通过/.test(apiMsg)) return `${apiMsg.replace(/。$/, "")}。请更换后重试`;
    return `${apiMsg.replace(/。$/, "")}，${suffix}`;
  }

  return "图片未通过内容审核，文件未保存。请更换后重试";
}

/**
 * 业务层 code / success（HTTP 200 但业务失败时常有）
 * @param {unknown} parsed
 */
function assertSimpleUploadBusinessOk(parsed) {
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
  const p = /** @type {Record<string, unknown>} */ (parsed);
  if (p.success === false) {
    throw new Error(String(p.msg ?? p.message ?? p.error ?? "上传失败"));
  }
  const c = p.code;
  if (c === undefined || c === null) return;
  const ok = c === 200 || c === 0 || c === "200" || c === "0";
  if (!ok) {
    throw new Error(String(p.msg ?? p.message ?? p.error ?? "上传失败"));
  }
}

/**
 * POST multipart：字段 file；可选 filename（此处不传则服务端用原名）
 * @param {File} file
 * @returns {Promise<string>} 文件访问 URL
 */
async function postFileToSimpleUpload(file) {
  const base = String(BASE_AI_URL || "").replace(/\/$/, "");
  if (!base) {
    throw new Error("未配置上传服务地址（VITE_AI_UPLOAD_BASE 或默认 /ai-upload）");
  }

  const formData = new FormData();
  formData.append("file", file, file.name || "file");

  const userStore = useUserStore();
  const headers = {};
  const token = userStore.token || "";
  if (token) {
    headers.Authorization = token;
  }

  const res = await fetch(`${base}${SIMPLE_UPLOAD_PATH}`, {
    method: "POST",
    headers,
    /** 不带跨域 Cookie，减轻上传服务 CORS 要求（鉴权仅用 Authorization） */
    credentials: "omit",
    body: formData
  });

  const rawText = await res.text();
  let parsed = null;
  const trimmed = rawText.trim();
  if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
    try {
      parsed = JSON.parse(rawText);
    } catch (_) {
      /* ignore */
    }
  }

  if (!res.ok) {
    const modHint = formatSimpleUploadModerationMessage(parsed);
    let msg = modHint;
    if (!msg && parsed && typeof parsed === "object") {
      const p = /** @type {Record<string, unknown>} */ (parsed);
      const m = p.msg ?? p.message ?? p.error;
      if (m != null && String(m)) msg = String(m);
    }
    if (!msg && trimmed) msg = trimmed.slice(0, 200);
    if (!msg) msg = "上传失败";
    throw new Error(msg);
  }

  try {
    assertSimpleUploadBusinessOk(parsed);
  } catch (bizErr) {
    console.error("[upload/simple] 业务失败", bizErr, trimmed.slice(0, 500));
    throw bizErr;
  }

  const moderationUserTip = formatSimpleUploadModerationMessage(parsed);
  if (moderationUserTip) {
    throw new Error(moderationUserTip);
  }

  let url = pickUrlFromJsonBody(parsed);
  if (!url && trimmed.startsWith("http")) {
    url = trimmed;
  }
  if (!url) {
    const fromHeader =
      res.headers.get("x-file-url") ||
      res.headers.get("x-url") ||
      res.headers.get("x-oss-url") ||
      res.headers.get("file-url") ||
      "";
    if (fromHeader?.trim()) {
      const h = fromHeader.trim();
      url = normalizeToFileUrl(h) || (h.startsWith("http") ? h : "");
    }
  }
  if (!url) {
    const loc = res.headers.get("location") || res.headers.get("Location") || "";
    if (loc) {
      url = normalizeToFileUrl(loc.trim()) || (loc.trim().startsWith("http") ? loc.trim() : "");
    }
  }
  if (!url) {
    console.error("[upload/simple] 无法解析文件地址，响应片段:", trimmed.slice(0, 800));
    throw new Error("上传失败，未返回文件地址");
  }
  return url;
}

/**
 * 上传文件：图片与视频均走同一接口 POST /upload/simple，formData 键 file。
 * @param {File | File[] | FileList} files 浏览器文件对象；支持单个 File、数组或 FileList
 * @param {string} [_fileType] 保留参数，兼容旧调用（当前不参与分支）
 * @param {{ showLoading?: boolean }} [options] showLoading 为 true 时用 ElMessage 提示上传中（非阻塞）
 * @returns {Promise<string[]>} 上传成功后的文件 URL 列表
 */
export async function uploadFilesToOss(files, _fileType, options = {}) {
  const { showLoading = false } = options;
  const fileArr = normalizeFiles(files);
  if (fileArr.length === 0) {
    throw new Error("请选择要上传的文件");
  }

  let closeLoading = () => {};
  if (showLoading) {
    const loading = ElMessage({ message: "上传中...", type: "info", duration: 0, showClose: false });
    closeLoading = () => loading.close();
  }

  try {
    const uploadedUrls = [];
    for (const file of fileArr) {
      const url = await postFileToSimpleUpload(file);
      uploadedUrls.push(url);
    }
    closeLoading();
    return uploadedUrls;
  } catch (e) {
    closeLoading();
    console.error("上传失败", e);
    const msg = e?.message || "上传失败";
    ElMessage.error(msg);
    throw e;
  }
}

/**
 * 上传单个文件，返回 URL 字符串
 * @param {File} file
 * @param {string} [fileType]
 * @param {{ showLoading?: boolean }} [options]
 * @returns {Promise<string>}
 */
export async function uploadFileToOss(file, fileType, options) {
  const urls = await uploadFilesToOss(file, fileType, options);
  return urls[0];
}

/**
 * 兼容 UploadImg 等组件的 api 格式：接收 FormData，返回 { data: { fileUrl } }
 * @param {FormData} formData 包含 file 字段
 * @param {string} [fileType] 不传或传空时按 file.type 自动区分 image / video
 * @param {{ showLoading?: boolean }} [options]
 * @returns {Promise<{ data: { fileUrl: string } }>}
 */
export async function uploadFormDataToOss(formData, fileType, options = {}) {
  const file = formData.get("file");
  if (!file || !(file instanceof File)) {
    throw new Error("请选择要上传的文件");
  }
  const inferred =
    fileType === undefined || fileType === null || fileType === ""
      ? String(file.type || "").startsWith("video/")
        ? "video"
        : "image"
      : fileType;
  const url = await uploadFileToOss(file, inferred, options);
  return { data: { fileUrl: url } };
}

/**
 * FormData 走 `/upload/simple` 后，包装成旧组件习惯的 `{ code, msg, data: { fileUrl }, fileUrl }`（并非请求 `/file/uploadMore`）
 * @param {FormData} formData
 * @param {{ showLoading?: boolean }} [options]
 * @returns {Promise<{ code: number; msg: string; data: { fileUrl: string }; fileUrl: string }>}
 */
export async function uploadFormDataSimpleCompat(formData, options = {}) {
  const file = formData.get("file");
  if (!file || !(file instanceof File)) {
    throw new Error("请选择要上传的文件");
  }
  const isVideo = String(file.type || "").startsWith("video/");
  const inner = await uploadFormDataToOss(formData, isVideo ? "video" : "image", options);
  const fileUrl = inner.data.fileUrl;
  return {
    code: 200,
    msg: "success",
    data: inner.data,
    fileUrl
  };
}
