import mammoth from "mammoth";
import TurndownService from "turndown";
import { gfm } from "turndown-plugin-gfm";
import { StoreError } from "./article-store";

export const MAX_WORD_FILE_SIZE = 20 * 1024 * 1024;

const DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
const ACCEPTED_DOCX_MIMES = new Set([DOCX_MIME, "application/octet-stream", "application/zip"]);
const IMAGE_MIME_ALIASES: Record<string, string> = {
  "image/jpg": "image/jpeg",
  "image/x-png": "image/png",
};
const SUPPORTED_IMAGE_MIMES = new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);

export interface WordImportResult {
  body: string;
  suggestedTitle: string;
  imageCount: number;
  warnings: string[];
}

export function decodeWordDataUrl(dataUrl: unknown, fileName: unknown): Buffer {
  const name = String(fileName || "").trim();
  if (/\.doc$/i.test(name)) throw new StoreError("仅支持 .docx 文件，不支持旧版 .doc 格式");
  if (!/\.docx$/i.test(name)) throw new StoreError("请选择 .docx 文件");

  const match = String(dataUrl || "").match(/^data:([^;,]+);base64,([a-zA-Z0-9+/=\s]+)$/s);
  if (!match || !ACCEPTED_DOCX_MIMES.has(match[1].toLowerCase())) {
    throw new StoreError("Word 文件格式不正确，请上传 .docx 文件");
  }

  const buffer = Buffer.from(match[2].replace(/\s/g, ""), "base64");
  if (!buffer.length || buffer.subarray(0, 2).toString("ascii") !== "PK") {
    throw new StoreError("Word 文件无效或已经损坏");
  }
  if (buffer.length > MAX_WORD_FILE_SIZE) throw new StoreError("Word 文件不能超过 20MB", 413);
  return buffer;
}

function titleFromFileName(fileName: string): string {
  return fileName
    .replace(/^.*[\\/]/, "")
    .replace(/\.docx$/i, "")
    .replace(/[_-]+/g, " ")
    .trim() || "Word 导入文章";
}

function plainHeading(value: string): string {
  return value
    .replace(/!\[[^\]]*\]\([^)]*\)/g, "")
    .replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
    .replace(/[`*_~]/g, "")
    .trim();
}

export async function convertWordToMarkdown(
  buffer: Buffer,
  fileName: string,
  saveImage: (dataUrl: string) => Promise<string>,
): Promise<WordImportResult> {
  let imageCount = 0;
  let result: Awaited<ReturnType<typeof mammoth.convertToHtml>>;

  try {
    result = await mammoth.convertToHtml({ buffer }, {
      styleMap: ["p[style-name='Title'] => h1:fresh"],
      convertImage: mammoth.images.imgElement(async (image) => {
        const mime = IMAGE_MIME_ALIASES[image.contentType] || image.contentType;
        if (!SUPPORTED_IMAGE_MIMES.has(mime)) {
          throw new StoreError(`Word 中包含不支持的图片格式：${image.contentType || "未知格式"}`);
        }
        const base64 = await image.readAsBase64String();
        const src = await saveImage(`data:${mime};base64,${base64}`);
        imageCount += 1;
        return { src };
      }),
    });
  } catch (error) {
    if (error instanceof StoreError) throw error;
    throw new StoreError(`Word 文件解析失败：${error instanceof Error ? error.message : "文件无效或已经损坏"}`);
  }

  const turndown = new TurndownService({
    bulletListMarker: "-",
    codeBlockStyle: "fenced",
    headingStyle: "atx",
  });
  turndown.use(gfm);
  let body = turndown.turndown(result.value).trim();

  let suggestedTitle = titleFromFileName(fileName);
  const firstHeading = body.match(/^#\s+(.+)$/m);
  if (firstHeading && firstHeading.index !== undefined) {
    suggestedTitle = plainHeading(firstHeading[1]) || suggestedTitle;
    body = `${body.slice(0, firstHeading.index)}${body.slice(firstHeading.index + firstHeading[0].length)}`
      .replace(/^\s+|\s+$/g, "")
      .replace(/\n{3,}/g, "\n\n");
  }
  if (!body) throw new StoreError("Word 文件中没有可导入的正文内容");

  return {
    body,
    suggestedTitle,
    imageCount,
    warnings: result.messages.map((message) => message.message),
  };
}
