import { useEffect, useMemo, useRef, useState } from "react";
import { Download, FileWarning, RefreshCw, X } from "lucide-react";
import { renderAsync } from "docx-preview";
import { HttpError } from "../../shared/api/errors";
import { AllowedAction, DocumentType } from "../../shared/api/enums";
import { usePdfViewerEngine } from "../../shared/config/runtimeConfig";
import { attachmentApi } from "../attachments/attachmentApi";
import type { DocumentSummary } from "../documents/documentTypes";
import { documentApi } from "../documents/documentApi";
import { acquirePreview, downloadFile } from "./fileState";
import { PdfViewer } from "./PdfViewer";

const PDF_MIME = "application/pdf";
const DOCX_MIME = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";

function previewError(value: unknown, requestId = "unknown"): HttpError {
  if (value instanceof HttpError) return value;
  return new HttpError({
    httpStatus: 0,
    code: "INTERNAL_ERROR",
    message: value instanceof Error ? value.message : "文件预览失败",
    details: null,
    requestId,
    cause: value,
  });
}

function errorText(error: HttpError, extension: string): string {
  if (error.code === "PREVIEW_UNAVAILABLE" || error.httpStatus === 415) {
    return extension.toLowerCase() === "doc"
      ? "当前DOC格式暂不支持在线预览，请下载原始文件。"
      : "当前文件类型暂不支持在线预览";
  }
  if (error.httpStatus === 403 || error.code === "DOCUMENT_VIEW_FORBIDDEN") return "当前账号无权查看该文件";
  if (error.httpStatus === 404 || error.code === "FILE_NOT_FOUND") return "文件不存在或已被删除";
  if (error.httpStatus === 500) return "预览服务暂时不可用";
  if (error.httpStatus === 0) return "网络连接失败或文档渲染失败";
  return error.message;
}

function sanitizeRenderedContent(container: HTMLElement): void {
  container.querySelectorAll("a").forEach((anchor) => {
    const href = anchor.getAttribute("href") ?? "";
    if (href.trim().toLowerCase().startsWith("javascript:")) anchor.removeAttribute("href");
    if (!/^(https?:|mailto:|#)/i.test(href)) anchor.removeAttribute("href");
    anchor.setAttribute("rel", "noopener noreferrer");
    if (/^https?:/i.test(href)) anchor.setAttribute("target", "_blank");
  });
  container.querySelectorAll("img").forEach((image) => {
    const src = image.getAttribute("src") ?? "";
    if (src && !/^(blob:|data:)/i.test(src)) image.removeAttribute("src");
  });
}

export function FilePreviewModal({ item, onClose, mode = "side" }: {
  item: DocumentSummary;
  onClose: () => void;
  mode?: "side" | "modal";
}) {
  const [pdfBlob, setPdfBlob] = useState<Blob | null>(null);
  const [error, setError] = useState<HttpError | null>(null);
  const [loading, setLoading] = useState(true);
  const [downloading, setDownloading] = useState(false);
  const [retryVersion, setRetryVersion] = useState(0);
  const [contentText, setContentText] = useState<string | null>(null);
  const [contentLoading, setContentLoading] = useState(false);
  const docxContainer = useRef<HTMLDivElement>(null);
  const requestVersion = useRef(0);
  const canDownload = item.allowedActions.includes(AllowedAction.DOWNLOAD);
  const pdfViewerEngine = usePdfViewerEngine();
  const isModal = mode === "modal";

  // iframe 兜底需要对象 URL；pdfjs 模式直接消费 Blob。
  // 这里按 Blob 生命周期派生 URL 并在卸载/切换时释放，避免内存泄漏。
  const pdfUrl = useMemo(
    () => (pdfBlob && pdfViewerEngine === "iframe" ? URL.createObjectURL(pdfBlob) : null),
    [pdfBlob, pdfViewerEngine],
  );
  useEffect(() => {
    if (!pdfUrl) return;
    return () => URL.revokeObjectURL(pdfUrl);
  }, [pdfUrl]);

  // modal 模式下额外拉取 detail 以展示提取文本（contentText 仅在 detail DTO 暴露）。
  // side 模式宽度不足以分栏，跳过拉取以节省请求。
  useEffect(() => {
    setContentText(null);
    setContentLoading(false);
    if (!isModal) return;
    const controller = new AbortController();
    setContentLoading(true);
    const isAttachment = item.documentType === DocumentType.ATTACHMENT;
    const request = isAttachment
      ? attachmentApi.detail(item.id, controller.signal)
      : documentApi.detail(item.id, controller.signal);
    request
      .then((detail) => {
        if (!controller.signal.aborted) setContentText(detail.contentText ?? null);
      })
      .catch(() => {
        // 静默失败：右侧降级为"暂无正文"
      })
      .finally(() => {
        if (!controller.signal.aborted) setContentLoading(false);
      });
    return () => controller.abort();
  }, [item.id, item.documentType, isModal]);

  // 按空行（≥1 个空行）分段；过滤纯空白段。
  const paragraphs = useMemo(() => {
    if (!contentText) return [];
    return contentText
      .split(/\n\s*\n+/)
      .map((segment) => segment.trim())
      .filter((segment) => segment.length > 0);
  }, [contentText]);

  useEffect(() => {
    const version = ++requestVersion.current;
    const preview = acquirePreview(item.id, `${item.rowVersion}:${item.updatedAt}:${retryVersion}`);
    let responseRequestId = "unknown";
    setPdfBlob(null);
    setError(null);
    setLoading(true);
    if (docxContainer.current) docxContainer.current.replaceChildren();

    void preview.promise.then(async (result) => {
      responseRequestId = result.requestId;
      if (version !== requestVersion.current) return;
      const mime = (result.contentType || result.blob.type || "").split(";", 1)[0].trim().toLowerCase();
      const extension = item.fileExtension.trim().toLowerCase();
      // 1.6 预览：PDF/DOC/DOCX 统一返回 application/pdf；XLS/XLSX 在后端返回 415
      if ((extension === "pdf" || extension === "doc" || extension === "docx") && mime === PDF_MIME) {
        if (version !== requestVersion.current) return;
        setPdfBlob(result.blob);
        return;
      }
      if (extension === "docx") {
        // 受控回退：仅当后端明确返回 DOCX 时使用 docx-preview
        if (mime !== DOCX_MIME) {
          throw new HttpError({ httpStatus: result.httpStatus, code: "INTERNAL_ERROR", message: "预览文件类型与响应内容不一致", details: { expected: DOCX_MIME, actual: mime }, requestId: result.requestId });
        }
        const buffer = await result.blob.arrayBuffer();
        if (version !== requestVersion.current || !docxContainer.current) return;
        docxContainer.current.replaceChildren();
        await renderAsync(buffer, docxContainer.current, undefined, {
          inWrapper: true,
          ignoreWidth: mode === "side",
          ignoreHeight: true,
          breakPages: true,
          useBase64URL: true,
        });
        if (version !== requestVersion.current || !docxContainer.current) return;
        sanitizeRenderedContent(docxContainer.current);
        return;
      }
      throw new HttpError({ httpStatus: result.httpStatus, code: "INTERNAL_ERROR", message: "预览文件类型与响应内容不一致", details: { extension, actual: mime }, requestId: result.requestId });
    }).catch((value: unknown) => {
      if (version === requestVersion.current) setError(previewError(value, responseRequestId));
    }).finally(() => {
      if (version === requestVersion.current) setLoading(false);
    });

    return () => {
      requestVersion.current += 1;
      preview.release();
      if (docxContainer.current) docxContainer.current.replaceChildren();
    };
  }, [item.id, item.rowVersion, item.updatedAt, retryVersion, mode]);

  const download = async () => {
    if (!canDownload || downloading) return;
    setDownloading(true);
    try {
      await downloadFile(item.id, `${item.documentName}.${item.fileExtension || "bin"}`);
    } catch (value) {
      setError(previewError(value));
    } finally {
      setDownloading(false);
    }
  };

  const previewChildren = (
    <>
      {loading && <div className="absolute inset-0 z-10 flex items-center justify-center text-sm font-black" style={{ color: "#1A5299", background: "rgba(235,244,253,.9)" }}><RefreshCw size={15} className="animate-spin mr-2" />正在加载预览…</div>}
      {pdfBlob && pdfViewerEngine === "pdfjs" && <PdfViewer blob={pdfBlob} documentName={item.documentName} />}
      {pdfUrl && pdfViewerEngine === "iframe" && (
        <iframe
          src={`${pdfUrl}#view=FitH`}
          title={item.documentName}
          className="w-full h-full border-0 bg-white"
        />
      )}
      <div ref={docxContainer} className={`q3-docx-preview min-h-full overflow-auto [&_.docx-wrapper]:!bg-[#d8e8f5] [&_section.docx]:!shadow-sm ${mode === "side" ? "[&_.docx-wrapper]:!p-3 [&_section.docx]:!max-w-full" : "[&_.docx-wrapper]:!p-8"}`} />
      {error && <div className="p-8 text-center" style={{ color: "#1A5299" }}>
        <FileWarning size={38} className="mx-auto mb-3 opacity-60" />
        <div className="text-sm font-black leading-6">{errorText(error, item.fileExtension)}</div>
        {error.requestId && <div className="text-xs font-bold opacity-60 mt-2">错误编号：{error.requestId}</div>}
        {error.httpStatus !== 401 && <button onClick={() => setRetryVersion((value) => value + 1)} className="mt-4 px-3 py-1.5 text-xs font-black border" style={{ borderColor: "#3191eb" }}>重试预览</button>}
      </div>}
    </>
  );

  const textPanel = (
    <aside className="w-[360px] flex-shrink-0 flex flex-col border-l bg-white min-h-0" style={{ borderColor: "rgba(40,118,193,.2)" }}>
      <div className="px-4 py-3 text-sm font-black flex-shrink-0" style={{ color: "#1A5299", borderBottom: "1px solid rgba(40,118,193,.15)" }}>
        提取文本
      </div>
      <div className="flex-1 min-h-0 overflow-y-auto px-4 py-3 space-y-2.5">
        {contentLoading && <div className="text-xs font-bold opacity-60">正在加载正文…</div>}
        {!contentLoading && paragraphs.length === 0 && (
          <div className="text-xs font-bold opacity-60">暂无正文</div>
        )}
        {paragraphs.map((paragraph, index) => (
          <div key={index} className="rounded-sm px-2.5 py-2" style={{ background: "rgba(40,118,193,.04)" }}>
            <div className="text-xs leading-6 whitespace-pre-wrap break-words" style={{ color: "#1A5299" }}>{paragraph}</div>
            <div className="mt-1.5 text-[10px] font-black opacity-50" style={{ color: "#1A5299" }}>
              第 {index + 1} 段 · 共 {paragraph.length} 字
            </div>
          </div>
        ))}
      </div>
    </aside>
  );

  const panel = (
    <aside
      aria-label={mode === "modal" ? "完整文档查看" : "文档右侧预览"}
      className={mode === "modal"
        ? "w-[min(1400px,95vw)] h-[90vh] flex flex-col bg-white shadow-2xl min-h-0"
        : "w-[400px] max-w-[28vw] min-w-[360px] flex-shrink-0 flex flex-col border-l bg-white min-h-0"}
      style={{ borderColor: "rgba(40,118,193,.2)" }}
    >
      <div className="px-4 py-3 flex items-start gap-3 text-white" style={{ background: "#1A5299" }}>
        <div className="min-w-0 flex-1">
          <div className="text-sm font-black truncate" title={item.documentName}>{mode === "modal" ? `文件查看 · ${item.documentName}` : item.documentName}</div>
          <div className="text-[11px] font-bold opacity-70 mt-1">{item.fileExtension?.toUpperCase() || "无扩展名"} · {new Date(item.updatedAt).toLocaleString("zh-CN")}</div>
        </div>
        <button aria-label="关闭预览" onClick={onClose}><X size={17} /></button>
      </div>
      <div className={`flex-1 min-h-0 overflow-hidden relative ${isModal ? "flex flex-row" : ""}`} style={{ background: "#EBF4FD" }}>
        {isModal ? (
          <div className="flex-1 min-w-0 relative overflow-hidden">{previewChildren}</div>
        ) : previewChildren}
        {isModal && textPanel}
      </div>
      {canDownload && <button disabled={downloading} onClick={() => void download()} className="m-3 px-3 py-2 text-sm font-black text-white disabled:opacity-60" style={{ background: "#3191eb" }}><Download size={14} className="inline mr-2" />{downloading ? "正在下载…" : "下载原文件"}</button>}
    </aside>
  );
  if (mode === "modal") {
    return <div className="fixed inset-0 z-[80] flex items-center justify-center" style={{ background: "rgba(26,82,153,.58)" }} onClick={onClose}><div onClick={(event) => event.stopPropagation()}>{panel}</div></div>;
  }
  return panel;
}
