// DiagramCanvas — softened SysML diagram (rounded blocks w/ compartments,
// associations as gentle splines). Themed entirely via CSS variables read
// from the wrapping aesthetic. Pure visual, with hover + selection.

function DiagramCanvas({ data, density = "comfortable", variant = "softened", focusBlockId, onSelect }) {
  const W = 720, H = 460;
  const pad = density === "compact" ? 8 : 12;
  const titleSize = density === "compact" ? 12 : 13;
  const propSize = density === "compact" ? 10.5 : 11.5;
  const stroke = variant === "formal" ? 1.4 : 1;

  const blockMap = React.useMemo(
    () => Object.fromEntries(data.blocks.map((b) => [b.id, b])),
    [data]
  );

  function blockRect(b) {
    return { x: b.x * W, y: b.y * H, w: b.w, h: b.h };
  }

  // Compute a clean orthogonal-ish path between blocks: exit center→nearest edge,
  // then a soft S-curve.
  function pathFor(a) {
    const f = blockRect(blockMap[a.from]);
    const t = blockRect(blockMap[a.to]);
    const fc = { x: f.x + f.w / 2, y: f.y + f.h / 2 };
    const tc = { x: t.x + t.w / 2, y: t.y + t.h / 2 };
    // Exit/enter on horizontal axis if dx dominates, else vertical.
    const dx = tc.x - fc.x, dy = tc.y - fc.y;
    let p1, p2, mid;
    if (Math.abs(dx) > Math.abs(dy)) {
      p1 = { x: fc.x + Math.sign(dx) * f.w / 2, y: fc.y };
      p2 = { x: tc.x - Math.sign(dx) * t.w / 2, y: tc.y };
      const cx = (p1.x + p2.x) / 2;
      mid = `C ${cx} ${p1.y}, ${cx} ${p2.y}, ${p2.x} ${p2.y}`;
    } else {
      p1 = { x: fc.x, y: fc.y + Math.sign(dy) * f.h / 2 };
      p2 = { x: tc.x, y: tc.y - Math.sign(dy) * t.h / 2 };
      const cy = (p1.y + p2.y) / 2;
      mid = `C ${p1.x} ${cy}, ${p2.x} ${cy}, ${p2.x} ${p2.y}`;
    }
    return { d: `M ${p1.x} ${p1.y} ${mid}`, mid: { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 } };
  }

  const radius = variant === "formal" ? 3 : variant === "graph" ? 14 : 8;

  return (
    <div className="diagram-wrap" style={{ width: "100%", height: "100%", position: "relative", overflow: "hidden", background: "var(--diagram-bg)" }}>
      <div className="diagram-grid" />
      <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="xMidYMid meet" width="100%" height="100%" style={{ display: "block" }}>
        <defs>
          <marker id="arrow-soft" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
            <path d="M 0 1 L 9 5 L 0 9 z" fill="var(--edge)" />
          </marker>
          <marker id="diamond-soft" viewBox="0 0 12 10" refX="11" refY="5" markerWidth="10" markerHeight="8" orient="auto-start-reverse">
            <path d="M 0 5 L 6 1 L 12 5 L 6 9 z" fill="var(--surface)" stroke="var(--edge)" strokeWidth="1" />
          </marker>
        </defs>

        {/* Edges */}
        {data.associations.map((a) => {
          const p = pathFor(a);
          const isComposition = a.kind === "composition";
          const isConstraint = a.kind === "constraint";
          return (
            <g key={a.id}>
              <path
                d={p.d}
                fill="none"
                stroke="var(--edge)"
                strokeWidth={stroke}
                strokeDasharray={isConstraint ? "4 4" : null}
                markerEnd={isComposition ? "url(#diamond-soft)" : "url(#arrow-soft)"}
                opacity={0.85}
              />
              <g transform={`translate(${p.mid.x}, ${p.mid.y})`}>
                <rect x={-a.label.length * 3.4 - 5} y={-8} width={a.label.length * 6.8 + 10} height={16}
                  rx={4} fill="var(--diagram-bg)" stroke="var(--edge-soft)" strokeWidth="0.75" />
                <text x={0} y={3} textAnchor="middle" fontSize="10.5" fontFamily="var(--font-mono)" fill="var(--edge-label)">
                  {a.label}
                </text>
              </g>
            </g>
          );
        })}

        {/* Blocks */}
        {data.blocks.map((b) => {
          const r = blockRect(b);
          const focus = focusBlockId === b.id;
          const isConstraint = b.kind === "constraint";
          const isActor = b.kind === "actor";
          return (
            <g key={b.id} style={{ cursor: "pointer" }} onClick={() => onSelect && onSelect(b.id)}>
              {/* shadow */}
              {variant !== "formal" && (
                <rect x={r.x + 1.5} y={r.y + 3} width={r.w} height={r.h} rx={radius}
                  fill="var(--shadow)" opacity="0.5" />
              )}
              <rect
                x={r.x}
                y={r.y}
                width={r.w}
                height={r.h}
                rx={radius}
                fill={isConstraint ? "var(--block-constraint-bg)" : isActor ? "var(--block-actor-bg)" : "var(--block-bg)"}
                stroke={focus ? "var(--accent)" : "var(--block-border)"}
                strokeWidth={focus ? 1.75 : stroke}
                strokeDasharray={isConstraint ? "5 3" : null}
              />
              {/* Header bar */}
              {!isConstraint && (
                <line x1={r.x} y1={r.y + 32} x2={r.x + r.w} y2={r.y + 32} stroke="var(--block-divider)" strokeWidth="0.75" />
              )}
              {/* Stereotype */}
              <text x={r.x + pad} y={r.y + 15} fontSize="9" fontFamily="var(--font-mono)" fill="var(--muted)" letterSpacing="0.04em">
                «{isConstraint ? "constraint" : isActor ? "actor" : "block"}»
              </text>
              {/* Title */}
              <text x={r.x + pad} y={r.y + (isConstraint ? 40 : 28)} fontSize={titleSize} fontFamily="var(--font-display)" fontWeight="600" fill="var(--fg)">
                {b.label}
              </text>
              {/* Properties */}
              {!isConstraint && b.properties.slice(0, 4).map((p, i) => (
                <text key={i} x={r.x + pad} y={r.y + 50 + i * (propSize + 5)} fontSize={propSize} fontFamily="var(--font-mono)" fill="var(--muted-strong)">
                  · {p}
                </text>
              ))}
              {isConstraint && (
                <text x={r.x + pad} y={r.y + 58} fontSize={propSize} fontFamily="var(--font-mono)" fill="var(--muted-strong)">
                  {"{ tenancy = institutional }"}
                </text>
              )}
            </g>
          );
        })}
      </svg>
    </div>
  );
}

window.DiagramCanvas = DiagramCanvas;
