"use client";

import Link from "next/link";
import { useEffect, useId, useRef, useState, type RefObject } from "react";
import {
  BookOpen,
  ChevronDown,
  ClipboardCheck,
  FileText,
  Library,
  PanelLeft,
  PanelRight,
  PenLine,
  Sparkles,
  Square,
  Wand2,
  WrapText,
} from "lucide-react";

import { AppShell } from "@/components/AppShell";
import { Drawer } from "@/components/Drawer";
import { ThinkingIndicator } from "@/components/ThinkingIndicator";
import { useToast } from "@/components/Toast";
import { Button } from "@/components/ui/Button";
import { SectionHeader } from "@/components/ui/SectionHeader";
import { StatusNote } from "@/components/ui/StatusNote";
import { TextArea } from "@/components/ui/TextArea";
import type { ProjectResponse } from "@/lib/api/types";
import { friendlyError } from "@/lib/errors/messages";
import { useAutosave } from "@/lib/autosave/useAutosave";
import { useDraftStream } from "@/lib/stream/useDraftStream";
import {
  WORKBENCH_CHAPTER_NO,
  type ChapterEntry,
} from "@/lib/workbench/chapter";
import { composeDirective, STYLE_PRESETS } from "@/lib/workbench/directive";
import { applyRefinement } from "@/lib/workbench/refineApply";
import { buttonClass } from "@/lib/ui/variants";
import { ChapterList, ChapterListContent } from "./ChapterList";
import { ChapterAssistant, AssistantContent } from "./ChapterAssistant";
import { Editor, type EditorSelection } from "./Editor";
import { RefinePanel } from "./RefinePanel";
import { ContinuePanel } from "./ContinuePanel";
import { InlineToolbox } from "./InlineToolbox";

interface WorkbenchProps {
  project: ProjectResponse;
  // 当前章号（来自写作路由 `?chapter=N`）；缺省回退第 1 章。
  chapterNo?: number;
  // RSC 种入的已存草稿正文（GET .../draft）；无草稿（404）→ ""（空编辑器）。
  initialText?: string;
  // 大纲章节目录（GET .../outline）；无大纲 → 空数组（目录回退到仅当前章 + 去大纲提示）。
  chapters?: ChapterEntry[];
}

// 移动端右栏（目录/助手）经抽屉触达；桌面用三栏栅格。
type MobilePanel = "chapters" | "assistant" | null;

// M1：单章工作台。章节列表为占位（无章节列表端点）；默认第 1 章。
// WORKBENCH_CHAPTER_NO 移到 lib/workbench/chapter.ts（非客户端模块），供 Server Component 安全导入。

export function Workbench({
  project,
  chapterNo = WORKBENCH_CHAPTER_NO,
  initialText = "",
  chapters = [],
}: WorkbenchProps) {
  // 用已存草稿作初值；流式开始后由下方 effect 覆盖（流不会被初值反扑——
  // stream.state.text 起始为空，仅当 streaming/done/aborted 且变化才写回）。
  const [text, setText] = useState(initialText);
  const [mobilePanel, setMobilePanel] = useState<MobilePanel>(null);
  // 本章指令（T4-b）：自由文本 + 风格预设 → 写本章时组装传入 draft 生成。
  const [directive, setDirective] = useState("");
  const [presetIds, setPresetIds] = useState<string[]>([]);
  // 选区级 AI 动作（润色/再沟通）：编辑器上报的最新选区 + 结果卡开合。
  const [selection, setSelection] = useState<EditorSelection | null>(null);
  const [refineOpen, setRefineOpen] = useState(false);
  const [continueOpen, setContinueOpen] = useState(false);
  const [toolboxOpen, setToolboxOpen] = useState(false);
  const toast = useToast();
  const autosave = useAutosave(project.id, chapterNo, initialText);
  const stream = useDraftStream();
  const lastStreamText = useRef("");
  const canRefine = selection !== null && selection.text.trim().length > 0;
  const chapterTriggerRef = useRef<HTMLButtonElement>(null);
  const assistantTriggerRef = useRef<HTMLButtonElement>(null);

  // 流式 token 累积进编辑器（打字机）；停止/结束后已生成部分留在草稿。
  useEffect(() => {
    const streamed = stream.state.text;
    if (
      (stream.state.phase === "streaming" ||
        stream.state.phase === "done" ||
        stream.state.phase === "aborted") &&
      streamed !== lastStreamText.current
    ) {
      lastStreamText.current = streamed;
      setText(streamed);
      autosave.onChange(streamed);
    }
  }, [stream.state.phase, stream.state.text, autosave]);

  const onWrite = (): void => {
    void stream.start(
      project.id,
      chapterNo,
      composeDirective(directive, presetIds),
    );
  };

  const togglePreset = (id: string): void => {
    setPresetIds((prev) =>
      prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],
    );
  };

  const onEditorChange = (value: string): void => {
    setText(value);
    autosave.onChange(value);
  };

  // 接受润色/再沟通产出：按内容重锚把选段替换为产出并落草稿；原文已变则提示重选（不盲替换）。
  const onAcceptRefine = (refined: string): void => {
    if (selection) {
      const next = applyRefinement(text, selection.text, refined, {
        start: selection.start,
        end: selection.end,
      });
      if (next === null) {
        toast("正文已改动，找不到原选段，请重新选择后再润色。", "error");
      } else {
        setText(next);
        autosave.onChange(next);
      }
    }
    setRefineOpen(false);
    setSelection(null);
  };

  // 三张 AI 结果卡（润色/续写/工具箱）互斥，避免同时占位。
  const openRefine = (): void => {
    setContinueOpen(false);
    setToolboxOpen(false);
    setRefineOpen(true);
  };

  // 续写前先 flush 自动保存，确保 continue 读到的是最新草稿（with_prior_chapter 读库）。
  const openContinue = (): void => {
    autosave.flush();
    setRefineOpen(false);
    setToolboxOpen(false);
    setContinueOpen(true);
  };

  const openToolbox = (): void => {
    setRefineOpen(false);
    setContinueOpen(false);
    setToolboxOpen(true);
  };

  // 追加一段文本到章末并落草稿（续写候选 / 工具箱产物共用）。
  const appendToDraft = (chunk: string): void => {
    const base = text.trimEnd();
    const next = base.length > 0 ? `${base}\n\n${chunk}` : chunk;
    setText(next);
    autosave.onChange(next);
  };

  const onInsertContinuation = (candidate: string): void => {
    appendToDraft(candidate);
    setContinueOpen(false);
  };

  // 字数统计非空白字符（与中文读者直觉一致：标点/空格不计入正文体量）。
  const wordCount = text.replace(/\s+/g, "").length;
  const closePanel = (): void => setMobilePanel(null);

  // 流式里程碑播报（仅终结句，非逐 token）：完成/停止/失败时写入 role=status 区。
  const liveMessage = ((): string => {
    switch (stream.state.phase) {
      case "done":
        return `本章生成完成，共 ${wordCount} 字`;
      case "aborted":
        return "已停止";
      case "error":
        return stream.state.error
          ? friendlyError(stream.state.error.code, stream.state.error.message)
              .text
          : "本章生成失败，请稍后重试。";
      default:
        return "";
    }
  })();

  return (
    <AppShell
      title={`〈${project.title}〉`}
      subtitle={`第 ${chapterNo} 章`}
      projectId={project.id}
      activeNav="write"
    >
      <div className="grid min-h-[calc(100vh-var(--chrome,4rem))] grid-cols-1 xl:h-[calc(100vh-var(--chrome,4rem))] xl:grid-cols-[12rem_1fr_18rem]">
        <ChapterList
          projectId={project.id}
          chapters={chapters}
          currentChapterNo={chapterNo}
        />

        <section className="flex min-w-0 flex-col bg-bg">
          <MobileContextBar
            chapterNo={chapterNo}
            onOpenChapters={() => setMobilePanel("chapters")}
            onOpenAssistant={() => setMobilePanel("assistant")}
            chapterTriggerRef={chapterTriggerRef}
            assistantTriggerRef={assistantTriggerRef}
          />
          <DirectivePanel
            directive={directive}
            onDirectiveChange={setDirective}
            presetIds={presetIds}
            onTogglePreset={togglePreset}
          />
          {refineOpen && canRefine && selection ? (
            <RefinePanel
              projectId={project.id}
              chapterNo={chapterNo}
              original={selection.text}
              onAccept={onAcceptRefine}
              onClose={() => setRefineOpen(false)}
            />
          ) : null}
          {continueOpen ? (
            <ContinuePanel
              projectId={project.id}
              chapterNo={chapterNo}
              onInsert={onInsertContinuation}
              onClose={() => setContinueOpen(false)}
            />
          ) : null}
          {toolboxOpen ? (
            <InlineToolbox
              projectId={project.id}
              onInsertText={(chunk) => {
                appendToDraft(chunk);
                setToolboxOpen(false);
              }}
              onClose={() => setToolboxOpen(false)}
            />
          ) : null}
          <div className="flex-1 overflow-auto px-6 py-8">
            <Editor
              value={text}
              onChange={onEditorChange}
              streaming={stream.isStreaming}
              onSelectionChange={setSelection}
            />
          </div>
          <Toolbar
            projectId={project.id}
            chapterNo={chapterNo}
            wordCount={wordCount}
            savedLabel={autosave.savedLabel}
            saveStatus={autosave.status}
            streaming={stream.isStreaming}
            streamError={stream.state.error}
            liveMessage={liveMessage}
            onWrite={onWrite}
            onStop={stream.stop}
            canRefine={canRefine}
            onRefineSelection={openRefine}
            onContinue={openContinue}
            onToolbox={openToolbox}
          />
        </section>

        <ChapterAssistant projectId={project.id} chapterNo={chapterNo} />
      </div>

      {/* 窄屏 / 中屏(1024–1280)：目录 / 本章助手经抽屉触达；≥xl 已在三栏栅格里直显。 */}
      <Drawer
        open={mobilePanel === "chapters"}
        onClose={closePanel}
        side="left"
        label="目录"
        triggerRef={chapterTriggerRef}
      >
        <ChapterListContent
          projectId={project.id}
          chapters={chapters}
          currentChapterNo={chapterNo}
        />
      </Drawer>
      <Drawer
        open={mobilePanel === "assistant"}
        onClose={closePanel}
        side="right"
        label="本章助手"
        triggerRef={assistantTriggerRef}
      >
        <AssistantContent projectId={project.id} chapterNo={chapterNo} />
      </Drawer>
    </AppShell>
  );
}

interface MobileContextBarProps {
  chapterNo: number;
  onOpenChapters: () => void;
  onOpenAssistant: () => void;
  chapterTriggerRef: RefObject<HTMLButtonElement | null>;
  assistantTriggerRef: RefObject<HTMLButtonElement | null>;
}

function MobileContextBar({
  chapterNo,
  onOpenChapters,
  onOpenAssistant,
  chapterTriggerRef,
  assistantTriggerRef,
}: MobileContextBarProps) {
  return (
    <div className="flex items-center justify-between gap-2 border-b border-line bg-panel px-4 py-2 xl:hidden">
      <Button
        ref={chapterTriggerRef}
        onClick={onOpenChapters}
        variant="secondary"
        size="sm"
      >
        <PanelLeft className="h-4 w-4" aria-hidden="true" />
        目录
      </Button>
      <span className="min-w-0 truncate font-mono text-xs text-ink-soft">
        第 {chapterNo} 章
      </span>
      <Button
        ref={assistantTriggerRef}
        onClick={onOpenAssistant}
        variant="secondary"
        size="sm"
      >
        <PanelRight className="h-4 w-4" aria-hidden="true" />
        助手
      </Button>
    </div>
  );
}

interface DirectivePanelProps {
  directive: string;
  onDirectiveChange: (value: string) => void;
  presetIds: readonly string[];
  onTogglePreset: (id: string) => void;
}

// 本章指令面板（T4-b）：可折叠的 textarea + 风格快捷预设 chips。
// 默认折叠（不抢编辑视野）；指令覆盖/补充大纲节拍，写本章时组装传入生成。
function DirectivePanel({
  directive,
  onDirectiveChange,
  presetIds,
  onTogglePreset,
}: DirectivePanelProps) {
  const panelId = useId();
  const activeCount = presetIds.length + (directive.trim().length > 0 ? 1 : 0);
  return (
    <details className="group border-b border-line bg-panel px-4 py-3 sm:px-6">
      <summary
        className="flex cursor-pointer list-none items-center justify-between gap-3 text-sm text-ink-soft hover:text-cinnabar"
        aria-controls={panelId}
      >
        <span className="flex items-center gap-2 font-serif text-base text-ink">
          <ChevronDown
            className="h-4 w-4 text-ink-soft transition-transform group-open:rotate-180"
            aria-hidden="true"
          />
          本章写作指令
        </span>
        <span className="rounded border border-line bg-bg px-2 py-0.5 text-xs text-ink-soft">
          {activeCount > 0 ? `${activeCount} 项指令` : "可选"}
        </span>
      </summary>
      <div id={panelId} className="mt-3 space-y-3">
        <SectionHeader
          title="写作指令"
          description="用于补充或覆盖本章大纲节拍，只影响本次生成。"
        />
        <label className="block">
          <span className="sr-only">本章指令</span>
          <TextArea
            value={directive}
            onChange={(e) => onDirectiveChange(e.target.value)}
            rows={2}
            placeholder="本章想怎么写？（可选，覆盖/补充大纲节拍）"
          />
        </label>
        <div className="flex flex-wrap gap-2" role="group" aria-label="风格预设">
          {STYLE_PRESETS.map((preset) => {
            const active = presetIds.includes(preset.id);
            return (
              <Button
                key={preset.id}
                type="button"
                aria-pressed={active}
                onClick={() => onTogglePreset(preset.id)}
                variant={active ? "outline" : "secondary"}
                size="sm"
              >
                {preset.label}
              </Button>
            );
          })}
        </div>
        {activeCount > 0 ? (
          <StatusNote variant="info">
            写本章时会把已选预设和自由指令合并进本次生成请求。
          </StatusNote>
        ) : null}
      </div>
    </details>
  );
}

// 写章失败提示：友好文案（不暴露 raw code）+ 可选「去设置」动作（F4）。
function StreamErrorNote({
  streamError,
}: {
  streamError: { code: string; message: string };
}) {
  const friendly = friendlyError(streamError.code, streamError.message);
  return (
    <p className="mb-2 text-sm text-conflict">
      {friendly.text}
      {friendly.actionHref ? (
        <>
          {" "}
          <Link
            href={friendly.actionHref}
            className="underline hover:text-cinnabar"
          >
            {friendly.actionLabel}
          </Link>
        </>
      ) : null}
    </p>
  );
}

interface ToolbarProps {
  projectId: string;
  chapterNo: number;
  wordCount: number;
  savedLabel: string | null;
  saveStatus: ReturnType<typeof useAutosave>["status"];
  streaming: boolean;
  streamError: { code: string; message: string } | null;
  // 流式里程碑播报句（完成/停止/失败）；仅在 role=status 区出现，不逐 token 刷新。
  liveMessage: string;
  onWrite: () => void;
  onStop: () => void;
  // 选区级润色：有非空选区时可用；点击打开润色/再沟通结果卡。
  canRefine: boolean;
  onRefineSelection: () => void;
  // 续写：读本章前文续写候选。
  onContinue: () => void;
  // 工具箱：编辑器内联调用生成器，结果回填正文。
  onToolbox: () => void;
}

function Toolbar({
  projectId,
  chapterNo,
  wordCount,
  savedLabel,
  saveStatus,
  streaming,
  streamError,
  liveMessage,
  onWrite,
  onStop,
  canRefine,
  onRefineSelection,
  onContinue,
  onToolbox,
}: ToolbarProps) {
  return (
    <div className="sticky bottom-0 z-10 border-t border-line bg-panel/95 px-4 py-2 backdrop-blur sm:px-6 sm:py-3">
      {streamError ? <StreamErrorNote streamError={streamError} /> : null}
      <div className="grid gap-2 sm:flex sm:flex-wrap sm:items-center sm:gap-3">
        <div className="flex min-w-0 flex-wrap items-center gap-2">
        {streaming ? (
          <Button onClick={onStop} variant="danger" size="sm">
            <Square className="h-4 w-4" aria-hidden="true" />
            停
          </Button>
        ) : (
          <Button onClick={onWrite} variant="primary" size="sm">
            <PenLine className="h-4 w-4" aria-hidden="true" />
            写本章
          </Button>
        )}
        {!streaming ? (
          <>
            <Button onClick={onContinue} variant="secondary" size="sm">
              <WrapText className="h-4 w-4" aria-hidden="true" />
              续写
            </Button>
            <Button
              onClick={onRefineSelection}
              disabled={!canRefine}
              variant="secondary"
              size="sm"
              title={canRefine ? undefined : "先在正文里选中一段再润色"}
            >
              <Wand2 className="h-4 w-4" aria-hidden="true" />
              润色选段
            </Button>
            <Button onClick={onToolbox} variant="secondary" size="sm">
              <Sparkles className="h-4 w-4" aria-hidden="true" />
              工具箱
            </Button>
          </>
        ) : null}
        {streaming ? (
          // ThinkingIndicator 自带 role=status，开始时播报一次「生成中」；
          // 易变字数 aria-hidden，仅供视觉，不逐 token 打扰屏读。
          <span className="inline-flex items-center gap-2">
            <ThinkingIndicator label="生成中" className="text-xs text-cinnabar" />
            <span
              aria-hidden="true"
              className="font-mono text-xs text-cinnabar"
            >
              {wordCount} 字
            </span>
          </span>
        ) : (
          <span className="font-mono text-xs text-ink-soft">
            字数 {wordCount}
          </span>
        )}
        {/* 终结句播报区：完成/停止/失败时写入，屏读只播报里程碑。 */}
        <span className="sr-only" role="status" aria-live="polite">
          {liveMessage}
        </span>
        <Link
          href={`/projects/${projectId}/outline`}
          className={buttonClass({ variant: "secondary", size: "sm" })}
        >
          <BookOpen className="h-4 w-4" aria-hidden="true" />
          大纲
        </Link>
        <Link
          href={`/projects/${projectId}/foreshadow`}
          className={buttonClass({
            variant: "secondary",
            size: "sm",
            className: "hidden sm:inline-flex",
          })}
        >
          <Library className="h-4 w-4" aria-hidden="true" />
          伏笔
        </Link>
        <Link
          href={`/projects/${projectId}/review?chapter=${chapterNo}`}
          className={buttonClass({ variant: "outline", size: "sm" })}
        >
          <ClipboardCheck className="h-4 w-4" aria-hidden="true" />
          审稿
        </Link>
        </div>
        <span
          role="status"
          aria-live="polite"
          className="min-w-0 font-mono text-xs text-ink-soft sm:ml-auto"
        >
          <FileText className="mr-1 inline h-3.5 w-3.5" aria-hidden="true" />
          {saveStatus === "saving"
            ? "保存中…"
            : saveStatus === "error"
              ? "保存失败"
              : (savedLabel ?? "尚未保存")}
        </span>
      </div>
    </div>
  );
}
