"use client";

import { ChangeEvent, DragEvent, useRef, useState, useSyncExternalStore } from "react";
import { useRouter } from "next/navigation";
import { UploadIcon } from "@/components/AppBrand";
import { parseEpub } from "@/lib/client/parsers/epub";
import { parsePdf } from "@/lib/client/parsers/pdf";
import { chapterBatches, prepareChaptersForSync } from "@/lib/client/import/prepare";
import { ApiError, apiJson } from "@/lib/client/http";
import { createBrowserUploadCheckpointStore } from "@/lib/client/upload-checkpoints";
import {
  abortUpload,
  createUpload,
  inspectBookFile,
  resumeUpload,
  uploadBookFile,
} from "@/lib/client/upload";

type Stage = "choose" | "upload" | "parse" | "ocr" | "chapters" | "ready" | "error";

const stages: Array<{ id: Exclude<Stage, "choose" | "error">; label: string }> = [
  { id: "upload", label: "上传" },
  { id: "parse", label: "解析" },
  { id: "ocr", label: "文字识别" },
  { id: "chapters", label: "整理章节" },
  { id: "ready", label: "可以收听" },
];

const subscribeHydration = () => () => undefined;
const clientHydrationSnapshot = () => true;
const serverHydrationSnapshot = () => false;

function stageIndex(stage: Stage) {
  return stages.findIndex((item) => item.id === stage);
}

export function ImportWizard() {
  const router = useRouter();
  const inputRef = useRef<HTMLInputElement>(null);
  const [file, setFile] = useState<File | null>(null);
  const [stage, setStage] = useState<Stage>("choose");
  const [progress, setProgress] = useState(0);
  const [message, setMessage] = useState("");
  const [dragging, setDragging] = useState(false);
  const [uploadId, setUploadId] = useState<string | null>(null);
  const interactive = useSyncExternalStore(
    subscribeHydration,
    clientHydrationSnapshot,
    serverHydrationSnapshot,
  );

  async function choose(candidate: File | null) {
    if (!candidate) return;
    setMessage("");
    try {
      await inspectBookFile(candidate);
      setFile(candidate);
      setStage("choose");
      setProgress(0);
    } catch (error) {
      setFile(null);
      setStage("error");
      setMessage(error instanceof Error ? error.message : "无法读取这个文件。");
    }
  }

  async function start() {
    if (!file) return;
    setMessage("");
    try {
      setStage("upload");
      const checkpointStore = await createBrowserUploadCheckpointStore();
      const checkpoint = await checkpointStore.get(file);
      let session;
      if (checkpoint) {
        try {
          const restored = await resumeUpload(checkpoint.uploadId);
          session = ["created", "uploading", "complete"].includes(restored.status)
            ? restored
            : await createUpload(file);
        } catch (error) {
          if (!(error instanceof ApiError) || ![404, 409, 410].includes(error.status)) throw error;
          await checkpointStore.clear(file);
          session = await createUpload(file);
        }
      } else {
        session = await createUpload(file);
      }
      await checkpointStore.save(file, {
        uploadId: session.id,
        expiresAt: session.expiresAt,
      });
      setUploadId(session.id);
      const book = await uploadBookFile(file, session, ({ uploadedBytes, totalBytes }) => {
        setProgress(Math.round((uploadedBytes / totalBytes) * 100));
      });
      await checkpointStore.clear(file);

      setStage("parse");
      const bytes = await file.arrayBuffer();
      const parsed = session.format === "epub"
        ? await parseEpub(bytes)
        : await parsePdf(bytes);

      await apiJson(`/api/books/${book.id}`, {
        method: "PATCH",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          title: parsed.title || book.title,
          author: parsed.author || book.author,
        }),
      });

      if (parsed.requiresOcr) {
        setStage("ocr");
        setMessage("这是扫描版 PDF。原文件已经安全保存，请在下一页启动可暂停的 Windows Edge OCR。");
        router.push(`/books/${book.id}/edit?ocr=required`);
        return;
      }

      setStage("chapters");
      const prepared = await prepareChaptersForSync(book.id, parsed);
      for (const batch of chapterBatches(prepared)) {
        await apiJson(`/api/books/${book.id}/chapters`, {
          method: "PUT",
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ chapters: batch }),
        });
      }
      await apiJson(`/api/books/${book.id}/chapters/finalize`, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ chapterIds: prepared.map((chapter) => chapter.id) }),
      });
      setStage("ready");
      router.push(`/books/${book.id}/edit`);
    } catch (error) {
      setStage("error");
      setMessage(error instanceof Error ? error.message : "导入失败，请重试。");
    }
  }

  async function cancel() {
    if (uploadId) await abortUpload(uploadId).catch(() => undefined);
    if (file) {
      const checkpointStore = await createBrowserUploadCheckpointStore();
      await checkpointStore.clear(file);
    }
    setUploadId(null);
    setFile(null);
    setStage("choose");
    setProgress(0);
    setMessage("");
  }

  function fileChange(event: ChangeEvent<HTMLInputElement>) {
    void choose(event.target.files?.[0] ?? null);
  }

  function drop(event: DragEvent<HTMLDivElement>) {
    event.preventDefault();
    setDragging(false);
    void choose(event.dataTransfer.files?.[0] ?? null);
  }

  const activeIndex = stageIndex(stage);

  return (
    <section className="import-card">
      <ol className="stage-track" aria-label="导入进度">
        {stages.map((item, index) => (
          <li key={item.id} className={index < activeIndex || stage === "ready" ? "done" : index === activeIndex ? "active" : ""}>
            <span>{index < activeIndex || stage === "ready" ? "✓" : index + 1}</span>{item.label}
          </li>
        ))}
      </ol>

      <div
        className={`drop-zone ${dragging ? "dragging" : ""}`}
        onDragEnter={(event) => { event.preventDefault(); setDragging(true); }}
        onDragOver={(event) => event.preventDefault()}
        onDragLeave={() => setDragging(false)}
        onDrop={drop}
      >
        <input ref={inputRef} type="file" accept=".epub,.pdf,application/epub+zip,application/pdf" disabled={!interactive} onChange={fileChange} />
        <span className="drop-icon"><UploadIcon /></span>
        <h2>{file ? file.name : "把电子书拖到这里"}</h2>
        <p>{file ? `${(file.size / 1024 / 1024).toFixed(1)} MB · 已通过本地格式检查` : "或者从电脑中选择 EPUB 或 PDF"}</p>
        <button className="button" type="button" disabled={!interactive} onClick={() => inputRef.current?.click()}>{file ? "换一个文件" : "选择 EPUB 或 PDF"}</button>
      </div>

      {stage === "upload" && (
        <div className="import-progress" role="status">
          <span><strong>正在上传</strong><em>{progress}%</em></span>
          <progress value={progress} max="100">{progress}%</progress>
          <p>采用 8 MiB 分片；网络中断后，保留文件即可继续缺失分片。</p>
        </div>
      )}

      {message && <p className={stage === "error" ? "form-message error" : "form-message"} role={stage === "error" ? "alert" : "status"}>{message}</p>}

      <div className="import-actions">
        {stage !== "choose" && <button className="button button-quiet" type="button" onClick={() => void cancel()}>取消</button>}
        <button className="button button-primary" type="button" disabled={!file || !["choose", "error"].includes(stage)} onClick={() => void start()}>
          {stage === "error" ? "重新尝试" : "上传并解析"}
        </button>
      </div>

      <aside className="import-notes">
        <h3>上传前请确认</h3>
        <ul>
          <li>支持 EPUB、文字型 PDF 和扫描版 PDF，最大 200 MB。</li>
          <li>只支持无 DRM 文件，不会尝试破解密码或权限保护。</li>
          <li>大型扫描 PDF 的中英文 OCR 请在 Windows Edge 完成；手机适合查看和收听。</li>
          <li>这是公开书架，知道网址的人都能查看、修改或删除内容。</li>
        </ul>
      </aside>
    </section>
  );
}
