/* global React */
/* ============================================================
   Interactive diagram components
   - LogicalArchitecture (vertical flowchart)
   - StandardReviewPath (sequence diagram)
   - FailureAnalysisLoop (vertical flowchart)
   Each renders an SVG with clickable nodes + a side detail panel.
   ============================================================ */

const { useState } = React;

// Shared marker definitions
const Arrows = () => (
  <defs>
    <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#b6bdcf" />
    </marker>
    <marker id="arrow-soft" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
      <path d="M 0 0 L 10 5 L 0 10 z" fill="#6a738f" />
    </marker>
  </defs>
);

// ------- Node primitive -------
function Rect({ id, x, y, w, h, label, sub, tone, selected, onClick, diamond }) {
  const cls = [
    "node",
    diamond ? "diamond" : "",
    tone ? `${tone}-tone` : "",
    selected ? "selected" : ""
  ].join(" ");
  if (diamond) {
    const cx = x + w / 2, cy = y + h / 2;
    return (
      <g className={cls} onClick={() => onClick(id)}>
        <polygon
          className="node-fill"
          points={`${cx},${y} ${x + w},${cy} ${cx},${y + h} ${x},${cy}`}
        />
        <text className="node-label" x={cx} y={cy + 4} textAnchor="middle">{label}</text>
        {sub && <text className="node-label-sub" x={cx} y={cy + 18} textAnchor="middle">{sub}</text>}
      </g>
    );
  }
  return (
    <g className={cls} onClick={() => onClick(id)}>
      <rect className="node-fill" x={x} y={y} width={w} height={h} rx="6" />
      <text className="node-label" x={x + w / 2} y={y + h / 2 + (sub ? -2 : 4)} textAnchor="middle">{label}</text>
      {sub && <text className="node-label-sub" x={x + w / 2} y={y + h / 2 + 12} textAnchor="middle">{sub}</text>}
    </g>
  );
}

// ============================================================
// LOGICAL ARCHITECTURE
// ============================================================
const ARCH_NODES = {
  intake:    { x: 290, y: 20,  w: 200, h: 44, label: "Intake: PDF / DOCX",
               role: "Entry point",
               detail: "Drag-and-drop or API ingest. Stores the raw source so we can always re-run the pipeline against a fresh extractor." },
  extract:   { x: 290, y: 100, w: 200, h: 44, label: "Extraction: Markdown",
               role: "Stage 1",
               detail: "PDF/DOCX → Markdown. Preserves clause hierarchy, definitions, dates, tables. Markdown gives us a diff-able, version-controllable representation." },
  parse:     { x: 290, y: 180, w: 200, h: 44, label: "Clause Parser & Classifier",
               role: "Stage 2",
               detail: "Splits the Markdown into legal units (liability, indemnity, governing law…) and tags each with a clause family. Downstream comparisons happen per clause, not per document." },
  similarity:{ x: 290, y: 270, w: 200, h: 70, label: "Similarity Engine", diamond: true,
               role: "Routing decision",
               detail: "Per-clause and overall similarity vs the precedent library. Three bands: >90% Standard → auto-pass path; 50–90% Hybrid → ensemble; <50% Novel → straight to human." },
  ensemble:  { x: 290, y: 380, w: 200, h: 54, label: "Ensemble Wash: 3-Model Review", tone: "amber",
               role: "Stage 3 (Hybrid)",
               detail: "Three personas — Stickler, Commercial, Adversary — each produce structured JSON. Different system prompts, same input. Designed to surface disagreement, not consensus." },
  consensus: { x: 290, y: 470, w: 200, h: 70, label: "Consensus Check", diamond: true,
               role: "Routing decision",
               detail: "Orchestrator compares the three JSON outputs. 2/3 or 3/3 agreement = pass through to rules. Any meaningful divergence = human." },
  autopass:  { x: 60,  y: 590, w: 200, h: 44, label: "Auto-Pass: Rules Engine", tone: "pass",
               role: "Deterministic gate",
               detail: "Forbidden phrases, restricted jurisdictions, liability triggers — all hard-coded rules. Deterministic on purpose: legal owns this layer, not engineering." },
  gitlab:    { x: 60,  y: 660, w: 200, h: 44, label: "GitLab CI Policy Checks",
               role: "Audit trail",
               detail: "Every rule check is a CI job. Every config change is a PR. Every result is an artifact. You get versioning, review, and an audit trail for free." },
  approved:  { x: 0,   y: 740, w: 170, h: 44, label: "Approved Repository", tone: "pass",
               role: "Pass terminal",
               detail: "Documents that survive every check. Markdown + metadata committed to a repo. The repo IS the precedent library." },
  failure:   { x: 180, y: 740, w: 170, h: 44, label: "Failure Analysis Report", tone: "fail",
               role: "Fail handler",
               detail: "When CI fails, we don't just say 'failed' — we cluster the failed clause against historical rejections and exceptions, then emit a one-pager." },
  manual:    { x: 565, y: 715, w: 200, h: 44, label: "Manual Review Queue", tone: "fail",
               role: "Human escalation",
               detail: "Catches everything the system can't or shouldn't auto-decide: novel docs, discrepancy escalations, and policy failures with no exception path." },
  cron:      { x: 0,   y: 820, w: 170, h: 54, label: "Cron: Knowledge Graph", sub: "Update & Random QA",
               role: "Feedback loop",
               detail: "After approval: precedent updated, relationships indexed (stakeholder × clause × outcome), and 5% of approvals randomly sampled for blind human QA to catch drift." },
};

const ARCH_EDGES = [
  { from: "intake", to: "extract" },
  { from: "extract", to: "parse" },
  { from: "parse", to: "similarity" },
  // similarity branches
  { from: "similarity", to: "ensemble", label: "50–90%" },
  { from: "similarity", to: "autopass", label: ">90%", path: "left" },
  { from: "similarity", to: "manual", label: "<50%", path: "right" },
  { from: "ensemble", to: "consensus" },
  { from: "consensus", to: "autopass", label: "Agreement", path: "left-lower" },
  { from: "consensus", to: "manual", label: "Discrepancy", path: "right-lower" },
  { from: "autopass", to: "gitlab" },
  { from: "gitlab", to: "approved", label: "Pass" },
  { from: "gitlab", to: "failure", label: "Fail" },
  { from: "failure", to: "manual", path: "to-manual" },
  { from: "approved", to: "cron" },
];

function archPath(from, to, kind) {
  const a = ARCH_NODES[from], b = ARCH_NODES[to];
  const ax = a.x + a.w / 2, ay = a.y + a.h;
  const bx = b.x + b.w / 2, by = b.y;
  if (kind === "left") {
    // similarity → autopass (jump down-left, bypassing ensemble/consensus)
    const startY = a.y + a.h / 2;
    return `M ${a.x} ${startY} H 40 V ${by + b.h / 2} H ${b.x}`;
  }
  if (kind === "right") {
    // similarity → manual (down-right)
    const startY = a.y + a.h / 2;
    return `M ${a.x + a.w} ${startY} H 730 V ${by + b.h / 2} H ${b.x + b.w}`;
  }
  if (kind === "left-lower") {
    const startY = a.y + a.h / 2;
    return `M ${a.x} ${startY} H 200 V ${by} H ${b.x + b.w}`;
  }
  if (kind === "right-lower") {
    const startY = a.y + a.h / 2;
    return `M ${a.x + a.w} ${startY} H 665 V ${by + b.h / 2} H ${b.x + b.w}`;
  }
  if (kind === "to-manual") {
    return `M ${a.x + a.w} ${ay - a.h / 2} H 500 C 600 ${ay - a.h / 2}, 660 ${by + b.h / 2}, ${b.x} ${by + b.h / 2}`;
  }
  return `M ${ax} ${ay} L ${bx} ${by}`;
}

function archLabelPos(from, to, kind) {
  const a = ARCH_NODES[from], b = ARCH_NODES[to];
  if (kind === "left") return { x: 50, y: a.y + a.h / 2 + 16 };
  if (kind === "right") return { x: 720, y: a.y + a.h / 2 + 16 };
  if (kind === "left-lower") return { x: 210, y: a.y + a.h - 6 };
  if (kind === "right-lower") return { x: 660, y: a.y + a.h - 6 };
  const ax = a.x + a.w / 2;
  const ay = a.y + a.h;
  const by = b.y;
  return { x: ax + 8, y: (ay + by) / 2 + 3 };
}

function LogicalArchitecture() {
  const [sel, setSel] = useState("similarity");
  const node = ARCH_NODES[sel];

  return (
    <div className="diagram-wrap">
      <div className="diagram-canvas">
        <svg viewBox="0 0 780 900" preserveAspectRatio="xMidYMin meet">
          <Arrows />
          {/* edges */}
          {ARCH_EDGES.map((e, i) => {
            const p = archPath(e.from, e.to, e.path);
            const lp = archLabelPos(e.from, e.to, e.path);
            return (
              <g key={i}>
                <path className="edge" d={p} />
                {e.label && <text className="edge-label" x={lp.x} y={lp.y}>{e.label}</text>}
              </g>
            );
          })}
          {/* nodes */}
          {Object.entries(ARCH_NODES).map(([id, n]) => (
            <Rect
              key={id}
              id={id}
              x={n.x} y={n.y} w={n.w} h={n.h}
              label={n.label}
              sub={n.sub}
              diamond={n.diamond}
              tone={n.tone}
              selected={sel === id}
              onClick={setSel}
            />
          ))}
        </svg>
        <div className="legend">
          <span className="legend-item"><span className="legend-chip"></span>Process</span>
          <span className="legend-item"><span className="legend-chip" style={{ background: "#e8ebf4", transform: "rotate(45deg)" }}></span>Decision</span>
          <span className="legend-item"><span className="legend-chip" style={{ background: "#e3f5ec", borderColor: "rgba(47,158,108,0.4)" }}></span>Pass path</span>
          <span className="legend-item"><span className="legend-chip" style={{ background: "#fbeae8", borderColor: "rgba(217,74,61,0.4)" }}></span>Fail / escalate</span>
        </div>
      </div>
      <DetailPanel node={node} />
    </div>
  );
}

// ============================================================
// STANDARD REVIEW PATH — sequence diagram
// ============================================================
const LANES = [
  { id: "user", title: "User / Legal", x: 50 },
  { id: "intake", title: "Intake & Extraction", x: 200 },
  { id: "sim", title: "Similarity Engine", x: 350 },
  { id: "ens", title: "Ensemble (3 Models)", x: 510 },
  { id: "gitlab", title: "GitLab CI / Rules", x: 670 },
  { id: "repo", title: "Repo / Database", x: 830 },
];

const STEPS = {
  upload: { from: "user", to: "intake", y: 100, label: "Upload contract",
            role: "Step 1", detail: "Source file lands. We hash it, store it, and the pipeline starts. Original is immutable." },
  process: { from: "intake", to: "sim", y: 145, label: "Process Markdown & clauses",
             role: "Step 2", detail: "Markdown extraction + clause segmentation hand off to similarity scoring." },
  compare: { from: "sim", to: "sim", y: 190, label: "Compare vs precedent library", self: true,
             role: "Step 3", detail: "Per-clause embedding similarity against the accepted + rejected precedent corpus." },
  policyDirect: { from: "sim", to: "gitlab", y: 270, label: "Send to policy checks", branch: "standard",
                  role: "Branch: Standard (>90%)", detail: "If overall similarity is high and no novel clauses, skip the ensemble entirely. Go straight to deterministic checks." },
  triggerEns: { from: "sim", to: "ens", y: 345, label: "Trigger ensemble wash", branch: "deviated",
                role: "Branch: Deviated (50–90%)", detail: "Hybrid documents get the three-model treatment." },
  models: { from: "ens", to: "ens", y: 390, label: "GPT-4o + Claude + Gemini", self: true,
            role: "Step 5", detail: "Three different models, three different system prompts (Stickler, Commercial, Adversary), parallel calls." },
  consolidate: { from: "ens", to: "gitlab", y: 435, label: "Consolidated JSON results",
                 role: "Step 6", detail: "Orchestrator merges the three JSON outputs, flags agreement vs disagreement, and hands the package to the rules engine." },
  rules: { from: "gitlab", to: "gitlab", y: 495, label: "Run forbidden-phrase & jurisdiction tests", self: true,
           role: "Step 7", detail: "Deterministic checks: hard-fail lists for jurisdictions, phrases, country restrictions, liability triggers." },
  commit: { from: "gitlab", to: "repo", y: 580, label: "Commit to approved repo", branch: "pass",
            role: "Branch: All pass", detail: "Contract becomes precedent. Knowledge graph updates run on a cron later." },
  notify: { from: "gitlab", to: "user", y: 625, label: "Notify auto-approved",
            role: "Step 8a", detail: "Auto-pass notification with the matching precedent IDs and similarity score. Lawyer gets a receipt, not a task." },
  manual: { from: "gitlab", to: "user", y: 700, label: "Notify manual review with 1-pager", branch: "fail",
            role: "Branch: Test fails", detail: "Manual queue gets the contract plus a generated one-pager: failed clauses, closest historical rejections, closest accepted exceptions." },
};

function StandardReviewPath() {
  const [sel, setSel] = useState("compare");
  const node = STEPS[sel];

  const laneX = id => LANES.find(l => l.id === id).x;
  const TOP = 50;
  const BOTTOM = 760;

  return (
    <div className="diagram-wrap">
      <div className="diagram-canvas">
        <svg viewBox="0 0 900 800" preserveAspectRatio="xMidYMin meet">
          <Arrows />
          {/* Lane headers + lifelines */}
          {LANES.map(l => (
            <g key={l.id}>
              <rect className="lane-bg" x={l.x - 70} y={TOP - 35} width="140" height="28" rx="4" />
              <text className="lane-title" x={l.x} y={TOP - 16} textAnchor="middle">{l.title}</text>
              <line className="lifeline" x1={l.x} y1={TOP} x2={l.x} y2={BOTTOM} />
              {/* Footer lane */}
              <rect className="lane-bg" x={l.x - 70} y={BOTTOM + 8} width="140" height="28" rx="4" />
              <text className="lane-title" x={l.x} y={BOTTOM + 26} textAnchor="middle">{l.title}</text>
            </g>
          ))}

          {/* Standard / Deviated frame */}
          <rect className="frame" x="35" y="240" width="845" height="220" />
          <rect x="35" y="240" width="42" height="18" fill="#fff0eb" stroke="#ffd4c4" />
          <text className="frame-tag" x="44" y="253">alt</text>
          <text className="msg-label" x="455" y="253" textAnchor="middle" style={{ fill: "#6a738f", fontSize: 10.5 }}>[Standard] / [Deviated]</text>

          {/* Pass / Fail outer frame */}
          <rect className="frame" x="35" y="555" width="845" height="170" />
          <rect x="35" y="555" width="42" height="18" fill="#fff0eb" stroke="#ffd4c4" />
          <text className="frame-tag" x="44" y="568">alt</text>
          <text className="msg-label" x="455" y="568" textAnchor="middle" style={{ fill: "#6a738f", fontSize: 10.5 }}>[All tests pass] / [Test fails]</text>

          {/* Steps */}
          {Object.entries(STEPS).map(([id, s]) => {
            const x1 = laneX(s.from), x2 = laneX(s.to);
            const isSel = sel === id;
            const stroke = isSel ? "#ff6633" : "#6a738f";
            const labelFill = isSel ? "#ff6633" : "#1a2348";
            if (s.self) {
              const cx = x1;
              return (
                <g key={id} onClick={() => setSel(id)} style={{ cursor: "pointer" }}>
                  <path d={`M ${cx} ${s.y} C ${cx + 40} ${s.y - 10}, ${cx + 40} ${s.y + 18}, ${cx + 6} ${s.y + 18}`}
                        fill="none" stroke={stroke} strokeWidth="1.4" markerEnd="url(#arrow-soft)" />
                  <text className="msg-label" x={cx + 50} y={s.y + 4} style={{ fill: labelFill }}>{s.label}</text>
                  {isSel && <rect x={cx - 6} y={s.y - 18} width="280" height="40" rx="4" fill="rgba(255,102,51,0.08)" stroke="#ffd4c4" />}
                </g>
              );
            }
            const goesRight = x2 > x1;
            return (
              <g key={id} onClick={() => setSel(id)} style={{ cursor: "pointer" }}>
                <line x1={x1} y1={s.y} x2={x2} y2={s.y} stroke={stroke} strokeWidth="1.4" markerEnd="url(#arrow-soft)" />
                <text className="msg-label" x={(x1 + x2) / 2} y={s.y - 5} textAnchor="middle" style={{ fill: labelFill }}>{s.label}</text>
                {isSel && <rect x={Math.min(x1, x2) + 4} y={s.y - 18} width={Math.abs(x2 - x1) - 8} height="22" rx="3" fill="rgba(255,102,51,0.08)" />}
              </g>
            );
          })}
        </svg>
      </div>
      <DetailPanel node={node} />
    </div>
  );
}

// ============================================================
// FAILURE ANALYSIS LOOP
// ============================================================
const FAIL_NODES = {
  fail:       { x: 150, y: 20,  w: 220, h: 44, label: "GitLab CI Failure", tone: "fail",
                role: "Trigger", detail: "A deterministic policy check has failed. We don't stop — we ask: is this a 'genuinely bad' fail or a 'rare-but-acceptable' fail?" },
  prompt:     { x: 150, y: 100, w: 220, h: 44, label: "Failure Analysis Prompt",
                role: "Stage 1", detail: "Structured prompt: failed clauses, their context, and the rules they broke. Output JSON only." },
  rejections: { x: 150, y: 180, w: 220, h: 44, label: "Compare to Historical Rejections",
                role: "Stage 2", detail: "Semantic comparison against the rejected-precedent corpus. 'How close is this clause to things we've already said no to?'" },
  exceptions: { x: 150, y: 260, w: 220, h: 44, label: "Compare to Accepted Exceptions",
                role: "Stage 3", detail: "Same comparison, but against exceptions that were ultimately approved despite breaking a default rule." },
  router:     { x: 150, y: 350, w: 220, h: 70, label: "Closer to Accepted or Failed?", diamond: true,
                role: "Decision", detail: "Distance to nearest rejected vs distance to nearest accepted exception. The shorter distance wins." },
  exception:  { x: 0,   y: 470, w: 220, h: 50, label: "Suggest Exception Review", tone: "pass",
                role: "Accepted-like", detail: "Route to a focused legal review with the matching exception precedent attached. 'Approve this the way you approved that.'" },
  rejection:  { x: 300, y: 470, w: 220, h: 50, label: "Route to Manual Rejection Review", tone: "fail",
                role: "Failed-like", detail: "Manual review with a 'recommended reject' framing — closest rejected precedent shown side-by-side." },
  report:     { x: 150, y: 565, w: 220, h: 44, label: "Generate Failure Report",
                role: "Synthesis", detail: "One-pager: failed rules, similarity neighbours, suggested action, audit chain." },
  human:      { x: 150, y: 645, w: 220, h: 44, label: "Human Legal Review", tone: "amber",
                role: "Terminal", detail: "Lawyer makes the call. The decision (and reasoning) feeds back into the knowledge graph so the next pass is sharper." },
};

const FAIL_EDGES = [
  { from: "fail", to: "prompt" },
  { from: "prompt", to: "rejections" },
  { from: "rejections", to: "exceptions" },
  { from: "exceptions", to: "router" },
  { from: "router", to: "exception", label: "Accepted-like", kind: "left-down" },
  { from: "router", to: "rejection", label: "Failed-like", kind: "right-down" },
  { from: "exception", to: "report", kind: "left-up" },
  { from: "rejection", to: "report", kind: "right-up" },
  { from: "report", to: "human" },
];

function failPath(from, to, kind) {
  const a = FAIL_NODES[from], b = FAIL_NODES[to];
  if (kind === "left-down") {
    return `M ${a.x + 20} ${a.y + a.h} V ${b.y + b.h / 2} H ${b.x + b.w}`;
  }
  if (kind === "right-down") {
    return `M ${a.x + a.w - 20} ${a.y + a.h} V ${b.y + b.h / 2} H ${b.x}`;
  }
  if (kind === "left-up") {
    return `M ${a.x + a.w} ${a.y + a.h / 2} H ${b.x - 10} V ${b.y}`;
  }
  if (kind === "right-up") {
    return `M ${a.x} ${a.y + a.h / 2} H ${b.x + b.w + 10} V ${b.y}`;
  }
  return `M ${a.x + a.w / 2} ${a.y + a.h} L ${b.x + b.w / 2} ${b.y}`;
}

function FailureAnalysisLoop() {
  const [sel, setSel] = useState("router");
  const node = FAIL_NODES[sel];

  return (
    <div className="diagram-wrap">
      <div className="diagram-canvas">
        <svg viewBox="0 0 540 720" preserveAspectRatio="xMidYMin meet">
          <Arrows />
          {FAIL_EDGES.map((e, i) => {
            const p = failPath(e.from, e.to, e.kind);
            // label midpoint heuristics
            let lx = 270, ly = 440;
            if (e.kind === "left-down") { lx = 130; ly = FAIL_NODES[e.from].y + FAIL_NODES[e.from].h + 30; }
            if (e.kind === "right-down") { lx = 380; ly = FAIL_NODES[e.from].y + FAIL_NODES[e.from].h + 30; }
            return (
              <g key={i}>
                <path className="edge" d={p} />
                {e.label && <text className="edge-label" x={lx} y={ly} textAnchor="middle">{e.label}</text>}
              </g>
            );
          })}
          {Object.entries(FAIL_NODES).map(([id, n]) => (
            <Rect
              key={id}
              id={id}
              x={n.x} y={n.y} w={n.w} h={n.h}
              label={n.label}
              sub={n.sub}
              diamond={n.diamond}
              tone={n.tone}
              selected={sel === id}
              onClick={setSel}
            />
          ))}
        </svg>
      </div>
      <DetailPanel node={node} />
    </div>
  );
}

// ============================================================
// Shared detail panel
// ============================================================
function DetailPanel({ node }) {
  if (!node) {
    return (
      <div className="diagram-detail">
        <p className="empty">Click any node to see what it does.</p>
      </div>
    );
  }
  return (
    <div className="diagram-detail">
      <div className="role">{node.role}</div>
      <h4>{node.label}</h4>
      <p>{node.detail}</p>
    </div>
  );
}

window.Diagrams = { LogicalArchitecture, StandardReviewPath, FailureAnalysisLoop };
