import { readFileSync } from "node:fs";

type JsonRecord = Record<string, unknown>;
type DocTypeCode = "DCSN" | "GOAL" | "PLAN" | "RPRT" | "ACTN" | "ISSU" | "RETR" | "RQST" | "RESP" | "MINS";

interface DocMetadata {
  docNo: string;
  docType: DocTypeCode | "";
  priority: string;
  year: string;
  sequence: string;
}

const docTypeLabels: Record<DocTypeCode, string> = {
  DCSN: "决策/决议",
  GOAL: "目标",
  PLAN: "计划",
  RPRT: "报告",
  ACTN: "行动",
  ISSU: "问题",
  RETR: "复盘",
  RQST: "请示",
  RESP: "批复/答复",
  MINS: "会议纪要",
};

const docTypeOrder: DocTypeCode[] = ["DCSN", "GOAL", "PLAN", "RPRT", "ACTN", "ISSU", "RETR", "RQST", "RESP", "MINS"];
const docTypeSet = new Set<string>(docTypeOrder);
const docNoPattern = /\bDC[-−–—]([A-Z]{2,5})[-−–—]([A-Z][0-9])[-−–—](\d{4})[-−–—](\d{1,6})\b/iu;

function assertCondition(condition: unknown, message: string, detail: JsonRecord = {}): void {
  if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}

function source(path: string): string {
  return readFileSync(path, "utf8");
}

function includesAll(text: string, snippets: string[]): boolean {
  return snippets.every((snippet) => text.includes(snippet));
}

function assertEqual<T>(actual: T, expected: T, message: string, detail: JsonRecord = {}): void {
  assertCondition(Object.is(actual, expected), message, { ...detail, actual, expected });
}

function stringField(record: JsonRecord, keys: string[]): string {
  for (const key of keys) {
    const value = record[key];
    if (typeof value === "string" && value.trim()) return value.trim();
    if (typeof value === "number" && Number.isFinite(value)) return String(value);
  }
  return "";
}

function recordTags(record: JsonRecord): string[] {
  const tags = record.tags;
  return Array.isArray(tags) ? tags.map((tag) => String(tag)) : [];
}

function tagValues(record: JsonRecord, prefix: string): string[] {
  const normalized = `${prefix.toLowerCase()}:`;
  return recordTags(record)
    .map((tag) => tag.trim())
    .filter((tag) => tag.toLowerCase().startsWith(normalized))
    .map((tag) => tag.slice(normalized.length).trim())
    .filter(Boolean);
}

function normalizeDocType(value: unknown): DocTypeCode | "" {
  const upper = String(value || "").trim().toUpperCase();
  return docTypeSet.has(upper) ? upper as DocTypeCode : "";
}

function normalizeSequence(value: unknown): string {
  const raw = String(value || "").trim();
  if (!/^\d+$/u.test(raw)) return "";
  return String(Number(raw)).padStart(3, "0");
}

function parseDocNo(value: unknown): DocMetadata | null {
  const match = String(value || "").match(docNoPattern);
  if (match === null) return null;
  const docType = normalizeDocType(match[1]);
  const priority = String(match[2] || "").toUpperCase();
  const year = String(match[3] || "");
  const sequence = normalizeSequence(match[4]);
  if (!docType || !/^P[0-3]$/u.test(priority) || !year || !sequence) return null;
  return {
    docNo: `DC-${docType}-${priority}-${year}-${sequence}`,
    docType,
    priority,
    year,
    sequence,
  };
}

function mergeParsed(metadata: DocMetadata, parsed: DocMetadata | null): void {
  if (parsed === null) return;
  if (!metadata.docNo) metadata.docNo = parsed.docNo;
  if (!metadata.docType) metadata.docType = parsed.docType;
  if (!metadata.priority) metadata.priority = parsed.priority;
  if (!metadata.year) metadata.year = parsed.year;
  if (!metadata.sequence) metadata.sequence = parsed.sequence;
}

function completeDocNo(metadata: DocMetadata): string {
  return metadata.docType && metadata.priority && metadata.year && metadata.sequence
    ? `DC-${metadata.docType}-${metadata.priority}-${metadata.year}-${metadata.sequence}`
    : "";
}

function bodyFirstWindow(record: JsonRecord): string {
  const body = stringField(record, ["body", "summary", "markdown"]);
  return body.replace(/\r\n?/gu, "\n").split(/\n\s*\n/gu).map((part) => part.trim()).find(Boolean) || "";
}

function extractDocMetadata(record: JsonRecord): DocMetadata {
  const metadata: DocMetadata = { docNo: "", docType: "", priority: "", year: "", sequence: "" };
  mergeParsed(metadata, parseDocNo(stringField(record, ["docNo", "documentNo", "documentNumber", "documentId"])));
  metadata.docType ||= normalizeDocType(stringField(record, ["docType", "documentType", "documentKind"]));
  metadata.priority ||= stringField(record, ["docPriority", "priority", "level"]).toUpperCase();
  metadata.year ||= stringField(record, ["docYear", "year"]);
  metadata.sequence ||= normalizeSequence(stringField(record, ["docSeq", "sequence", "seq", "docSequence", "documentSequence"]));
  metadata.docNo ||= completeDocNo(metadata);
  mergeParsed(metadata, parseDocNo(stringField(record, ["title"])));
  for (const value of tagValues(record, "doc-no")) mergeParsed(metadata, parseDocNo(value));
  metadata.docType ||= normalizeDocType(tagValues(record, "doc-type")[0]);
  metadata.priority ||= String(tagValues(record, "doc-priority")[0] || "").toUpperCase();
  metadata.year ||= tagValues(record, "doc-year")[0] || "";
  metadata.sequence ||= normalizeSequence(tagValues(record, "doc-sequence")[0]);
  metadata.docNo ||= completeDocNo(metadata);
  mergeParsed(metadata, parseDocNo(bodyFirstWindow(record)));
  metadata.docNo ||= completeDocNo(metadata);
  return metadata;
}

function extractDocNo(record: JsonRecord): string {
  return extractDocMetadata(record).docNo;
}

function groupCount(records: JsonRecord[], type: string): number {
  const groups = new Map<DocTypeCode, JsonRecord[]>();
  for (const code of docTypeOrder) groups.set(code, []);
  for (const record of records) {
    const code = extractDocMetadata(record).docType || "DCSN";
    groups.get(code)?.push(record);
  }
  return groups.get(type as DocTypeCode)?.length || 0;
}

export function runDecisionCenterWorkspaceContract(): JsonRecord {
  const frontend = source("src/components/frontend/src/decision-center.tsx");
  const css = source("src/components/frontend/public/style.css");

  const titleDcsn = {
    id: "title-dcsn",
    title: "DC-DCSN-P0-2026-001 决策记录",
    tags: [],
  };
  const tagGoal = {
    id: "tag-goal",
    title: "目标文书",
    tags: ["doc-no:DC-GOAL-P0-2026-002"],
  };
  const bodyRprt = {
    id: "body-rprt",
    title: "报告正文",
    body: "DC-RPRT-P2-2026-003\n\n报告正文第一段。",
    tags: [],
  };
  const tagRetr = {
    id: "tag-retr",
    title: "复盘文书",
    tags: ["doc-type:RETR"],
  };
  const structuredPlan = {
    id: "structured-plan",
    title: "结构化计划文书",
    docType: "PLAN",
    priority: "P1",
    year: 2026,
    sequence: 4,
  };

  assertEqual(extractDocNo(titleDcsn), "DC-DCSN-P0-2026-001", "title prefix must extract complete DCSN doc number");
  assertEqual(extractDocMetadata(titleDcsn).docType, "DCSN", "title prefix must extract DCSN doc type");
  assertEqual(extractDocNo(tagGoal), "DC-GOAL-P0-2026-002", "doc-no tag must extract complete GOAL doc number");
  assertEqual(extractDocMetadata(tagGoal).docType, "GOAL", "doc-no tag must extract GOAL doc type");
  assertEqual(extractDocNo(bodyRprt), "DC-RPRT-P2-2026-003", "body first paragraph must fallback extract complete RPRT doc number");
  assertEqual(extractDocMetadata(bodyRprt).priority, "P2", "body fallback must extract RPRT priority");
  assertEqual(extractDocMetadata(tagRetr).docType, "RETR", "doc-type tag must extract RETR doc type");
  assertEqual(docTypeLabels.RETR, "复盘", "RETR label must be 复盘");
  assertEqual(extractDocNo(structuredPlan), "DC-PLAN-P1-2026-004", "structured fields must compose complete PLAN doc number");
  assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "DCSN"), 1, "DCSN records must be grouped by parsed type");
  assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "GOAL"), 1, "GOAL records must be grouped by parsed type");
  assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "RPRT"), 1, "RPRT records must be grouped by parsed type");
  assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "RETR"), 1, "RETR records must be grouped by parsed type");
  assertEqual(groupCount([titleDcsn, tagGoal, bodyRprt, tagRetr, structuredPlan], "PLAN"), 1, "PLAN records must be grouped by structured type");

  assertCondition(
    includesAll(frontend, [
      "export function extractDocMetadata(record: any): DocMetadata",
      "export function extractDocNo(record: any): string",
      "export function buildDocTypeTree(records: any[]): Array<{ type: DocTypeCode",
      "function buildTagGroups(records: any[]): Array<{ tag: string",
      "DOC_TYPE_CODES = [\"DCSN\", \"GOAL\", \"PLAN\", \"RPRT\", \"ACTN\", \"ISSU\", \"RETR\", \"RQST\", \"RESP\", \"MINS\"]",
      "docTypeLabels: Record<DocTypeCode, string>",
    ]),
    "frontend must implement exported doc metadata extraction, doc-type tree, and tag grouping",
  );

  assertCondition(
    includesAll(frontend, [
      "function DocTreeNode({ record, depth, onSelect, selectedId }: AnyRecord)",
      "function DocTreeGroup({ group, onSelect, selectedId, defaultOpen }: AnyRecord)",
      "function TagTreeGroup({ group, onSelect, selectedId, defaultOpen }: AnyRecord)",
      "function DocTypeTree({ records, onSelect, selectedId, activeGrouping }: AnyRecord)",
    ]),
    "frontend must implement DocTreeNode, DocTreeGroup, TagTreeGroup, and DocTypeTree components",
  );

  assertCondition(
    includesAll(frontend, [
      "function DocDetailSidebar({ record, onSelect, onSave, saving, error, saveMsg, onRaw }: AnyRecord)",
      "function DocEditor({ form, saving, onChange, onSubmit }: AnyRecord)",
      "function DocViewer({ record }: AnyRecord)",
      "function DocDetailHeader({ record, onBack }: AnyRecord)",
    ]),
    "frontend must implement DocDetailSidebar, DocEditor, DocViewer, and DocDetailHeader",
  );

  assertCondition(
    includesAll(frontend, [
      "function DocWorkspace({ records, selectedRecord, onSelect, onSave, saving, saveError, saveMsg, onRaw }: AnyRecord)",
      "h(DocWorkspace, {",
      "activeView === \"workspace\"",
      "decision-tab-workspace",
    ]),
    "frontend must implement DocWorkspace component and workspace tab",
  );

  assertCondition(
    includesAll(frontend, [
      "mode === \"view\"",
      "mode === \"edit\"",
      "setMode(\"view\")",
      "setMode(\"edit\")",
      "doc-sidebar-mode-tabs",
    ]),
    "frontend must implement read/edit mode toggle in DocDetailSidebar",
  );

  assertCondition(
    includesAll(frontend, [
      "selectedRecord?.id",
      "setState((prev: any) => ({ ...prev, selectedDoc: record",
      "state.selectedDoc",
    ]),
    "frontend must track selectedDoc in state and sync on selection",
  );

  assertCondition(
    includesAll(frontend, [
      "await onSave(form)",
      "recordPayloadFromForm(form)",
      "setRecordSaveState({ saving: false, error: \"\", message:",
      "setState((prev: any) => ({ ...prev, selectedDoc: saved",
    ]),
    "frontend must call onSave, payload conversion, error handling, and sync selectedDoc after save",
  );

  assertCondition(
    css.includes(".doc-workspace"),
    "CSS must include .doc-workspace layout",
  );

  assertCondition(
    css.includes(".doc-sidebar"),
    "CSS must include .doc-sidebar",
  );

  assertCondition(
    css.includes("grid-template-columns: minmax(200px, 260px) minmax(0, 1fr) minmax(0, 50%)"),
    "CSS must implement three-column layout with right sidebar at 50%",
  );

  assertCondition(
    includesAll(css, [
      ".doc-type-tree",
      ".doc-tree-group",
      ".doc-tree-node",
      ".doc-list-item",
      ".doc-full-markdown",
      ".doc-editor-form",
    ]),
    "CSS must include all workspace component styles",
  );

  assertCondition(
    includesAll(frontend, [
      "extractDocMetadata(record)",
      "recordTagValues(record, \"doc-no\")",
      "firstBodyWindow(record)",
    ]),
    "extractDocNo must use metadata extraction from title, doc-no tag, and body fallback",
  );

  assertCondition(
    includesAll(frontend, [
      "grouping === \"type\"",
      "grouping === \"tag\"",
      "setGrouping(\"type\")",
      "setGrouping(\"tag\")",
    ]),
    "workspace must support type and tag grouping toggle",
  );

  return {
    ok: true,
    checks: [
      "doc-no-extraction-functions",
      "doc-type-tree-components",
      "doc-detail-sidebar-components",
      "doc-workspace-component",
      "read-edit-mode-toggle",
      "selected-doc-state-sync",
      "save-and-sync",
      "workspace-css-layout",
      "workspace-css-components",
      "three-column-layout-50-percent",
      "doc-metadata-fixtures",
      "type-tag-grouping-toggle",
    ],
  };
}

if (import.meta.main) {
  process.stdout.write(`${JSON.stringify(runDecisionCenterWorkspaceContract(), null, 2)}\n`);
}
