import { useEffect, useMemo, useRef, useState } from "react";
import { Check, FilePlus2, RefreshCw, Trash2, Upload, X } from "lucide-react";
import { HttpError } from "../../shared/api/errors";
import {
  AttachmentType,
  DocumentType,
  SecurityLevel,
} from "../../shared/api/enums";
import { notifyContentChanged } from "../../shared/state/contentRefresh";
import { attachmentApi } from "../attachments/attachmentApi";
import { useCategoryTree } from "../categories/categoryState";
import { flattenCategoryOptions } from "../categories/categoryTree";
import { useDictionaries } from "../dictionaries/dictionaryContext";
import { documentApi } from "../documents/documentApi";
import {
  cacheDetail,
  invalidateDetail,
  useDocumentList,
} from "../documents/documentState";
import type { DocumentDetail, DocumentSummary } from "../documents/documentTypes";
import {
  ACCEPTED_DOCUMENT_EXTENSIONS,
  suggestedNameFromFile,
  validateSelectedFile,
} from "../files/filename";
import { invalidatePreview } from "../files/fileState";
import { attachmentMutationApi } from "./attachmentMutationApi";
import { documentMutationApi } from "./documentMutationApi";
import {
  applyBatchResult,
  createBatchDraft,
  retainFailedDrafts,
} from "./mutationState";
import {
  parseTags,
  type AttachmentCreateMetadata,
  type BatchDraft,
  type DocumentCreateMetadata,
  type DocumentUploadMetadata,
} from "./mutationTypes";

const BLUE = "#3191eb";
const NAVY = "#1A5299";
const ACCEPT = ACCEPTED_DOCUMENT_EXTENSIONS.join(",");

function mutationMessage(error: HttpError): string {
  const messages: Record<string, string> = {
    PAYLOAD_TOO_LARGE: "文件或本次请求过大，请减少文件数量或选择更小的文件",
    UNSUPPORTED_FILE_TYPE: "文件类型、文件内容或容器结构不符合服务端要求",
    BATCH_MANIFEST_MISMATCH: "文件与元数据数量不一致，请重新检查批次",
    DATA_VERSION_CONFLICT: "数据已被其他操作修改，已重新加载最新信息，请确认后再编辑",
    MAIN_PLAN_HAS_CHILDREN: "该主案仍存在有效子方案，不能删除",
    ATTACHMENT_IN_USE: "该附件仍被主案使用，必须先解除全部挂载",
    FILE_NOT_FOUND: "服务端未找到对应文件",
    FORBIDDEN: "当前账号无权执行该操作",
    DOCUMENT_VIEW_FORBIDDEN: "当前账号无权查看该文档",
    DOCUMENT_DOWNLOAD_FORBIDDEN: "当前账号无权下载该文档",
    CATEGORY_NOT_LEAF: "所选分类不是叶子分类，请选择最末级分类",
  };
  return messages[error.code] ?? error.message;
}

function asMutationError(value: unknown): HttpError {
  return value instanceof HttpError
    ? value
    : new HttpError({
        httpStatus: 0,
        code: "INTERNAL_ERROR",
        message: value instanceof Error ? value.message : "操作失败",
        details: null,
        requestId: "client-validation",
        cause: value,
      });
}

function ErrorBlock({ error }: { error: HttpError | null }) {
  if (!error) return null;
  const details = error.details && typeof error.details === "object"
    ? error.details as Record<string, unknown>
    : null;
  const plans = Array.isArray(details?.mainPlans) ? details.mainPlans : [];
  return (
    <div className="p-3 text-xs font-bold leading-5" style={{ background: "rgba(26,82,153,.08)", color: NAVY }}>
      <div>{mutationMessage(error)}</div>
      {typeof details?.mountedPlanCount === "number" && <div>当前挂载主案数：{details.mountedPlanCount}</div>}
      {typeof details?.childCategoryCount === "number" && <div>下级分类数：{details.childCategoryCount}</div>}
      {plans.map((plan, index) => {
        const value = plan && typeof plan === "object" ? plan as Record<string, unknown> : {};
        return <div key={String(value.id ?? index)}>· {String(value.documentName ?? value.name ?? "未命名主案")}</div>;
      })}
      {error.requestId && <div className="opacity-60">错误编号：{error.requestId}</div>}
    </div>
  );
}

function Modal({ title, children, onClose, wide = false }: {
  title: string;
  children: React.ReactNode;
  onClose: () => void;
  wide?: boolean;
}) {
  return (
    <div className="fixed inset-0 z-[60] flex items-center justify-center" style={{ background: "rgba(26,82,153,.55)" }} onClick={onClose}>
      <div className={`${wide ? "w-[1080px]" : "w-[660px]"} max-h-[90vh] overflow-auto bg-white shadow-2xl`} onClick={(event) => event.stopPropagation()}>
        <div className="h-11 px-5 flex items-center justify-between text-white sticky top-0 z-10" style={{ background: NAVY }}>
          <span className="font-black">{title}</span>
          <button onClick={onClose}><X size={17} /></button>
        </div>
        {children}
      </div>
    </div>
  );
}

function Field({ label, children }: { label: string; children: React.ReactNode }) {
  return <label className="block"><span className="block text-xs font-black mb-1" style={{ color: NAVY }}>{label}</span>{children}</label>;
}

const inputClass = "w-full h-9 px-3 text-sm font-bold border outline-none";
const selectStyle = { color: NAVY, borderColor: "rgba(26,82,153,.25)" };

function useReferenceData() {
  const categories = useCategoryTree();
  const mainPlans = useDocumentList({
    documentType: DocumentType.MAIN,
    sortBy: "documentName",
    sortDirection: "asc",
    page: 1,
    pageSize: 100,
  });
  return {
    categories: flattenCategoryOptions(categories.tree),
    categoryLoading: categories.status === "loading",
    refreshCategories: categories.refresh,
    mainPlans: mainPlans.data?.items ?? [],
    mainPlanLoading: mainPlans.loading,
  };
}

function defaultDocumentMetadata(
  name: string,
  categoryId: string,
): DocumentCreateMetadata {
  return {
    documentName: name,
    documentType: "MAIN",
    summary: null,
    categoryId,
    securityLevel: SecurityLevel.INTERNAL,
    tags: [],
  };
}

export function SingleUploadModal({ kind, documentType: fixedDocumentType = "MAIN", initialParentId, onClose }: {
  kind: "document" | "attachment";
  documentType?: "MAIN" | "SUB_PLAN";
  initialParentId?: string;
  onClose: () => void;
}) {
  const references = useReferenceData();
  const dictionaries = useDictionaries();
  const [file, setFile] = useState<File | null>(null);
  const [documentName, setDocumentName] = useState("");
  const [documentType, setDocumentType] = useState<"MAIN" | "SUB_PLAN">(
    initialParentId ? "SUB_PLAN" : fixedDocumentType,
  );
  const [parentDocumentId, setParentDocumentId] = useState(initialParentId ?? "");
  const [categoryId, setCategoryId] = useState("");
  const [summary, setSummary] = useState("");
  const [securityLevel, setSecurityLevel] = useState(SecurityLevel.INTERNAL);
  const [attachmentType, setAttachmentType] = useState(AttachmentType.OTHER);
  const [tags, setTags] = useState("");
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<HttpError | null>(null);
  const [formError, setFormError] = useState("");
  const [overwrite, setOverwrite] = useState<{
    existingDocumentId: string;
    existingDocumentName: string;
    existingRowVersion: number;
  } | null>(null);
  const controller = useRef<AbortController | null>(null);

  useEffect(() => () => controller.current?.abort(), []);

  const submit = async (confirmedOverwrite = false) => {
    if (submitting) return;
    const fileError = file ? validateSelectedFile(file) : "请选择文件";
    if (fileError || !documentName.trim()) {
      setFormError(fileError || "名称不能为空");
      return;
    }
    const selectedCategory = references.categories.find((category) => category.id === categoryId);
    if (kind === "document" && documentType === "MAIN" && (!selectedCategory || !selectedCategory.isLeaf)) {
      setFormError("请选择有效的最末级分类");
      return;
    }
    if (kind === "document" && documentType === "SUB_PLAN" && !parentDocumentId) {
      setFormError("子方案必须选择所属主案");
      return;
    }
    setFormError("");
    setError(null);
    setSubmitting(true);
    controller.current = new AbortController();
    try {
      if (kind === "attachment") {
        const metadata: AttachmentCreateMetadata = {
          documentName: documentName.trim(),
          attachmentType,
          summary: summary.trim() || null,
          tags: parseTags(tags),
        };
        const detail = await attachmentMutationApi.create(file!, metadata, controller.current.signal);
        cacheDetail("attachment", detail);
        notifyContentChanged("attachments");
      } else {
        const base = {
          documentName: documentName.trim(),
          summary: summary.trim() || null,
          securityLevel,
          tags: parseTags(tags),
        };
        const metadata: DocumentUploadMetadata = documentType === "MAIN"
          ? { ...base, documentType: "MAIN", categoryId }
          : { ...base, documentType: "SUB_PLAN", parentDocumentId };
        if (confirmedOverwrite && overwrite) {
          metadata.overwriteDocumentId = overwrite.existingDocumentId;
          metadata.rowVersion = overwrite.existingRowVersion;
        }
        const detail = await documentMutationApi.create(file!, metadata, controller.current.signal);
        cacheDetail("document", detail);
        notifyContentChanged("documents");
      }
      onClose();
    } catch (value) {
      const nextError = asMutationError(value);
      if (nextError.code === "DOCUMENT_NAME_CONFLICT" && nextError.details && typeof nextError.details === "object") {
        const details = nextError.details as Record<string, unknown>;
        if (typeof details.existingDocumentId === "string" && typeof details.existingDocumentName === "string" && typeof details.existingRowVersion === "number") {
          setOverwrite({
            existingDocumentId: details.existingDocumentId,
            existingDocumentName: details.existingDocumentName,
            existingRowVersion: details.existingRowVersion,
          });
          setError(null);
          return;
        }
      }
      setError(nextError);
      if (nextError.code === "CATEGORY_NOT_LEAF") references.refreshCategories();
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <Modal title={kind === "attachment" ? "上传共享附件" : "上传方案文档"} onClose={onClose}>
      <div className="p-6 space-y-4">
        <Field label="文件 *"><input type="file" accept={ACCEPT} disabled={submitting} onChange={(event) => {
          const selected = event.target.files?.[0] ?? null;
          setFile(selected);
          setOverwrite(null);
          if (selected && !documentName) setDocumentName(suggestedNameFromFile(selected));
          setFormError(selected ? validateSelectedFile(selected) ?? "" : "");
        }} className={inputClass} style={selectStyle} /></Field>
        {kind === "document" && <Field label="方案类型 *"><select value={documentType} disabled={submitting || !!initialParentId} onChange={(event) => { const next = event.target.value as "MAIN" | "SUB_PLAN"; setDocumentType(next); setOverwrite(null); if (next === "SUB_PLAN" && !parentDocumentId) setParentDocumentId(references.mainPlans[0]?.id ?? ""); }} className={inputClass} style={selectStyle}><option value="MAIN">主案</option><option value="SUB_PLAN">子案</option></select></Field>}
        {kind === "document" && documentType === "SUB_PLAN" && <Field label="所属主案 *"><select value={parentDocumentId} disabled={submitting || references.mainPlanLoading} onChange={(event) => { setParentDocumentId(event.target.value); setOverwrite(null); }} className={inputClass} style={selectStyle}><option value="">请选择主案</option>{references.mainPlans.map((plan) => <option key={plan.id} value={plan.id}>{plan.documentName}</option>)}</select></Field>}
        <Field label={`${kind === "attachment" ? "附件" : "文档"}名称 *`}><input value={documentName} disabled={submitting} onChange={(event) => { setDocumentName(event.target.value); setOverwrite(null); }} className={inputClass} style={selectStyle} /></Field>
        {kind === "attachment" ? (
          <Field label="附件类型 *"><select value={attachmentType} disabled={submitting} onChange={(event) => setAttachmentType(event.target.value as typeof attachmentType)} className={inputClass} style={selectStyle}>{dictionaries.options("attachmentTypes").map((option) => <option key={option.code} value={option.code}>{option.label}</option>)}</select></Field>
        ) : (
          <>
            {documentType === "MAIN" ? <Field label="方案分类 *"><select value={categoryId} disabled={submitting || references.categoryLoading} onChange={(event) => { setCategoryId(event.target.value); setOverwrite(null); }} className={inputClass} style={selectStyle}><option value="">请选择最末级分类</option>{references.categories.map((category) => <option key={category.id} value={category.id} disabled={!category.isLeaf} title={category.categoryPath}>{category.displayLabel}{category.isLeaf ? "" : "（请选择下级分类）"}</option>)}</select></Field> : <div className="px-3 py-2 text-xs font-bold" style={{ color: NAVY, background: "#EBF4FD" }}>方案分类继承自主案：{references.mainPlans.find((plan) => plan.id === parentDocumentId)?.categoryPath ?? references.mainPlans.find((plan) => plan.id === parentDocumentId)?.categoryName ?? "请选择所属主案"}</div>}
            <div className="grid grid-cols-1 gap-3">
              <Field label="密级 *"><select value={securityLevel} onChange={(event) => setSecurityLevel(event.target.value as typeof securityLevel)} className={inputClass} style={selectStyle}>{dictionaries.options("securityLevels").map((option) => <option key={option.code} value={option.code}>{option.label}</option>)}</select></Field>
            </div>
          </>
        )}
        <Field label="概述"><textarea value={summary} disabled={submitting} onChange={(event) => setSummary(event.target.value)} className="w-full min-h-20 p-3 text-sm font-bold border outline-none" style={selectStyle} /></Field>
        <Field label="标签（逗号分隔）"><input value={tags} disabled={submitting} onChange={(event) => setTags(event.target.value)} className={inputClass} style={selectStyle} /></Field>
        {formError && <div className="text-xs font-bold" style={{ color: NAVY }}>{formError}</div>}
        <ErrorBlock error={error} />
        {overwrite && <div role="alertdialog" className="p-4 text-sm font-bold border" style={{ color: NAVY, borderColor: BLUE, background: "#EBF4FD" }}><div>已存在同名{documentType === "MAIN" ? "主案" : "子案"}“{overwrite.existingDocumentName}”，是否覆盖？</div><div className="mt-1 text-xs opacity-70">覆盖将保留原记录、权限及关联关系，仅替换文件和本次填写的元数据。</div><div className="flex justify-end gap-2 mt-3"><button type="button" onClick={() => setOverwrite(null)} className="px-3 py-1.5 border" style={selectStyle}>取消覆盖</button><button type="button" disabled={submitting} onClick={() => void submit(true)} className="px-3 py-1.5 text-white" style={{ background: BLUE }}>确认覆盖</button></div></div>}
        <div className="flex justify-end gap-2"><button onClick={onClose} disabled={submitting} className="px-4 py-2 text-sm font-black border" style={selectStyle}>取消</button><button onClick={() => void submit()} disabled={submitting || dictionaries.status !== "success"} className="px-4 py-2 text-sm font-black text-white disabled:opacity-60" style={{ background: BLUE }}>{submitting ? <><RefreshCw size={13} className="inline animate-spin mr-1" />正在上传/处理中</> : <><Upload size={13} className="inline mr-1" />确认上传</>}</button></div>
      </div>
    </Modal>
  );
}

type BatchKind = "document" | "attachment";
type BatchMetadata = DocumentCreateMetadata | AttachmentCreateMetadata;

export function BatchImportModal({ kind, onClose }: { kind: BatchKind; onClose: () => void }) {
  const references = useReferenceData();
  const dictionaries = useDictionaries();
  const [drafts, setDrafts] = useState<BatchDraft<BatchMetadata>[]>([]);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<HttpError | null>(null);
  const [summary, setSummary] = useState<{ total: number; successCount: number; failureCount: number } | null>(null);
  const controller = useRef<AbortController | null>(null);
  useEffect(() => () => controller.current?.abort(), []);

  const addFiles = (files: File[]) => {
    const valid = files.filter((file) => !validateSelectedFile(file));
    setDrafts((current) => [
      ...current,
      ...valid.map((file) => createBatchDraft(
        file,
        kind === "attachment"
          ? {
              documentName: suggestedNameFromFile(file),
              attachmentType: AttachmentType.OTHER,
              summary: null,
              tags: [],
            } satisfies AttachmentCreateMetadata
          : defaultDocumentMetadata(
              suggestedNameFromFile(file),
              references.categories.find((category) => category.isLeaf)?.id ?? "",
            ),
      )),
    ]);
    setSummary(null);
  };

  const updateMetadata = (key: string, patch: Partial<BatchMetadata>) => {
    setDrafts((current) => current.map((draft) => draft.key === key
      ? { ...draft, metadata: { ...draft.metadata, ...patch } as BatchMetadata, result: undefined }
      : draft));
  };

  const changeDocumentType = (key: string, type: "MAIN" | "SUB_PLAN") => {
    setDrafts((current) => current.map((draft) => {
      if (draft.key !== key) return draft;
      const metadata = draft.metadata as DocumentCreateMetadata;
      const common = {
        documentName: metadata.documentName,
        summary: metadata.summary,
        securityLevel: metadata.securityLevel,
        tags: metadata.tags,
      };
      return {
        ...draft,
        metadata: type === "MAIN"
          ? { ...common, documentType: "MAIN", categoryId: references.categories.find((category) => category.isLeaf)?.id ?? "" }
          : {
              ...common,
              documentType: "SUB_PLAN",
              parentDocumentId: references.mainPlans[0]?.id ?? "",
            },
        result: undefined,
      };
    }));
  };

  const submit = async () => {
    if (submitting || drafts.length === 0) return;
    setSubmitting(true);
    setError(null);
    controller.current = new AbortController();
    try {
      const result = kind === "attachment"
        ? await attachmentMutationApi.batchImport(
            drafts.map((draft) => draft.file),
            drafts.map((draft) => draft.metadata as AttachmentCreateMetadata),
            controller.current.signal,
          )
        : await documentMutationApi.batchImport(
            drafts.map((draft) => draft.file),
            drafts.map((draft) => draft.metadata as DocumentCreateMetadata),
            controller.current.signal,
          );
      setDrafts((current) => applyBatchResult(current, result));
      setSummary(result);
      if (result.successCount > 0) {
        notifyContentChanged(kind === "attachment" ? "attachments" : "documents");
      }
    } catch (value) {
      setError(asMutationError(value));
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <Modal title={kind === "attachment" ? "批量导入共享附件" : "批量导入方案文档"} onClose={onClose} wide>
      <div className="p-5 space-y-3">
        <div onDragOver={(event) => event.preventDefault()} onDrop={(event) => { event.preventDefault(); addFiles(Array.from(event.dataTransfer.files)); }}
          className="p-5 text-center border-2 border-dashed text-sm font-black" style={{ color: NAVY, borderColor: "rgba(49,145,235,.45)", background: "#EBF4FD" }}>
          拖入多个文件，或 <label className="underline cursor-pointer"><input type="file" multiple accept={ACCEPT} className="hidden" onChange={(event) => addFiles(Array.from(event.target.files ?? []))} />点击选择</label>
        </div>
        {drafts.map((draft, index) => {
          const metadata = draft.metadata;
          const documentMetadata = kind === "document" ? metadata as DocumentCreateMetadata : null;
          const attachmentMetadata = kind === "attachment" ? metadata as AttachmentCreateMetadata : null;
          return (
            <div key={draft.key} className="p-3 border grid grid-cols-12 gap-2 items-end" style={{ borderColor: "rgba(26,82,153,.18)" }}>
              <div className="col-span-2 text-xs font-black truncate" title={draft.file.name}>{index + 1}. {draft.file.name}</div>
              <div className="col-span-2"><input value={metadata.documentName} onChange={(event) => updateMetadata(draft.key, { documentName: event.target.value })} className={inputClass} style={selectStyle} /></div>
              {kind === "document" ? (
                <>
                  <div className="col-span-1"><select value={documentMetadata!.documentType} onChange={(event) => changeDocumentType(draft.key, event.target.value as "MAIN" | "SUB_PLAN")} className={inputClass} style={selectStyle}>{dictionaries.options("documentTypes").filter((option) => option.code !== "ATTACHMENT").map((option) => <option key={option.code} value={option.code}>{option.label}</option>)}</select></div>
                  <div className="col-span-2">{documentMetadata!.documentType === "MAIN" ? <select value={documentMetadata!.categoryId} onChange={(event) => updateMetadata(draft.key, { categoryId: event.target.value } as Partial<BatchMetadata>)} className={inputClass} style={selectStyle}>{references.categories.map((category) => <option key={category.id} value={category.id} disabled={!category.isLeaf} title={category.categoryPath}>{category.displayLabel}</option>)}</select> : <div className="h-9 px-2 flex items-center text-xs font-bold" style={{ background: "#EBF4FD", color: NAVY }}>分类继承自主案</div>}</div>
                  <div className="col-span-2"><select disabled={documentMetadata!.documentType === "MAIN"} value={documentMetadata!.documentType === "SUB_PLAN" ? documentMetadata!.parentDocumentId : ""} onChange={(event) => updateMetadata(draft.key, { parentDocumentId: event.target.value } as Partial<BatchMetadata>)} className={inputClass} style={selectStyle}><option value="">父主案</option>{references.mainPlans.map((plan) => <option key={plan.id} value={plan.id}>{plan.documentName}</option>)}</select></div>
                </>
              ) : (
                <div className="col-span-2"><select value={attachmentMetadata!.attachmentType} onChange={(event) => updateMetadata(draft.key, { attachmentType: event.target.value as AttachmentCreateMetadata["attachmentType"] })} className={inputClass} style={selectStyle}>{dictionaries.options("attachmentTypes").map((option) => <option key={option.code} value={option.code}>{option.label}</option>)}</select></div>
              )}
              <div className="col-span-2"><input value={metadata.tags.join(",")} onChange={(event) => updateMetadata(draft.key, { tags: parseTags(event.target.value) })} placeholder="标签" className={inputClass} style={selectStyle} /></div>
              <button disabled={submitting} onClick={() => setDrafts((current) => current.filter((item) => item.key !== draft.key))} className="col-span-1 h-9 text-xs font-black" style={{ color: NAVY }}><Trash2 size={13} className="inline" />移除</button>
              <div className="col-span-4"><input value={metadata.summary ?? ""} onChange={(event) => updateMetadata(draft.key, { summary: event.target.value || null })} placeholder="概述" className={inputClass} style={selectStyle} /></div>
              {documentMetadata && <>
                <div className="col-span-2"><select value={documentMetadata.securityLevel} onChange={(event) => updateMetadata(draft.key, { securityLevel: event.target.value as DocumentCreateMetadata["securityLevel"] } as Partial<BatchMetadata>)} className={inputClass} style={selectStyle}>{dictionaries.options("securityLevels").map((option) => <option key={option.code} value={option.code}>{option.label}</option>)}</select></div>
              </>}
              {draft.result && <div className="col-span-12 text-xs font-bold" style={{ color: draft.result.success ? BLUE : NAVY }}>{draft.result.success ? `成功：${draft.result.document.documentName}` : `失败：${draft.result.errorCode} · ${draft.result.errorMessage}`}</div>}
            </div>
          );
        })}
        {summary && <div className="text-sm font-black" style={{ color: NAVY }}>总数 {summary.total} · 成功 {summary.successCount} · 失败 {summary.failureCount}</div>}
        <ErrorBlock error={error} />
        <div className="flex justify-end gap-2">
          {summary?.failureCount ? <button onClick={() => { setDrafts((current) => retainFailedDrafts(current)); setSummary(null); }} className="px-4 py-2 text-sm font-black border" style={selectStyle}>仅保留失败项重试</button> : null}
          <button onClick={onClose} disabled={submitting} className="px-4 py-2 text-sm font-black border" style={selectStyle}>关闭</button>
          <button onClick={() => void submit()} disabled={submitting || dictionaries.status !== "success" || drafts.length === 0 || drafts.every((draft) => draft.result?.success)} className="px-4 py-2 text-sm font-black text-white disabled:opacity-50" style={{ background: BLUE }}><FilePlus2 size={13} className="inline mr-1" />{submitting ? "正在上传/处理中" : "提交当前批次"}</button>
        </div>
      </div>
    </Modal>
  );
}

export function EditMetadataModal({ item, onClose }: {
  item: DocumentSummary;
  onClose: () => void;
}) {
  const attachment = item.documentType === DocumentType.ATTACHMENT;
  const references = useReferenceData();
  const dictionaries = useDictionaries();
  const [detail, setDetail] = useState<DocumentDetail | null>(null);
  const [documentName, setDocumentName] = useState(item.documentName);
  const [summary, setSummary] = useState(item.summary ?? "");
  const [categoryId, setCategoryId] = useState(item.categoryId ?? "");
  const [securityLevel, setSecurityLevel] = useState(item.securityLevel);
  const [attachmentType, setAttachmentType] = useState(item.attachmentType ?? AttachmentType.OTHER);
  const [tags, setTags] = useState(item.tags.join(","));
  const [loading, setLoading] = useState(true);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<HttpError | null>(null);

  const loadLatest = async (preserveError = false) => {
    setLoading(true);
    if (!preserveError) setError(null);
    try {
      const latest = attachment
        ? await attachmentApi.detail(item.id)
        : await documentApi.detail(item.id);
      setDetail(latest);
      setDocumentName(latest.documentName);
      setSummary(latest.summary ?? "");
      setCategoryId(latest.categoryId ?? "");
      setSecurityLevel(latest.securityLevel);
      setAttachmentType(latest.attachmentType ?? AttachmentType.OTHER);
      setTags(latest.tags.join(","));
      cacheDetail(attachment ? "attachment" : "document", latest);
      invalidatePreview(item.id);
    } catch (value) {
      if (value instanceof HttpError) setError(value);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { void loadLatest(); }, [item.id]);

  const submit = async () => {
    if (!detail || submitting) return;
    setSubmitting(true);
    setError(null);
    try {
      const latest = attachment
        ? await attachmentMutationApi.update(item.id, {
            documentName: documentName.trim(),
            attachmentType,
            summary: summary.trim() || null,
            tags: parseTags(tags),
            rowVersion: detail.rowVersion,
          })
        : await documentMutationApi.update(item.id, item.documentType === DocumentType.MAIN ? {
            documentName: documentName.trim(),
            summary: summary.trim() || null,
            categoryId,
            securityLevel,
            tags: parseTags(tags),
            rowVersion: detail.rowVersion,
          } : {
            documentName: documentName.trim(),
            summary: summary.trim() || null,
            securityLevel,
            tags: parseTags(tags),
            rowVersion: detail.rowVersion,
          });
      cacheDetail(attachment ? "attachment" : "document", latest);
      invalidatePreview(item.id);
      notifyContentChanged(attachment ? "attachments" : "documents", ...(attachment ? ["documents" as const] : []));
      onClose();
    } catch (value) {
      const nextError = asMutationError(value);
      setError(nextError);
      if (nextError.code === "DATA_VERSION_CONFLICT") {
          invalidateDetail(attachment ? "attachment" : "document", item.id);
          notifyContentChanged(attachment ? "attachments" : "documents");
          await loadLatest(true);
      }
      if (nextError.code === "CATEGORY_NOT_LEAF") references.refreshCategories();
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <Modal title={`编辑 · ${item.documentName}`} onClose={onClose}>
      <div className="p-6 space-y-3">
        {loading && <div className="text-sm font-bold" style={{ color: NAVY }}><RefreshCw size={14} className="inline animate-spin mr-2" />正在加载最新详情…</div>}
        {detail && <>
          <Field label="名称 *"><input value={documentName} onChange={(event) => setDocumentName(event.target.value)} className={inputClass} style={selectStyle} /></Field>
          {attachment ? <Field label="附件类型"><select value={attachmentType} onChange={(event) => setAttachmentType(event.target.value as typeof attachmentType)} className={inputClass} style={selectStyle}>{dictionaries.options("attachmentTypes").map((option) => <option key={option.code} value={option.code}>{option.label}</option>)}</select></Field> : <>
            {item.documentType === DocumentType.MAIN ? <Field label="分类"><select value={categoryId} onChange={(event) => setCategoryId(event.target.value)} className={inputClass} style={selectStyle}>{references.categories.map((category) => <option key={category.id} value={category.id} disabled={!category.isLeaf} title={category.categoryPath}>{category.displayLabel}{category.isLeaf ? "" : "（请选择下级分类）"}</option>)}</select></Field> : <div className="px-3 py-2 text-xs font-bold" style={{ background: "#EBF4FD", color: NAVY }}>方案分类继承自主案：{detail.categoryPath ?? detail.categoryName ?? "未分类"}</div>}
            <div className="grid grid-cols-1 gap-3">
              <Field label="密级"><select value={securityLevel} onChange={(event) => setSecurityLevel(event.target.value as typeof securityLevel)} className={inputClass} style={selectStyle}>{dictionaries.options("securityLevels").map((option) => <option key={option.code} value={option.code}>{option.label}</option>)}</select></Field>
            </div>
          </>}
          <Field label="概述"><textarea value={summary} onChange={(event) => setSummary(event.target.value)} className="w-full min-h-20 p-3 text-sm font-bold border outline-none" style={selectStyle} /></Field>
          <Field label="标签"><input value={tags} onChange={(event) => setTags(event.target.value)} className={inputClass} style={selectStyle} /></Field>
        </>}
        <ErrorBlock error={error} />
        <div className="flex justify-end gap-2"><button onClick={onClose} disabled={submitting} className="px-4 py-2 text-sm font-black border" style={selectStyle}>取消</button><button onClick={() => void submit()} disabled={!detail || submitting || dictionaries.status !== "success"} className="px-4 py-2 text-sm font-black text-white disabled:opacity-50" style={{ background: BLUE }}><Check size={13} className="inline mr-1" />{submitting ? "提交中" : "保存元数据"}</button></div>
      </div>
    </Modal>
  );
}

export function DeleteDocumentModal({ item, onClose, onDeleted }: {
  item: DocumentSummary;
  onClose: () => void;
  onDeleted?: () => void;
}) {
  const attachment = item.documentType === DocumentType.ATTACHMENT;
  const [detail, setDetail] = useState<DocumentDetail | null>(null);
  const [loading, setLoading] = useState(true);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<HttpError | null>(null);

  const loadLatest = async () => {
    setLoading(true);
    try {
      setDetail(attachment ? await attachmentApi.detail(item.id) : await documentApi.detail(item.id));
    } catch (value) {
      if (value instanceof HttpError) setError(value);
    } finally {
      setLoading(false);
    }
  };
  useEffect(() => { void loadLatest(); }, [item.id]);

  const remove = async () => {
    if (!detail || submitting) return;
    setSubmitting(true);
    setError(null);
    try {
      if (attachment) await attachmentMutationApi.remove(item.id, detail.rowVersion);
      else await documentMutationApi.remove(item.id, detail.rowVersion);
      invalidateDetail(attachment ? "attachment" : "document", item.id);
      invalidatePreview(item.id);
      notifyContentChanged("documents", "attachments");
      onDeleted?.();
      onClose();
    } catch (value) {
      const nextError = asMutationError(value);
      setError(nextError);
      if (nextError.code === "DATA_VERSION_CONFLICT") {
          invalidateDetail(attachment ? "attachment" : "document", item.id);
          await loadLatest();
      }
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <Modal title={`删除 · ${item.documentName}`} onClose={onClose}>
      <div className="p-6 space-y-4" style={{ color: NAVY }}>
        {loading ? <div className="text-sm font-bold"><RefreshCw size={14} className="inline animate-spin mr-2" />正在加载…</div> : <div className="text-sm font-bold leading-7">确认删除该{attachment ? "共享附件" : item.documentType === "MAIN" ? "主案" : "子方案"}？</div>}
        <ErrorBlock error={error} />
        <div className="flex justify-end gap-2"><button onClick={onClose} disabled={submitting} className="px-4 py-2 text-sm font-black border" style={selectStyle}>取消</button><button onClick={() => void remove()} disabled={!detail || submitting} className="px-4 py-2 text-sm font-black text-white disabled:opacity-50" style={{ background: NAVY }}><Trash2 size={13} className="inline mr-1" />{submitting ? "删除中" : "确认删除"}</button></div>
      </div>
    </Modal>
  );
}
