/* global window React */
const { useState, useRef, useEffect } = React;
const { I, TREE_NODES, DEMO_PATHS } = window;

// ---------------------------------------------------------------------------
// Question text fallback for any node not provided by window.TREE_NODES.
// The six checks (number / distribution × Individual / Group / Subgroup) repeat
// across tiers because every tier represents a different failure history of
// the same underlying cascade.
// ---------------------------------------------------------------------------
const QUESTION_TEXT = {
  N0:  "Is the assessed crop Fresh Sweet Corn?",
  // Tier 1 — full cascade
  N1:  "Does the number of successful trials qualify as a USEPA Individual crop?",
  N2:  "Does the distribution of successful trials qualify as a USEPA Individual crop?",
  N3:  "Does the number of successful trials qualify as a USEPA Crop Group?",
  N4:  "Does the distribution of successful trials qualify as a USEPA Crop Group?",
  N5:  "Does the number of successful trials qualify as a USEPA Crop Subgroup?",
  N6:  "Does the distribution of successful trials qualify as a USEPA Crop Subgroup?",
  // Tier 2 — Subgroup-only fallback after Tier 1 Group failure
  N7:  "Does the number of successful trials qualify as a USEPA Crop Subgroup?",
  N8:  "Does the distribution of successful trials qualify as a USEPA Crop Subgroup?",
  // Tier 3 — Group → Subgroup cascade after Individual failed
  N9:  "Does the number of successful trials qualify as a USEPA Crop Group?",
  N10: "Does the distribution of successful trials qualify as a USEPA Crop Group?",
  N11: "Does the number of successful trials qualify as a USEPA Crop Subgroup?",
  N12: "Does the distribution of successful trials qualify as a USEPA Crop Subgroup?",
  // Tier 4 — Subgroup-only fallback after Tier 3 Group failure
  N13: "Does the number of successful trials qualify as a USEPA Crop Subgroup?",
  N14: "Does the distribution of successful trials qualify as a USEPA Crop Subgroup?",
};

// ---------------------------------------------------------------------------
// Geometry constants
// Decision nodes are rounded rectangles sized to hold up to 4 lines of
// wrapped question text (22 chars per line). Terminals stay as circles.
// ---------------------------------------------------------------------------
const DECISION_HW = 75;   // rectangle half-width
const DECISION_HH = 36;   // rectangle half-height
const DECISION_RX = 10;   // corner radius
const TERMINAL_R  = 22;   // PASS / FAIL circle radius
const WRAP_CHARS  = 22;
const LINE_HEIGHT = 11;

// ---------------------------------------------------------------------------
// Layout
//   Columns 0..7 left-to-right; rows 1..4 top-to-bottom.
//   Same-level checks across tiers share columns (e.g. all "num Subgroup"
//   rectangles at col 5) so fallback paths read vertically.
// ---------------------------------------------------------------------------
const NODE_POS = {
  // Entry filter
  N0:     { x: 110,  y: 200 },

  // Tier 1: full Individual → Group → Subgroup chain
  N1:     { x: 290,  y: 200 },   // num Individual (orange entry)
  N2:     { x: 470,  y: 200 },   // dist Individual
  N3:     { x: 650,  y: 200 },   // num Group
  N4:     { x: 830,  y: 200 },   // dist Group
  N5:     { x: 1010, y: 200 },   // num Subgroup
  N6:     { x: 1190, y: 200 },   // dist Subgroup
  PASS_1: { x: 1340, y: 200 },

  // Tier 2: Subgroup-only fallback (Tier 1 Group failed)
  N7:     { x: 1010, y: 400 },
  N8:     { x: 1190, y: 400 },
  PASS_2: { x: 1340, y: 400 },

  // Tier 3: Group → Subgroup cascade (Tier 1 Individual failed)
  N9:     { x: 650,  y: 600 },
  N10:    { x: 830,  y: 600 },
  N11:    { x: 1010, y: 600 },
  N12:    { x: 1190, y: 600 },
  PASS_3: { x: 1340, y: 600 },

  // Tier 4: Subgroup-only fallback (Tier 3 Group failed)
  N13:    { x: 1010, y: 800 },
  N14:    { x: 1190, y: 800 },
  PASS_4: { x: 1340, y: 800 },

  // FAIL terminals — only at the Subgroup level, where no broader fallback exists.
  // Sit between tiers, in the same column as their source rectangle.
  FAIL_5:  { x: 1010, y: 300 },
  FAIL_6:  { x: 1190, y: 300 },
  FAIL_7:  { x: 1010, y: 500 },
  FAIL_8:  { x: 1190, y: 500 },
  FAIL_11: { x: 1010, y: 700 },
  FAIL_12: { x: 1190, y: 700 },
  FAIL_13: { x: 1010, y: 900 },
  FAIL_14: { x: 1190, y: 900 },

};

// ---------------------------------------------------------------------------
// Edges
//   • yes-chain edges step rightward within a tier
//   • drop edges fall to the next-broader fallback when a non-Subgroup level fails
//   • terminal-NO edges point to a FAIL only at the Subgroup level
// ---------------------------------------------------------------------------
const ALL_EDGES = [
  // Entry filter
  ["N0", "N1",   "yes"],

  // ---- Tier 1 ----
  ["N1", "N2",     "yes"],
  ["N2", "N3",     "yes"],
  ["N3", "N4",     "yes"],
  ["N4", "N5",     "yes"],
  ["N5", "N6",     "yes"],
  ["N6", "PASS_1", "yes"],
  // Individual fails → drop to Tier 3 (start Group cascade)
  ["N1", "N9",     "no"],
  ["N2", "N9",     "no"],
  // Group fails → drop to Tier 2 (start Subgroup-only)
  ["N3", "N7",     "no"],
  ["N4", "N7",     "no"],
  // Subgroup fails → terminal FAIL
  ["N5", "FAIL_5", "no"],
  ["N6", "FAIL_6", "no"],

  // ---- Tier 2 ----
  ["N7", "N8",     "yes"],
  ["N8", "PASS_2", "yes"],
  ["N7", "FAIL_7", "no"],
  ["N8", "FAIL_8", "no"],

  // ---- Tier 3 ----
  ["N9",  "N10",    "yes"],
  ["N10", "N11",    "yes"],
  ["N11", "N12",    "yes"],
  ["N12", "PASS_3", "yes"],
  // Group fails → drop to Tier 4 (Subgroup-only)
  ["N9",  "N13",    "no"],
  ["N10", "N13",    "no"],
  // Subgroup fails → terminal FAIL
  ["N11", "FAIL_11", "no"],
  ["N12", "FAIL_12", "no"],

  // ---- Tier 4 ----
  ["N13", "N14",     "yes"],
  ["N14", "PASS_4",  "yes"],
  ["N13", "FAIL_13", "no"],
  ["N14", "FAIL_14", "no"],
];

const TIER_LABELS = [
  //{ y: 200, text: "TIER 1 — FULL CASCADE: INDIVIDUAL → GROUP → SUBGROUP" },
  //{ y: 400, text: "TIER 2 — SUBGROUP FALLBACK (Tier 1 group failed)" },
  //{ y: 600, text: "TIER 3 — GROUP CASCADE (Tier 1 individual failed)" },
  //{ y: 800, text: "TIER 4 — SUBGROUP FALLBACK (Tier 3 group failed)" },
];

// viewBox is tightened to actual content extent (no centered/letterboxed dead space).
// Content x-range ≈ 30 → 1365 (LOOP arrow swings out to ~10); y-range ≈ 145 → 940.
const VIEW_X = 0;
const VIEW_Y = 130;
const VIEW_W = 1380;
const VIEW_H = 820;

function TreeView({ selectedFile }) {
  const [zoom, setZoom] = useState(1);
  // fitScale = the multiplier where the whole graph just fits the visible pane.
  // Rendered SVG dims are VIEW_W * zoom * fitScale, so zoom=1 ALWAYS shows the
  // entire tree. Recomputed on resize.
  const [fitScale, setFitScale] = useState(1);
  const paneRef = useRef(null);
  useEffect(() => {
    const el = paneRef.current;
    if (!el) return;
    const compute = () => {
      const padX = 40;  // matches container padding
      const padY = 32;
      const availW = Math.max(100, el.clientWidth - padX);
      const availH = Math.max(100, el.clientHeight - padY);
      const s = Math.min(availW / VIEW_W, availH / VIEW_H);
      setFitScale(s > 0 ? s : 1);
    };
    compute();
    const ro = new ResizeObserver(compute);
    ro.observe(el);
    return () => ro.disconnect();
  }, []);
  const path = selectedFile ? DEMO_PATHS?.[selectedFile] : null;
  const pathEdges = new Set();
  if (path) {
    for (let i = 0; i < path.nodes.length - 1; i++) {
      pathEdges.add(`${path.nodes[i]}->${path.nodes[i+1]}`);
    }
  }

  return (
    <div className="rac-card" style={{padding:0, overflow:"hidden"}}>
      <div style={{padding:"12px 18px", borderBottom:"1px solid var(--rac-line)", display:"flex", alignItems:"center", gap:14}}>
        <div style={{display:"flex", gap:14, fontSize:12}}>
          <Legend swatch={<svg width="22" height="14"><rect x="1" y="1" width="20" height="12" rx="3" ry="3" fill="#fff" stroke="var(--rac-ink-2)" strokeWidth="1.4"/></svg>} label="Decision"/>
          <Legend swatch={<svg width="16" height="16"><circle cx="8" cy="8" r="7" fill="var(--rac-pass-bg)" stroke="var(--rac-pass)" strokeWidth="1.6"/></svg>} label="Pass"/>
          <Legend swatch={<svg width="16" height="16"><circle cx="8" cy="8" r="7" fill="var(--rac-fail-bg)" stroke="var(--rac-fail)" strokeWidth="1.6"/></svg>} label="Fail"/>
          {selectedFile && <Legend swatch={<svg width="20" height="6"><line x1="0" y1="3" x2="20" y2="3" stroke={selectedFile === "pass" ? "var(--rac-pass)" : "var(--rac-fail)"} strokeWidth="3"/></svg>} label={`${selectedFile === "pass" ? "PASS" : "FAIL"} demo path`}/>}
        </div>
        <div style={{marginLeft:"auto", display:"flex", gap:6}}>
          <button className="rac-btn rac-btn--ghost rac-btn--sm" onClick={() => setZoom(z => Math.max(0.4, z - 0.1))}><I.Minus size={12}/></button>
          <span style={{display:"inline-flex", alignItems:"center", justifyContent:"center", padding:"0 12px", height:28, fontSize:12, color:"var(--rac-muted)", whiteSpace:"nowrap", minWidth:60, flexShrink:0, lineHeight:1}}>{Math.round(zoom*100)}%</span>
          <button className="rac-btn rac-btn--ghost rac-btn--sm" onClick={() => setZoom(z => Math.min(1.4, z + 0.1))}><I.Plus size={12}/></button>
          <button className="rac-btn rac-btn--ghost rac-btn--sm" onClick={() => setZoom(1)}><I.Reset size={12}/> Fit</button>
        </div>
      </div>

      <div ref={paneRef} style={{overflow:"auto", background:"var(--rac-surface-alt)", padding:"16px 20px", height:560}}>
        <svg viewBox={`${VIEW_X} ${VIEW_Y} ${VIEW_W} ${VIEW_H}`} width={VIEW_W * zoom * fitScale} height={VIEW_H * zoom * fitScale} style={{display:"block", margin:0}}>
          <defs>
            <marker id="arr-default" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#9aa1bd"/></marker>
            <marker id="arr-pass" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="var(--rac-pass)"/></marker>
            <marker id="arr-fail" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="var(--rac-fail)"/></marker>
          </defs>

          {/* Tier row labels (positioned just above each tier's rectangles) */}
          <g style={{fontFamily:"var(--font-display)", fontSize:11, fontWeight:700, fill:"var(--rac-muted)", letterSpacing:"0.08em"}}>
            {TIER_LABELS.map(t => (
              <text key={t.y} x="-90" y={t.y - DECISION_HH - 14} textAnchor="start">{t.text}</text>
            ))}
          </g>

          {/* Edges */}
          {ALL_EDGES.map(([from, to, label]) => {
            const a = NODE_POS[from]; const b = NODE_POS[to];
            if (!a || !b) return null;
            const onPath = pathEdges.has(`${from}->${to}`);
            const isFailEdge = onPath && path?.outcome === "fail" && to.startsWith("FAIL");
            const isPassEdge = onPath && path?.outcome === "pass";
            const stroke = isFailEdge ? "var(--rac-fail)" : isPassEdge ? "var(--rac-pass)" : onPath ? "var(--rac-fail)" : "#cbd2e2";
            const strokeWidth = onPath ? 2.6 : 1.4;
            const marker = isFailEdge ? "arr-fail" : isPassEdge ? "arr-pass" : onPath ? "arr-fail" : "arr-default";
            const opacity = onPath ? 1 : 0.55;

            // Choose attachment points based on relative geometry of a → b
            const isVerticalUp   = b.y < a.y - 40;
            const isVerticalDown = b.y > a.y + 40;
            const isHorizontal   = !isVerticalUp && !isVerticalDown;
            const bIsTerminal    = to.startsWith("PASS") || to.startsWith("FAIL");
            const bVRad          = bIsTerminal ? TERMINAL_R : DECISION_HH;
            const bHRad          = bIsTerminal ? TERMINAL_R : DECISION_HW;

            let ax, ay, bx, by, c1x, c1y, c2x, c2y;
            if (isVerticalUp) {
              ax = a.x; ay = a.y - DECISION_HH;
              bx = b.x; by = b.y + bVRad;
              c1x = a.x; c1y = (a.y + b.y) / 2;
              c2x = b.x; c2y = (a.y + b.y) / 2;
            } else if (isVerticalDown) {
              ax = a.x; ay = a.y + DECISION_HH;
              bx = b.x; by = b.y - bVRad;
              c1x = a.x; c1y = (a.y + b.y) / 2;
              c2x = b.x; c2y = (a.y + b.y) / 2;
            } else {
              ax = a.x + DECISION_HW; ay = a.y;
              bx = b.x - bHRad; by = b.y;
              const mx = (ax + bx) / 2;
              c1x = mx; c1y = a.y;
              c2x = mx; c2y = b.y;
            }
            const dPath = `M ${ax} ${ay} C ${c1x} ${c1y}, ${c2x} ${c2y}, ${bx} ${by}`;

            // Label position
            let lx, ly;
            if (isHorizontal) {
              lx = (ax + bx) / 2;
              ly = a.y - 8;
            } else {
              // For long diagonal drops, push label off the curve so adjacent
              // drops to the same target don't stack labels on top of each other
              const isLongDrop = isVerticalDown && Math.abs(b.x - a.x) > 80;
              const xOffset = isLongDrop ? 18 : 10;
              lx = ax + (bx - ax) * 0.20 + xOffset;
              ly = ay + (by - ay) * 0.25;
            }

            return (
              <g key={`${from}-${to}`} opacity={opacity}>
                <path d={dPath} stroke={stroke} strokeWidth={strokeWidth} fill="none" markerEnd={`url(#${marker})`} />
                <text x={lx} y={ly} fill={onPath ? stroke : "#9aa1bd"} fontSize="10" fontWeight="700" textAnchor="middle" style={{textTransform:"uppercase", letterSpacing:"0.06em"}}>
                  {label}
                </text>
              </g>
            );
          })}

          {/* Nodes */}
          {Object.entries(NODE_POS).map(([id, pos]) => {
            if (id === "LOOP") return null;
            const fromTreeNodes = (TREE_NODES || []).find(n => n.id === id);
            const question = fromTreeNodes?.question || QUESTION_TEXT[id] || "";
            const onPath = path?.nodes?.includes(id);
            const isFailingNode = path?.failingNode === id;
            const isTerminalPass = id.startsWith("PASS");
            const isTerminalFail = id.startsWith("FAIL");
            return (
              <Node
                key={id}
                id={id}
                question={question}
                x={pos.x} y={pos.y}
                onPath={onPath}
                isFailing={isFailingNode}
                isTerminalPass={isTerminalPass}
                isTerminalFail={isTerminalFail}
              />
            );
          })}

          
        </svg>
      </div>
    </div>
  );
}

function Node({ id, question, x, y, onPath, isFailing, isTerminalPass, isTerminalFail }) {
  if (isTerminalPass || isTerminalFail) {
    const color = isTerminalPass ? "var(--rac-pass)" : "var(--rac-fail)";
    const bg    = isTerminalPass ? "var(--rac-pass-bg)" : "var(--rac-fail-bg)";
    return (
      <g>
        <circle cx={x} cy={y} r={TERMINAL_R} fill={onPath ? color : bg} stroke={color} strokeWidth={onPath ? 2.5 : 1.5}/>
        <text x={x} y={y + 4} textAnchor="middle" fill={onPath ? "#fff" : color} fontSize="10" fontWeight="700">
          {isTerminalPass ? "PASS" : "FAIL"}
        </text>
        <text x={x} y={y + 38} textAnchor="middle" fill="var(--rac-muted)" fontSize="9.5" fontFamily="var(--font-mono)">{id}</text>
      </g>
    );
  }
  // Decision rectangle — N1 is the orange-highlighted entry into the qualification logic
  const isEntry = id === "N1";
  const stroke = isEntry ? "#c47a09" : isFailing ? "var(--rac-fail)" : onPath ? "var(--rac-primary)" : "var(--rac-line-strong)";
  const fill = isFailing ? "var(--rac-fail-bg)" : onPath ? "var(--rac-primary-50)" : "#fff";
  const strokeWidth = onPath ? 2.5 : isEntry ? 2 : 1.4;
  const lines = wrapText(question, WRAP_CHARS).slice(0, 4);
  // Vertically center the wrapped text block inside the rectangle.
  const firstBaselineY = y - (lines.length - 1) * LINE_HEIGHT / 2 + 2.5;
  return (
    <g>
      {/* Node ID label above the rectangle, aligned to top-left so it doesn't
          collide with arrows that enter the top-center of the rectangle */}
      <text
        x={x - DECISION_HW + 4}
        y={y - DECISION_HH - 6}
        textAnchor="start"
        fill="var(--rac-muted)"
        fontSize="9.5"
        fontWeight="600"
        fontFamily="var(--font-mono)"
        style={{letterSpacing:"0.05em"}}
      >
        {id}
      </text>
      {/* Rounded rectangle */}
      <rect
        x={x - DECISION_HW}
        y={y - DECISION_HH}
        width={DECISION_HW * 2}
        height={DECISION_HH * 2}
        rx={DECISION_RX}
        ry={DECISION_RX}
        fill={fill}
        stroke={stroke}
        strokeWidth={strokeWidth}
      />
      {/* Question text inside rectangle, vertically centered */}
      <g style={{fontSize:9.5, fontFamily:"var(--font-sans)", fill:"var(--rac-ink)"}}>
        {lines.map((ln, i) => (
          <text key={i} x={x} y={firstBaselineY + i * LINE_HEIGHT} textAnchor="middle">{ln}</text>
        ))}
      </g>
    </g>
  );
}

function wrapText(text, maxChars) {
  const words = text.split(/\s+/);
  const lines = [];
  let cur = "";
  for (const w of words) {
    if ((cur + " " + w).trim().length > maxChars) {
      if (cur) lines.push(cur);
      cur = w;
    } else {
      cur = cur ? cur + " " + w : w;
    }
  }
  if (cur) lines.push(cur);
  return lines;
}

function Legend({ swatch, label }) {
  return (
    <span style={{display:"inline-flex", alignItems:"center", gap:6, color:"var(--rac-muted)"}}>
      {swatch}<span>{label}</span>
    </span>
  );
}

window.TreeView = TreeView;
