"use client";

import { useCallback, useMemo, useState } from "react";
import { ArrowLeft, Database, Sparkles } from "lucide-react";

import { useToast } from "@/components/Toast";
import { ConflictAdjudication } from "@/components/generation/ConflictAdjudication";
import { Button } from "@/components/ui/Button";
import { Badge } from "@/components/ui/Badge";
import { Field } from "@/components/ui/Field";
import { StatusNote } from "@/components/ui/StatusNote";
import { TextArea } from "@/components/ui/TextArea";
import { TextInput } from "@/components/ui/TextInput";
import { ThinkingIndicator } from "@/components/ThinkingIndicator";
import type { ToolDescriptorView, ToolInputFieldView } from "@/lib/api/types";
import {
  buildGenerateRequest,
  initialFieldValues,
  missingRequiredFields,
  previewRows,
  templateFillTarget,
  type FieldValues,
  type PreviewItem,
} from "@/lib/toolbox/toolbox";
import { ingestTable, isSingleObjectIngest } from "@/lib/toolbox/ingest";
import { buildManuscriptExcerpt } from "@/lib/workbench/manuscriptExcerpt";
import { useGenerator } from "@/lib/toolbox/useGenerator";
import { TemplateFiller } from "./TemplateFiller";

interface GeneratorRunnerProps {
  projectId: string;
  tool: ToolDescriptorView;
  onClose: () => void;
  // 可选：把某条预览文本插入正文（编辑器内联工具箱场景）。工具箱页不传 → 不显示「插入正文」。
  onInsertText?: (text: string) => void;
  // 可选：作者【已写正文】。传入且当前工具有 brief 字段时，表单里出现「用当前正文」按钮，
  // 点击把正文摘录填进 brief，让内容感知型生成器（如书名）据正文反推。工具箱页不传 → 不显示。
  manuscriptText?: string;
}

// 声明驱动的通用生成器（核心）：按 descriptor.input_fields 渲染表单 → 调通用 generate →
// 按 output_kind 渲染结构化预览 → 可入库者勾选 + 入库（复用 ConflictAdjudication 过 409）。
// 不硬编码任何工具的表单/预览/入库形状——全部由声明 + lib/toolbox 纯逻辑驱动。
export function GeneratorRunner({
  projectId,
  tool,
  onClose,
  onInsertText,
  manuscriptText,
}: GeneratorRunnerProps) {
  const gen = useGenerator();
  const toast = useToast();
  const [values, setValues] = useState<FieldValues>(() =>
    initialFieldValues(tool.input_fields),
  );
  // 入库勾选：预览行下标集合（与 gen.rawPreview 行对齐）。
  const [selected, setSelected] = useState<Set<number>>(new Set());

  const setField = useCallback((name: string, value: string): void => {
    setValues((prev) => ({ ...prev, [name]: value }));
  }, []);

  // 「从模板填入」的目标字段（brief / text；无则不渲染）。匹配按 name，展示用中文 label。
  const fillTarget = useMemo(
    () => templateFillTarget(tool.input_fields),
    [tool.input_fields],
  );
  const fillLabel = useMemo(
    () =>
      (tool.input_fields ?? []).find((f) => f.name === fillTarget)?.label ??
      fillTarget,
    [tool.input_fields, fillTarget],
  );

  // 当前工具是否有名为 brief 的输入字段——只有它才承接「据正文反推」。
  const hasBriefField = useMemo(
    () => (tool.input_fields ?? []).some((f) => f.name === "brief"),
    [tool.input_fields],
  );
  // 有 brief 字段且拿到非空正文时，才提供「用当前正文」。
  const canUseManuscript =
    hasBriefField && (manuscriptText?.trim().length ?? 0) > 0;

  const onUseManuscript = useCallback((): void => {
    if (!manuscriptText) return;
    const excerpt = buildManuscriptExcerpt(manuscriptText);
    if (excerpt.length === 0) {
      toast("当前还没有正文可供反推。", "error");
      return;
    }
    setField("brief", excerpt);
    toast("已按当前正文填入，可继续编辑后生成。", "success");
  }, [manuscriptText, setField, toast]);

  const onGenerate = useCallback(async (): Promise<void> => {
    const missing = missingRequiredFields(tool.input_fields, values);
    if (missing.length > 0) {
      toast(`请填写：${missing.join("、")}`, "error");
      return;
    }
    setSelected(new Set());
    await gen.generate(projectId, tool.key, buildGenerateRequest(values));
  }, [gen, projectId, tool, values, toast]);

  const rows = useMemo(
    () => previewRows(gen.rawPreview),
    [gen.rawPreview],
  );

  const table = gen.outputKind ? ingestTable(gen.outputKind) : null;
  // 单对象产物（如拆书）：整个 preview 即一条入库项，无逐行勾选；行式产物按勾选入库。
  const singleObject = gen.outputKind
    ? isSingleObjectIngest(gen.outputKind)
    : false;
  const hasPreview = (gen.preview?.items.length ?? 0) > 0;
  const canIngest =
    tool.ingestable &&
    table !== null &&
    (singleObject ? hasPreview : rows.length > 0);

  const toggle = useCallback((idx: number): void => {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(idx)) {
        next.delete(idx);
      } else {
        next.add(idx);
      }
      return next;
    });
  }, []);

  const selectedRows = useMemo(
    () => rows.filter((_, i) => selected.has(i)),
    [rows, selected],
  );

  const runIngest = useCallback(
    async (acknowledge: boolean): Promise<void> => {
      if (!gen.outputKind) return;
      const chapterNo = Number(values["chapter_no"]?.trim() || "") || null;
      // 单对象产物（拆书）：整个 rawPreview 作唯一入库行；行式产物用勾选行。
      const ingestRows =
        singleObject && gen.rawPreview ? [gen.rawPreview] : selectedRows;
      await gen.ingest(projectId, tool.key, {
        outputKind: gen.outputKind,
        rows: ingestRows,
        chapterNo,
        acknowledgeConflicts: acknowledge,
      });
    },
    [gen, projectId, tool.key, selectedRows, values, singleObject],
  );

  const generating = gen.genStatus === "generating";
  const ingesting = gen.ingestStatus === "ingesting";

  return (
    <section
      className="flex flex-col gap-5 rounded border border-line bg-panel p-5 shadow-paper"
      aria-label={tool.title}
    >
      <div className="flex items-start justify-between gap-4">
        <div>
          <div className="mb-2 flex flex-wrap items-center gap-2">
            <h2 className="font-serif text-lg text-ink">{tool.title}</h2>
            {tool.ingestable ? (
              <Badge variant="info">
                <Database className="h-3 w-3" aria-hidden="true" />
                可入库
              </Badge>
            ) : null}
          </div>
          <p className="mt-1 text-sm text-ink-soft">{tool.subtitle}</p>
        </div>
        <Button onClick={onClose} variant="secondary" size="sm">
          <ArrowLeft className="h-4 w-4" aria-hidden="true" />
          返回
        </Button>
      </div>

      <form
        className="grid gap-3 rounded border border-line bg-bg/50 p-4"
        onSubmit={(e) => {
          e.preventDefault();
          void onGenerate();
        }}
      >
        <TemplateFiller
          targetField={fillTarget}
          targetLabel={fillLabel}
          onFill={setField}
        />
        {canUseManuscript ? (
          <div className="flex items-center justify-between gap-2 rounded border border-line bg-bg/50 p-2">
            <span className="text-xs text-ink-soft">
              据当前正文反推（自动摘录首尾，控制长度）
            </span>
            <Button
              type="button"
              onClick={onUseManuscript}
              variant="outline"
              size="sm"
            >
              <Sparkles className="h-4 w-4" aria-hidden="true" />
              用当前正文
            </Button>
          </div>
        ) : null}
        {(tool.input_fields ?? []).map((field) => (
          <FormField
            key={field.name}
            field={field}
            value={values[field.name] ?? ""}
            onChange={(v) => setField(field.name, v)}
          />
        ))}
        <Button type="submit" disabled={generating} variant="primary">
          <Sparkles className="h-4 w-4" aria-hidden="true" />
          {generating ? "生成中…" : "生成"}
        </Button>
        {generating ? (
          <ThinkingIndicator
            label="生成中，长文可能需要十几秒…"
            className="text-cinnabar"
          />
        ) : null}
      </form>

      {gen.genStatus === "preview" && gen.preview ? (
        <div className="flex flex-col gap-3">
          <h3 className="font-serif text-sm text-ink">
            预览（{gen.preview.items.length}）
            {canIngest
              ? singleObject
                ? "（可入库）"
                : "（勾选后可入库）"
              : null}
          </h3>
          <ul className="flex flex-col gap-2">
            {gen.preview.items.map((item, i) => (
              <PreviewRow
                key={i}
                item={item}
                index={i}
                selectable={canIngest && !singleObject}
                checked={selected.has(i)}
                onToggle={toggle}
                onInsert={
                  onInsertText
                    ? () => onInsertText(item.body ?? item.heading ?? "")
                    : undefined
                }
              />
            ))}
          </ul>

          {canIngest && gen.ingestStatus !== "conflict" ? (
            <Button
              onClick={() => void runIngest(false)}
              disabled={ingesting || (!singleObject && selected.size === 0)}
              variant="outline"
            >
              <Database className="h-4 w-4" aria-hidden="true" />
              {ingesting
                ? "入库中…"
                : singleObject
                  ? `入库为规则至 ${table}`
                  : `入库选中（${selected.size}）至 ${table}`}
            </Button>
          ) : null}

          {gen.ingestStatus === "conflict" && gen.conflicts ? (
            <ConflictAdjudication
              conflicts={gen.conflicts}
              busy={ingesting}
              onAcknowledge={() => void runIngest(true)}
              onCancel={() => gen.reset()}
            />
          ) : null}

          {gen.ingestStatus === "done" ? (
            <StatusNote variant="success" title={`已入库 ${gen.created.length} 项`}>
              <p>内容已写入设定库，可在对应页面查看与续编。</p>
            </StatusNote>
          ) : null}
        </div>
      ) : null}
    </section>
  );
}

interface FormFieldProps {
  field: ToolInputFieldView;
  value: string;
  onChange: (value: string) => void;
}

// 声明式控件映射：type → text / textarea / number（select 暂同 text，后端未给 options）。
function FormField({ field, value, onChange }: FormFieldProps) {
  return (
    <Field label={field.label} help={field.help} required={field.required}>
      {field.type === "textarea" ? (
        <TextArea
          value={value}
          onChange={(e) => onChange(e.target.value)}
          rows={3}
          aria-label={field.label}
        />
      ) : (
        <TextInput
          type={field.type === "number" ? "number" : "text"}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          aria-label={field.label}
        />
      )}
    </Field>
  );
}

interface PreviewRowProps {
  item: PreviewItem;
  index: number;
  selectable: boolean;
  checked: boolean;
  onToggle: (index: number) => void;
  // 可选：把本条插入正文（编辑器内联工具箱）。有可插入文本时才渲染按钮。
  onInsert?: () => void;
}

// 统一预览行渲染（heading + 次要字段 + 正文段），由 mapPreview 归一，跨工具复用。
function PreviewRow({
  item,
  index,
  selectable,
  checked,
  onToggle,
  onInsert,
}: PreviewRowProps) {
  const canInsert = onInsert && (item.body || item.heading);
  return (
    <li className="flex gap-2 rounded border border-line bg-bg p-3 text-sm">
      {selectable ? (
        <input
          type="checkbox"
          checked={checked}
          onChange={() => onToggle(index)}
          className="mt-1"
          aria-label={`选择第 ${index + 1} 项入库`}
        />
      ) : null}
      <div className="min-w-0 flex-1">
        {item.heading ? (
          <p className="mb-1 font-serif text-base text-ink">{item.heading}</p>
        ) : null}
        {item.fields.map((f, j) => (
          <p key={j} className="text-ink-soft">
            <span className="text-ink-soft/80">{f.label}：</span>
            {f.value}
          </p>
        ))}
        {item.body ? (
          <p className="whitespace-pre-wrap text-ink">{item.body}</p>
        ) : null}
        {canInsert ? (
          <Button
            onClick={onInsert}
            variant="outline"
            size="sm"
            className="mt-2"
          >
            <ArrowLeft className="h-4 w-4 rotate-90" aria-hidden="true" />
            插入正文
          </Button>
        ) : null}
      </div>
    </li>
  );
}
