import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
import { MessageSquare, CornerDownRight, Lightbulb } from "lucide-react";
import { C, FD, FM, toolIcon, intentOf, intentIcon, toolTypeVisible } from "../theme.js";
import { EdgeLegend } from "./Primitives.jsx";
import { buildDataflow } from "../dataflow.js";

// MODE B centre: per-turn DATAFLOW graph. Root = the user query. Children = the
// tool calls along the spine. INDIRECT edges cross-link a tool_result to the
// later tool that consumed its value — the highlighted causal path.
//
// PROVEN (value-flow) edges = SOLID orange.  HYPOTHESIS (proximity) = DOTTED muted.
// Always visually separated (NON-NEGOTIABLE #4).

const ROW_H = 40;       // vertical spacing per tool row
const GUTTER = 64;      // left gutter reserved for the edge SVG
const NODE_X = 22;      // x of node centre inside the gutter

export default function TurnGraph({ turn, selected, onSelect, vis }) {
  const { nodes, edges } = buildDataflow(turn);
  // Tool-type legend filter: rows of a hidden tool type are dimmed and their
  // value-flow arcs suppressed, so the eyes work here too.
  const hidden = new Set();
  (turn.tools || []).forEach((tl, idx) => {
    if (vis && !toolTypeVisible(vis, tl.name)) hidden.add("t" + idx);
  });
  const containerRef = useRef(null);
  const rowRefs = useRef({});
  const [ys, setYs] = useState({});

  // Measure each node's vertical centre so the SVG edges can connect to the
  // real DOM rows (robust to wrapping / variable heights).
  useLayoutEffect(() => {
    const cont = containerRef.current;
    if (!cont) return;
    const base = cont.getBoundingClientRect().top;
    const next = {};
    nodes.forEach((n) => {
      const el = rowRefs.current[n.key];
      if (el) {
        const r = el.getBoundingClientRect();
        next[n.key] = r.top - base + r.height / 2;
      }
    });
    setYs(next);
    // re-run when the turn or selection changes layout
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [turn.i, selected, nodes.length]);

  // When a tool is selected (e.g. opened from a chat citation), scroll it into
  // view — a 71-tool turn can hide the cited tool far down the spine.
  useEffect(() => {
    if (selected == null) return;
    const el = rowRefs.current["t" + selected];
    if (el && el.scrollIntoView) el.scrollIntoView({ block: "center", behavior: "smooth" });
  }, [selected, turn.i]);

  const II = intentIcon(intentOf(turn));

  // Focus-driven causal path: a turn with 49 value-flow links must NOT render as
  // a permanent hairball. The clean grey spine is always on; an orange value-flow
  // arc is drawn ONLY for the tool the user hovers or selects (UI-SPEC: "the
  // highlighted causal path").
  const [hover, setHover] = useState(null);
  const activeIdx = hover != null ? hover : selected;
  const activeKey = activeIdx != null ? "t" + activeIdx : null;
  const provenCount = edges.filter((e) => e.type === "proven").length;
  const tracedHere = edges.filter((e) => e.type === "proven" && e.from && e.to).length;

  // Build SVG path data for each edge using measured y-centres.
  function edgePath(e) {
    const y1 = ys[e.from];
    const y2 = ys[e.to];
    if (y1 == null || y2 == null) return null;
    if (e.type === "spine") {
      // vertical backbone hugging the gutter
      return `M ${NODE_X} ${y1} L ${NODE_X} ${y2}`;
    }
    // cross-link: ONE smooth cubic bow to the left — a clean causal jump.
    const bow = Math.min(40, 16 + Math.abs(y2 - y1) * 0.12);
    const cx = NODE_X - bow;
    return `M ${NODE_X} ${y1} C ${cx} ${y1}, ${cx} ${y2}, ${NODE_X} ${y2}`;
  }

  return (
    <div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column" }}>
      {/* query header */}
      <div style={{ padding: "14px 22px 12px", borderBottom: `1px solid ${C.borderSoft}` }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <II size={15} color={turn.heavy ? C.orange : C.blue} />
          <span style={{ fontFamily: FD, fontWeight: 600, fontSize: 13, letterSpacing: 0.3, color: C.muted }}>
            QUERY {String(turn.i).padStart(2, "0")}
          </span>
          {turn.origin === "system" && (
            <span style={{ fontFamily: FM, fontSize: 9, color: C.blue, border: `1px solid ${C.blue}`, borderRadius: 4, padding: "1px 6px" }}>SYSTEM</span>
          )}
          {turn.heavy && (
            <span style={{ fontFamily: FM, fontSize: 9, color: C.orange, border: `1px solid ${C.orange}`, borderRadius: 4, padding: "1px 6px" }}>HEAVY</span>
          )}
        </div>
        <div style={{ fontSize: 14, color: C.text, marginTop: 8, lineHeight: 1.5, whiteSpace: "pre-wrap", maxHeight: 80, overflow: "auto", fontFamily: FM, background: C.black, padding: "10px 12px", borderRadius: 7, border: `1px solid ${C.borderSoft}` }}>
          {turn.prompt}
        </div>
        {turn.reply && (
          <div style={{ display: "flex", gap: 7, marginTop: 10, fontSize: 12.5, color: C.text2, lineHeight: 1.5 }}>
            <CornerDownRight size={14} color={C.cyan} style={{ flexShrink: 0, marginTop: 2 }} />
            <span><b style={{ color: C.cyan, fontFamily: FM, fontSize: 10 }}>REPLY </b>{turn.reply.slice(0, 240)}…</span>
          </div>
        )}
      </div>

      <div style={{ overflowY: "auto", flex: 1, padding: "14px 22px" }}>
        {/* guidance card — surfaces inline on flagged turns ONLY */}
        {turn.guide && (
          <div className="pop" style={{ display: "flex", gap: 12, background: `linear-gradient(135deg,${C.orangeMut},transparent)`, border: `1px solid ${C.amber}`, borderRadius: 8, padding: "13px 15px", marginBottom: 16 }}>
            <div style={{ width: 32, height: 32, borderRadius: 8, flexShrink: 0, background: C.amber, display: "flex", alignItems: "center", justifyContent: "center" }}>
              <Lightbulb size={17} color="#1b1a19" />
            </div>
            <div>
              <div style={{ fontSize: 13.5, fontWeight: 700, color: C.amber, fontFamily: FD, letterSpacing: 0.2 }}>NEXT TIME — {turn.guide.head}</div>
              <div style={{ fontSize: 12.5, color: C.text2, lineHeight: 1.6, marginTop: 5 }}>{turn.guide.body}</div>
            </div>
          </div>
        )}

        {/* edge legend — proven vs hypothesis, always separated */}
        <div style={{ display: "flex", gap: 16, marginBottom: 10, flexWrap: "wrap", alignItems: "center" }}>
          <EdgeLegend c={C.border} text="sequence" />
          <EdgeLegend c={C.orange} text="proven · value-flow" />
          <EdgeLegend c={C.muted} text="hypothesis · proximity" dashed />
          <div style={{ flex: 1 }} />
          <span style={{ fontFamily: FM, fontSize: 11, color: C.muted }}>{turn.reqs} round-trips · {turn.tools.length} tools</span>
        </div>

        {/* causal-path hint — keeps the canvas clean; arcs are drawn on focus */}
        {provenCount > 0 && (
          <div style={{ marginBottom: 12, fontFamily: FM, fontSize: 10.5, color: C.muted }}>
            <span style={{ color: C.orange }}>●</span> {provenCount} proven value-flow link{provenCount === 1 ? "" : "s"} on this turn
            {tracedHere < provenCount && <span> · {tracedHere} traceable in-turn</span>} ·{" "}
            <span style={{ color: C.text2 }}>hover or click a tool to trace its causal path</span>
          </div>
        )}

        {/* the graph: SVG edge layer + DOM node rows */}
        <div ref={containerRef} style={{ position: "relative" }}>
          <svg style={{ position: "absolute", left: 0, top: 0, width: GUTTER, height: "100%", overflow: "visible", pointerEvents: "none" }}>
            {edges.map((e, i) => {
              // Spine = always-on clean grey backbone.
              if (e.type === "spine") {
                const d = edgePath(e);
                if (!d) return null;
                return <path key={i} d={d} fill="none" stroke={C.border} strokeWidth={1.5} opacity={0.5} />;
              }
              // Cross-links: focus-driven. Only the active node's incident,
              // intra-turn links are drawn — no permanent hairball.
              if (!e.from || !e.to) return null; // external (cross-turn source) — not drawable
              if (hidden.has(e.from) || hidden.has(e.to)) return null; // tool type hidden
              if (!activeKey || (e.from !== activeKey && e.to !== activeKey)) return null;
              const d = edgePath(e);
              if (!d) return null;
              const proven = e.type === "proven";
              return (
                <path
                  key={i}
                  d={d}
                  fill="none"
                  stroke={proven ? C.orange : C.muted}
                  strokeWidth={proven ? 2.25 : 1.75}
                  strokeDasharray={proven ? "0" : "3 3"}
                  opacity={proven ? 0.98 : 0.8}
                  style={proven ? { filter: `drop-shadow(0 0 3px ${C.orange}66)` } : undefined}
                />
              );
            })}
          </svg>

          {/* root query node */}
          <NodeRow
            innerRef={(el) => (rowRefs.current.q = el)}
            x={GUTTER}
            icon={MessageSquare}
            iconColor={C.blue}
            title={nodes[0].label}
            sub={nodes[0].sub}
            tone="query"
          />

          {/* tool nodes */}
          {turn.tools.map((tl, idx) => {
            const Icon = toolIcon(tl.name);
            const ind = tl.provenance === "indirect";
            const provenFlow = ind && tl.flowValue;
            const sel = selected === idx;
            return (
              <NodeRow
                key={idx}
                innerRef={(el) => (rowRefs.current["t" + idx] = el)}
                x={GUTTER}
                icon={Icon}
                iconColor={provenFlow ? C.orange : ind ? C.amber : C.text2}
                title={tl.mcp ? `${tl.mcp.server}:${tl.mcp.tool}` : tl.name}
                sub={tl.summary}
                tone={provenFlow ? "proven" : ind ? "indirect" : "direct"}
                errored={tl.errored}
                selected={sel || hover === idx}
                dimmed={hidden.has("t" + idx)}
                onHover={(on) => setHover(on ? idx : null)}
                tag={
                  ind
                    ? provenFlow
                      ? { color: C.orange, text: tl.sourceTool || "prior", solid: true }
                      : { color: C.muted, text: tl.sourceTool || "prior", solid: false }
                    : { color: C.muted, text: "direct", solid: false }
                }
                onClick={() => onSelect(sel ? null : idx)}
              />
            );
          })}

          {/* reply node */}
          <NodeRow
            innerRef={(el) => (rowRefs.current.reply = el)}
            x={GUTTER}
            icon={CornerDownRight}
            iconColor={C.cyan}
            title="Reply to you"
            sub={turn.reply ? turn.reply.slice(0, 90) + "…" : "(work-only turn)"}
            tone="query"
            noEdge
          />
        </div>
      </div>
    </div>
  );
}

function NodeRow({ innerRef, x, icon: Icon, iconColor, title, sub, tone, errored, selected, tag, onClick, onHover, dimmed }) {
  const clickable = !!onClick;
  const isTool = tone === "proven" || tone === "indirect" || tone === "direct";
  const borderLeft = tone === "proven" ? C.orange : tone === "indirect" ? C.amber : C.border;
  return (
    <div
      ref={innerRef}
      className={clickable ? "row" : ""}
      onClick={onClick}
      onMouseEnter={onHover ? () => onHover(true) : undefined}
      onMouseLeave={onHover ? () => onHover(false) : undefined}
      style={{
        marginLeft: x,
        display: "flex",
        alignItems: "center",
        gap: 10,
        minHeight: ROW_H,
        padding: isTool ? "7px 10px" : "9px 11px",
        cursor: clickable ? "pointer" : "default",
        borderLeft: isTool ? `2px solid ${borderLeft}` : `1px solid transparent`,
        background: selected ? C.card : isTool ? "transparent" : `linear-gradient(135deg,${C.card},${C.panel})`,
        border: isTool ? undefined : `1px solid ${iconColor}`,
        borderRadius: isTool ? "0 6px 6px 0" : 8,
        marginBottom: isTool ? 0 : 6,
        marginTop: isTool ? 0 : 0,
        opacity: dimmed ? 0.32 : 1,
        filter: dimmed ? "grayscale(0.5)" : "none",
        transition: "opacity .15s ease",
      }}
    >
      <div style={{ width: isTool ? 24 : 28, height: isTool ? 24 : 28, borderRadius: isTool ? 5 : 7, flexShrink: 0, background: C.elevated, display: "flex", alignItems: "center", justifyContent: "center", border: `1px solid ${iconColor}` }}>
        <Icon size={isTool ? 13 : 15} color={iconColor} />
      </div>
      <div style={{ minWidth: 0, flex: 1 }}>
        <div style={{ display: "flex", gap: 7, alignItems: "baseline" }}>
          <span style={{ fontFamily: FM, fontSize: isTool ? 12 : 13, fontWeight: 600, color: iconColor }}>
            {title}
            {errored && <span style={{ marginLeft: 6, color: C.red, fontSize: 9 }}>● err</span>}
          </span>
          <span style={{ fontFamily: FM, fontSize: isTool ? 11 : 10.5, color: C.muted, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", flex: 1 }}>{sub}</span>
        </div>
      </div>
      {tag && (
        <span style={{ fontFamily: FM, fontSize: 9, color: tag.color, display: "flex", alignItems: "center", gap: 3, flexShrink: 0 }}>
          {tag.solid && <CornerDownRight size={10} />}
          {tag.text}
        </span>
      )}
    </div>
  );
}
