"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { apiJson } from "@/lib/client/http";
import { contentFingerprint } from "@/lib/domain/text";
import { createBrowserOcrCheckpointStore } from "@/lib/client/ocr/checkpoints";
import { createOcrJob, type OcrProgress } from "@/lib/client/ocr/job";
import { createTesseractRecognizer } from "@/lib/client/ocr/tesseract";
import { ocrCheckpointsToParsedBook } from "@/lib/client/ocr/to-parsed-book";
import { loadBrowserPdf, renderPdfPageForOcr } from "@/lib/client/ocr/browser-pdf";
import { chapterBatches, prepareChaptersForSync } from "@/lib/client/import/prepare";

interface Book {
  id: string;
  title: string;
  author: string;
  format: "epub" | "pdf";
  status: string;
  sourceBytes?: number;
}

interface Segment {
  id: string;
  position: number;
  text: string;
  language: "zh-CN" | "en-US";
  contentHash: string;
  textVersion: number;
  included: boolean;
  sourceKind: "epub" | "pdf_text" | "ocr" | "manual";
  pageNumber: number | null;
  ocrConfidence: number | null;
  warning: string | null;
}

interface Chapter {
  id: string;
  title: string;
  position: number;
  pageStart: number | null;
  pageEnd: number | null;
  segments: Segment[];
}

const warningLabels: Record<string, string> = {
  formula: "复杂公式 · 默认跳过",
  table: "表格 · 默认跳过",
  "low-confidence": "识别置信度较低",
  "repeated-header-footer": "疑似页眉页脚",
  "page-number": "页码",
};

export function Proofreader({ bookId }: { bookId: string }) {
  const [book, setBook] = useState<Book | null>(null);
  const [chapters, setChapters] = useState<Chapter[]>([]);
  const [activeChapterId, setActiveChapterId] = useState("");
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [message, setMessage] = useState("");
  const [error, setError] = useState("");
  const [pageNumber, setPageNumber] = useState(1);
  const [ocrProgress, setOcrProgress] = useState<OcrProgress | null>(null);
  const [ocrDetail, setOcrDetail] = useState("");
  const [ocrRunning, setOcrRunning] = useState(false);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const ocrJobRef = useRef<{ pause(): void } | null>(null);

  useEffect(() => {
    let active = true;
    void Promise.all([
      apiJson<{ book: Book }>(`/api/books/${bookId}`),
      apiJson<{ chapters: Chapter[] }>(`/api/books/${bookId}/chapters`),
    ])
      .then(([bookResult, chapterResult]) => {
        if (!active) return;
        setBook(bookResult.book);
        setChapters(chapterResult.chapters);
        const first = chapterResult.chapters[0];
        if (first) {
          setActiveChapterId(first.id);
          setPageNumber(first.pageStart ?? 1);
        }
      })
      .catch((loadError: unknown) => {
        if (active) setError(loadError instanceof Error ? loadError.message : "校对内容无法加载。");
      })
      .finally(() => {
        if (active) setLoading(false);
      });
    return () => { active = false; };
  }, [bookId]);

  const activeChapter = useMemo(
    () => chapters.find((chapter) => chapter.id === activeChapterId) ?? chapters[0] ?? null,
    [activeChapterId, chapters],
  );

  useEffect(() => {
    if (book?.format !== "pdf" || !canvasRef.current) return;
    let cancelled = false;
    let loadedPdf: Awaited<ReturnType<typeof loadBrowserPdf>> | undefined;
    void loadBrowserPdf(`/api/books/${bookId}/source`)
      .then(async (result) => {
        loadedPdf = result;
        const page = await loadedPdf.document.getPage(pageNumber);
        const canvas = canvasRef.current;
        if (!page || !canvas || cancelled) return;
        const viewport = page.getViewport({ scale: 1.35 });
        const ratio = window.devicePixelRatio || 1;
        canvas.width = Math.floor(viewport.width * ratio);
        canvas.height = Math.floor(viewport.height * ratio);
        canvas.style.width = `${viewport.width}px`;
        canvas.style.height = `${viewport.height}px`;
        const context = canvas.getContext("2d");
        if (!context) return;
        context.setTransform(ratio, 0, 0, ratio, 0, 0);
        await page.render({ canvas, canvasContext: context, viewport }).promise;
      })
      .catch((renderError: unknown) => {
        if (!cancelled) setMessage(renderError instanceof Error ? renderError.message : "原始页面预览失败。");
      });
    return () => {
      cancelled = true;
      void loadedPdf?.destroy();
    };
  }, [book?.format, bookId, pageNumber]);

  function updateChapter(updater: (chapter: Chapter) => Chapter) {
    if (!activeChapter) return;
    setChapters((current) => current.map((chapter) => chapter.id === activeChapter.id ? updater(chapter) : chapter));
  }

  function updateSegment(segmentId: string, patch: Partial<Segment>) {
    updateChapter((chapter) => ({
      ...chapter,
      segments: chapter.segments.map((segment) => segment.id === segmentId ? { ...segment, ...patch } : segment),
    }));
  }

  function renameChapter() {
    if (!activeChapter) return;
    const title = window.prompt("新的章节名称", activeChapter.title)?.trim();
    if (title) updateChapter((chapter) => ({ ...chapter, title }));
  }

  function splitAt(segmentId: string) {
    if (!activeChapter) return;
    const index = activeChapter.segments.findIndex((segment) => segment.id === segmentId);
    if (index <= 0) {
      setMessage("请从第二段或更后的位置拆分。");
      return;
    }
    const title = window.prompt("新章节名称", `${activeChapter.title}（续）`)?.trim();
    if (!title) return;
    const rightId = crypto.randomUUID();
    const leftSegments = activeChapter.segments.slice(0, index).map((segment, position) => ({ ...segment, position }));
    const rightSegments = activeChapter.segments.slice(index).map((segment, position) => ({ ...segment, id: `${rightId}:seg:${position}`, position }));
    const activeIndex = chapters.findIndex((chapter) => chapter.id === activeChapter.id);
    const next = [...chapters];
    next.splice(activeIndex, 1,
      { ...activeChapter, segments: leftSegments },
      { ...activeChapter, id: rightId, title, position: activeChapter.position + 1, pageStart: rightSegments[0]?.pageNumber ?? activeChapter.pageStart, segments: rightSegments },
    );
    setChapters(next.map((chapter, position) => ({ ...chapter, position })));
    setActiveChapterId(rightId);
  }

  function mergeNext() {
    if (!activeChapter) return;
    const index = chapters.findIndex((chapter) => chapter.id === activeChapter.id);
    const right = chapters[index + 1];
    if (!right) { setMessage("已经是最后一章。"); return; }
    const merged = {
      ...activeChapter,
      pageEnd: right.pageEnd ?? activeChapter.pageEnd,
      segments: [...activeChapter.segments, ...right.segments].map((segment, position) => ({ ...segment, position })),
    };
    setChapters(chapters.filter((_, chapterIndex) => chapterIndex !== index + 1).map((chapter, chapterIndex) => chapterIndex === index ? merged : chapter).map((chapter, position) => ({ ...chapter, position })));
  }

  async function save() {
    if (!chapters.length) return;
    setSaving(true);
    setMessage("");
    setError("");
    try {
      const normalizedChapters = await Promise.all(chapters.map(async (chapter) => ({
        ...chapter,
        segments: await Promise.all(chapter.segments.map(async (segment) => ({
          ...segment,
          contentHash: await contentFingerprint(segment.text),
        }))),
      })));
      for (let index = 0; index < normalizedChapters.length; index += 10) {
        await apiJson(`/api/books/${bookId}/chapters`, {
          method: "PUT",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ chapters: normalizedChapters.slice(index, index + 10) }),
        });
      }
      const result = await apiJson<{ chapters: Chapter[] }>(`/api/books/${bookId}/chapters/finalize`, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ chapterIds: normalizedChapters.map((chapter) => chapter.id) }),
      });
      setChapters(result.chapters);
      setMessage("修改已保存。变更段落的旧语音已失效，其余语音会继续复用。");
    } catch (saveError) {
      setError(saveError instanceof Error ? saveError.message : "保存失败。");
    } finally {
      setSaving(false);
    }
  }

  async function runOcr() {
    if (!book || book.format !== "pdf" || ocrRunning) return;
    setOcrRunning(true);
    setError("");
    setMessage("");
    setOcrDetail("正在加载 PDF…");
    let loadedPdf: Awaited<ReturnType<typeof loadBrowserPdf>> | undefined;
    try {
      loadedPdf = await loadBrowserPdf(`/api/books/${bookId}/source`);
      const pdfDocument = loadedPdf.document;
      const checkpointStore = await createBrowserOcrCheckpointStore();
      const sourceFingerprint = await contentFingerprint(`${bookId}:${book.sourceBytes ?? pdfDocument.numPages}`);
      const recognizer = await createTesseractRecognizer((event) => {
        const percent = Math.round(event.progress * 100);
        setOcrDetail(`当前页：${event.status} ${percent}%`);
      });
      const job = createOcrJob<HTMLCanvasElement>({
        bookId,
        sourceFingerprint,
        pageCount: pdfDocument.numPages,
        checkpoints: checkpointStore,
        renderer: async (currentPage) => ({
          pageNumber: currentPage,
          bitmap: await renderPdfPageForOcr(pdfDocument!, currentPage),
        }),
        recognizer,
      });
      ocrJobRef.current = job;
      const checkpoints = await job.run(setOcrProgress);
      const parsed = ocrCheckpointsToParsedBook({
        checkpoints,
        pageCount: pdfDocument.numPages,
        title: book.title,
        author: book.author,
      });
      const prepared = await prepareChaptersForSync(bookId, parsed);
      for (const batch of chapterBatches(prepared)) {
        await apiJson(`/api/books/${bookId}/chapters`, {
          method: "PUT",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ chapters: batch }),
        });
      }
      const complete = checkpoints.length === pdfDocument.numPages;
      const result = complete
        ? await apiJson<{ chapters: Chapter[] }>(`/api/books/${bookId}/chapters/finalize`, {
            method: "POST",
            headers: { "content-type": "application/json" },
            body: JSON.stringify({ chapterIds: prepared.map((chapter) => chapter.id) }),
          })
        : await apiJson<{ chapters: Chapter[] }>(`/api/books/${bookId}/chapters`);
      setChapters(result.chapters);
      const first = result.chapters[0];
      if (first) {
        setActiveChapterId(first.id);
        setPageNumber(first.pageStart ?? 1);
      }
      setMessage(complete ? "OCR 已完成。请抽查正文，确认后即可收听。" : `OCR 已暂停，已保存前 ${checkpoints.length} 页；下次会从断点继续。`);
    } catch (ocrError) {
      setError(ocrError instanceof Error ? ocrError.message : "OCR 未能完成，已识别的页面仍保存在这台设备上。");
    } finally {
      ocrJobRef.current = null;
      setOcrRunning(false);
      await loadedPdf?.destroy();
    }
  }

  if (loading) return <section className="state-card" role="status"><span className="loading-dot" />正在准备校对页…</section>;
  if (error && !book) return <section className="state-card state-error" role="alert"><strong>校对页没有打开</strong><p>{error}</p></section>;

  return (
    <section className="proofreader-card">
      <aside className="chapter-rail">
        <div className="chapter-rail-title"><strong>{book?.title}</strong><small>{chapters.length} 章</small></div>
        <nav aria-label="章节">
          {chapters.map((chapter) => (
            <button key={chapter.id} className={chapter.id === activeChapter?.id ? "active" : ""} onClick={() => { setActiveChapterId(chapter.id); setPageNumber(chapter.pageStart ?? 1); }}>
              <span>{chapter.position + 1}</span><span>{chapter.title}<small>{chapter.segments.filter((segment) => segment.included).length} 个朗读片段</small></span>
            </button>
          ))}
        </nav>
      </aside>

      <div className="proofreader-workspace">
        <div className="proofreader-toolbar">
          <div><strong>{activeChapter?.title ?? "尚无章节"}</strong><small>{activeChapter?.pageStart ? `第 ${activeChapter.pageStart}–${activeChapter.pageEnd ?? activeChapter.pageStart} 页` : "EPUB 正文"}</small></div>
          <div>
            <button onClick={renameChapter}>重命名</button>
            <button onClick={mergeNext}>合并下一章</button>
            <button className="save-button" disabled={saving} onClick={() => void save()}>{saving ? "保存中…" : "保存修改"}</button>
          </div>
        </div>

        <div className="proofreader-columns">
          <section className="source-pane" aria-labelledby="source-pane-title">
            <header><h2 id="source-pane-title">原始页面</h2>{book?.format === "pdf" && <div><button disabled={pageNumber <= 1} onClick={() => setPageNumber((page) => page - 1)}>上一页</button><span>第 {pageNumber} 页</span><button disabled={Boolean(activeChapter?.pageEnd && pageNumber >= activeChapter.pageEnd)} onClick={() => setPageNumber((page) => page + 1)}>下一页</button></div>}</header>
            {book?.format === "pdf" ? <div className="pdf-canvas-wrap"><canvas ref={canvasRef} aria-label={`PDF 第 ${pageNumber} 页`} /></div> : <div className="epub-original"><p>EPUB 原文已安全提取为章节正文。为了避免脚本与外链风险，这里不直接执行书内 HTML。</p>{activeChapter?.segments.map((segment) => <p key={segment.id}>{segment.text}</p>)}</div>}
          </section>

          <section className="text-pane" aria-labelledby="text-pane-title">
            <header><h2 id="text-pane-title">可朗读正文</h2><span>逐段保存 · 中英文自动切换</span></header>
            <div className="segment-editor-list">
              {activeChapter?.segments.map((segment, index) => (
                <article key={segment.id} className={`segment-editor ${segment.included ? "" : "excluded"}`}>
                  <div className="segment-meta"><span>{index + 1}</span><em>{segment.language === "zh-CN" ? "中文" : "English"}</em>{segment.warning && <strong>{warningLabels[segment.warning] ?? segment.warning}</strong>}</div>
                  <textarea aria-label={`第 ${index + 1} 段正文`} value={segment.text} onChange={(event) => updateSegment(segment.id, { text: event.target.value })} />
                  <footer>
                    <label><input type="checkbox" checked={segment.included} onChange={(event) => updateSegment(segment.id, { included: event.target.checked })} />{segment.included ? "朗读此段" : "跳过此段"}</label>
                    <button onClick={() => splitAt(segment.id)}>从这里拆分</button>
                  </footer>
                </article>
              ))}
              {!activeChapter?.segments.length && <div className="state-card ocr-card"><strong>还没有可朗读正文</strong><p>这可能是扫描 PDF。OCR 会在当前 Edge 浏览器中免费运行，并逐页保存断点；两栏页面会按先左后右排列。</p>{book?.format === "pdf" && <div className="ocr-actions"><button className="button button-primary" disabled={ocrRunning} onClick={() => void runOcr()}>{ocrRunning ? "正在识别…" : ocrProgress?.status === "paused" ? "继续 OCR" : "开始 OCR"}</button>{ocrRunning && <button className="button button-secondary" onClick={() => ocrJobRef.current?.pause()}>识别完本页后暂停</button>}</div>}{ocrProgress && <div className="ocr-progress"><progress max={ocrProgress.totalPages} value={ocrProgress.completedPages} /><span>{ocrProgress.completedPages} / {ocrProgress.totalPages} 页</span><small>{ocrDetail}</small></div>}</div>}
            </div>
          </section>
        </div>
        {(message || error) && <p className={`proofreader-message ${error ? "error" : ""}`} role={error ? "alert" : "status"}>{error || message}</p>}
      </div>
    </section>
  );
}
