/* ============================================================
   Albion Cockpit — presentational components
   ============================================================ */
const { useState, useEffect, useRef } = React;

/* ---------------- Icons ---------------- */
function Crest({ size = 46 }) {
  return (
    <svg className="crest" width={size} height={size} viewBox="0 0 48 48" aria-hidden="true">
      <circle cx="24" cy="24" r="23" fill="#123F2E" />
      <circle cx="24" cy="24" r="19" fill="none" stroke="#BE9C49" strokeWidth="1.4" />
      <rect x="14.5" y="14.5" width="19" height="19" rx="2" transform="rotate(45 24 24)" fill="none" stroke="#E6D5A8" strokeWidth="1.4" />
      <rect x="18.5" y="18.5" width="11" height="11" transform="rotate(45 24 24)" fill="#BE9C49" />
      <circle cx="24" cy="24" r="2.4" fill="#123F2E" />
    </svg>
  );
}
const IconPlay = () => <svg viewBox="0 0 24 24" fill="currentColor"><path d="M7 5.5v13a1 1 0 0 0 1.5.87l11-6.5a1 1 0 0 0 0-1.74l-11-6.5A1 1 0 0 0 7 5.5z" /></svg>;
const IconPause = () => <svg viewBox="0 0 24 24" fill="currentColor"><rect x="6.5" y="5" width="4" height="14" rx="1" /><rect x="13.5" y="5" width="4" height="14" rx="1" /></svg>;
const IconBolt = ({ cls = 'bolt' }) => <svg className={cls} viewBox="0 0 24 24" fill="currentColor"><path d="M13 2 4 14h6l-1 8 9-12h-6l1-8z" /></svg>;
const IconDoc = () => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5z" /><path d="M14 3v5h5M8.5 13h7M8.5 16.5h7" /></svg>;
const IconClose = () => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M6 6l12 12M18 6 6 18" /></svg>;
const IconWarn = () => <svg className="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M12 3 2 20h20L12 3z" /><path d="M12 10v5M12 17.5v.5" strokeLinecap="round" /></svg>;
const IconInfo = () => <svg className="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><circle cx="12" cy="12" r="9" /><path d="M12 11v5M12 8v.5" strokeLinecap="round" /></svg>;
const IconShield = () => <svg className="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M12 3 4 6v6c0 5 3.5 8 8 9 4.5-1 8-4 8-9V6l-8-3z" /><path d="M9 12l2 2 4-4" strokeLinecap="round" strokeLinejoin="round" /></svg>;

/* ---------------- Top bar ---------------- */
function TopBar(props) {
  const { playing, speed, day, onPlay, onSpeed, onScrub, onInject, injectOpen, tlFillRef, tlHeadRef, events } = props;
  const D = window.AlbionData;
  const dt = D.dayToDate(day);
  const trackRef = useRef(null);

  // timeline ticks derived from the real run events
  const ticks = (events || []).map(e => ({
    id: e.id,
    day: e.day,
    kind: e.feedType === 'escalation' ? 'esc'
      : (e.feedType === 'resolve' || e.feedType === 'produce' || e.feedType === 'handoff') ? 'hero'
      : 'bg',
  }));

  function scrub(e) {
    const r = trackRef.current.getBoundingClientRect();
    const ratio = Math.max(0, Math.min(1, (e.clientX - r.left) / r.width));
    onScrub(Math.round(ratio * 364));
  }
  const [drag, setDrag] = useState(false);
  useEffect(() => {
    if (!drag) return;
    const mv = e => scrub(e); const up = () => setDrag(false);
    window.addEventListener('mousemove', mv); window.addEventListener('mouseup', up);
    return () => { window.removeEventListener('mousemove', mv); window.removeEventListener('mouseup', up); };
  }, [drag]);

  return (
    <header className="topbar">
      <div className="topbar-main">
        <div className="brand">
          <Crest />
          <div className="brand-tx">
            <div className="brand-name">ALBION FINANCIAL GROUP</div>
            <div className="brand-sub">Agentic Operations Cockpit</div>
          </div>
        </div>

        <div className="transport">
          <button className="play-btn" onClick={onPlay} title={playing ? 'Pause (Space)' : 'Play (Space)'}>
            {playing ? <IconPause /> : <IconPlay />}
          </button>
          <div className="speed">
            <b>SPEED</b>
            {[1, 2, 4, 8].map(s => (
              <button key={s} className={speed === s ? 'on' : ''} onClick={() => onSpeed(s)}>{s}×</button>
            ))}
          </div>
        </div>

        <div className="clock">
          <div className="clock-q">{dt.q} {D.YEAR}</div>
          <div className="clock-date">{String(dt.dd).padStart(2,'0')} {dt.mon}</div>
          <div className="clock-meta">
            <div className="clock-day">DAY {String(day).padStart(3,'0')} / 365</div>
            <div className="clock-acc">SIMULATED · 1 YEAR / 120s</div>
          </div>
        </div>

        <div className="tb-spacer" />

        <div className="tb-right">
          <div className={'livepill' + (playing ? ' live' : '')}>
            <span className="dot" />{playing ? 'LIVE \u00b7 RUNNING' : 'PAUSED \u00b7 PRESENTER'}
          </div>
          <button className={'inject-btn' + (injectOpen ? ' on' : '')} onClick={onInject}>
            <IconBolt /> Inject Event
          </button>
        </div>
      </div>

      <div className="timeline">
        <div className="tl-label">JAN</div>
        <div className="tl-track" ref={trackRef} onMouseDown={e => { setDrag(true); scrub(e); }}>
          <div className="tl-rail" />
          <div className="tl-fill" ref={tlFillRef} />
          {['Q1','Q2','Q3','Q4'].map((q, i) => (
            <React.Fragment key={q}>
              {i > 0 && <div className="tl-qtick" style={{ left: (i * 25) + '%' }} />}
              <div className="tl-q" style={{ left: (i * 25 + 12.5) + '%', top:'150%' }}>{q}</div>
            </React.Fragment>
          ))}
          {ticks.map(t => (
            <div key={t.id} className={'tl-ev ' + (t.kind === 'esc' ? 'esc' : t.kind === 'hero' ? 'hero' : '')}
                 style={{ left: (t.day / 364 * 100) + '%' }} />
          ))}
          <div className="tl-head" ref={tlHeadRef} />
        </div>
        <div className="tl-label">DEC</div>
      </div>
    </header>
  );
}

/* ---------------- Agent node ---------------- */
function AgentNode({ agent, status, pulse, selected, hasDoc, score, onClick }) {
  const STAT = { idle:'Idle', active:'Working', done:'Signed off', escalate:'Escalation' };
  const band = typeof score === 'number' ? (score >= 8 ? 'hi' : score >= 6.5 ? 'mid' : 'lo') : null;
  return (
    <div className={`node s-${status}` + (pulse ? ' pulse' : '') + (selected ? ' sel' : '')}
         style={{ left: agent.x + 'px', top: agent.y + 'px' }}
         onClick={() => onClick(agent)}>
      {band && <span className={'node-score ' + band} title={'Chief of Agents — FY score ' + score}>{score}</span>}
      <div className="node-card">
        <div className="node-top">
          <span className="node-role">{agent.role}</span>
          <span className="node-stat"><i className="sd" />{STAT[status]}</span>
        </div>
        <div className="node-name">{agent.name}</div>
        <div className="node-foot">
          <span className="node-code">{agent.code}</span>
          <span className={'node-doc' + (hasDoc ? ' has' : '')}><IconDoc />Document</span>
        </div>
      </div>
    </div>
  );
}

/* ---------------- Org map ---------------- */
function OrgMap({ org, scorecards, statuses, pulses, selected, nodeDocs, escArmed, litConn, tokens, tokenLayerRef, onNode }) {
  const mapW = (org && org.mapWidth) || 1340;
  const mapH = (org && org.mapHeight) || 862;
  const nodes = (org && org.nodes) || [];
  const connectors = (org && org.connectors) || [];
  const fyById = {};
  for (const c of (scorecards || [])) { if (c.quarter === 'FY') fyById[c.agentId] = c.overall; }
  const wrapRef = useRef(null);
  const [view, setView] = useState({ x: 0, y: 0, k: 1 });
  const [grabbing, setGrabbing] = useState(false);
  const dragRef = useRef(null);

  function fit() {
    const w = wrapRef.current;
    if (!w) return;
    const pad = 28;
    const k = Math.max(0.2, Math.min(1, Math.min(
      (w.clientWidth - pad * 2) / mapW,
      (w.clientHeight - pad * 2) / mapH,
    )));
    setView({ k, x: (w.clientWidth - mapW * k) / 2, y: (w.clientHeight - mapH * k) / 2 });
  }

  // re-fit when the org layout loads/changes
  useEffect(() => { const id = requestAnimationFrame(fit); return () => cancelAnimationFrame(id); }, [org]);

  // latest zoom fn in a ref so the native (non-passive) wheel listener stays fresh
  const zoomRef = useRef(null);
  zoomRef.current = (factor) => setView(v => {
    const k = Math.max(0.2, Math.min(2.5, v.k * factor));
    const w = wrapRef.current;
    if (!w) return { ...v, k };
    const cx = w.clientWidth / 2, cy = w.clientHeight / 2;
    return { k, x: cx - (cx - v.x) * (k / v.k), y: cy - (cy - v.y) * (k / v.k) };
  });

  useEffect(() => {
    const id = requestAnimationFrame(fit);
    window.addEventListener('resize', fit);
    const w = wrapRef.current;
    const wheel = (e) => { e.preventDefault(); zoomRef.current(e.deltaY < 0 ? 1.12 : 0.89); };
    if (w) w.addEventListener('wheel', wheel, { passive: false });
    function mv(e) {
      const d = dragRef.current; if (!d) return;
      setView(v => ({ ...v, x: d.ox + (e.clientX - d.sx), y: d.oy + (e.clientY - d.sy) }));
    }
    function up() { dragRef.current = null; setGrabbing(false); }
    window.addEventListener('mousemove', mv); window.addEventListener('mouseup', up);
    return () => {
      cancelAnimationFrame(id);
      window.removeEventListener('resize', fit);
      if (w) w.removeEventListener('wheel', wheel);
      window.removeEventListener('mousemove', mv); window.removeEventListener('mouseup', up);
    };
  }, []);

  function onDown(e) {
    if (e.target.closest('.node')) return;
    dragRef.current = { sx: e.clientX, sy: e.clientY, ox: view.x, oy: view.y };
    setGrabbing(true);
  }

  return (
    <section className="panel main-panel" style={{ height:'100%' }}>
      <div className="panel-head">
        <span className="panel-idx">02</span>
        <span className="panel-title">Organisation · Live</span>
        <div className="panel-spacer" />
        <div className="legend">
          <span><i className="lg-idle" />Idle</span>
          <span><i className="lg-active" />Working</span>
          <span><i className="lg-done" />Signed off</span>
          <span><i className="lg-esc" />Escalation</span>
        </div>
      </div>
      <div className={'map-wrap' + (grabbing ? ' grabbing' : '')} ref={wrapRef} onMouseDown={onDown}>
        <div className="map-grid" />
        <div className="map-canvas" style={{ width: mapW + 'px', height: mapH + 'px', transform: `translate(${view.x}px, ${view.y}px) scale(${view.k})` }}>
          <svg className="map-svg" viewBox={`0 0 ${mapW} ${mapH}`} preserveAspectRatio="none">
            <defs>
              <marker id="arr" markerWidth="9" markerHeight="9" refX="6" refY="4.5" orient="auto">
                <path d="M1 1 L7 4.5 L1 8" fill="none" stroke="#CDBE9A" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
              </marker>
              <marker id="arrEsc" markerWidth="9" markerHeight="9" refX="6" refY="4.5" orient="auto">
                <path d="M1 1 L7 4.5 L1 8" fill="none" stroke="#A63427" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
              </marker>
            </defs>
            {connectors.map(c => (
              <path key={c.id} id={c.id} d={c.d}
                    className={'conn ' + c.kind + (c.id === 'audit-prod-esc' && escArmed ? ' armed' : '') + (litConn[c.id] ? ' lit' : '')}
                    markerEnd={(c.kind === 'flow' || c.kind === 'escalation') ? (c.kind === 'escalation' ? 'url(#arrEsc)' : 'url(#arr)') : undefined} />
            ))}
          </svg>

          {/* tier + franchise labels */}
          {['governance', 'orchestration', 'group'].map(t => {
            const ns = nodes.filter(n => n.tier === t);
            if (!ns.length) return null;
            const y = Math.min(...ns.map(n => n.y)) - 58;
            return <div className="tier-tag" key={t} style={{ left:'24px', top: y + 'px' }}>{t === 'group' ? 'GROUP FUNCTIONS' : t.toUpperCase()}</div>;
          })}
          {[...new Set(nodes.filter(n => n.franchise).map(n => n.franchise))].map(f => {
            const seats = nodes.filter(n => n.franchise === f);
            const head = seats.find(n => n.role === 'Franchise Head') || seats[0];
            return <div className="tier-tag" key={f} style={{ left: (head.x - 70) + 'px', top: (head.y - 62) + 'px' }}>ALBION {f.toUpperCase()}</div>;
          })}

          {nodes.map(a => (
            <AgentNode key={a.id} agent={a} status={statuses[a.id] || 'idle'}
                       pulse={pulses.has(a.id)} selected={selected === a.id}
                       hasDoc={!!nodeDocs[a.id]} score={fyById[a.id]} onClick={onNode} />
          ))}

          {/* token layer */}
          <div ref={tokenLayerRef} style={{ position:'absolute', inset:0, pointerEvents:'none' }}>
            {tokens.map(t => (
              <div className={'token ' + t.tone} id={'tok-' + t.id} key={t.id} style={{ left:'-99px', top:'-99px' }}>
                {t.reverse && <div className="token-tag">{t.label}</div>}
                <div className="token-dot" />
                {!t.reverse && <div className="token-tag">{t.label}</div>}
              </div>
            ))}
          </div>
        </div>
        <div className="map-controls">
          <button onClick={fit} title="Fit all agents">Fit</button>
          <button onClick={() => zoomRef.current(1.12)} title="Zoom in">+</button>
          <button onClick={() => zoomRef.current(0.89)} title="Zoom out">&minus;</button>
          <button onClick={() => setView({ x: 0, y: 0, k: 1 })} title="Actual size">1:1</button>
        </div>
      </div>
    </section>
  );
}

window.AlbionUI = {
  Crest, IconPlay, IconPause, IconBolt, IconDoc, IconClose, IconWarn, IconInfo, IconShield,
  TopBar, AgentNode, OrgMap,
};
