// === Unified Relationship Explorer — tripartite knowledge graph ===

const ExplorerPage = ({ onOpenCompetitor }) => {
  const [mode, setMode] = React.useState('tripartite'); // queries | tripartite | sources-comp
  const [tierFilter, setTierFilter] = React.useState(new Set(['dominant','competitive','at-risk','absent']));
  const [providerFilter, setProviderFilter] = React.useState('all');
  const [strengthFilter, setStrengthFilter] = React.useState('all');
  const [diffMode, setDiffMode] = React.useState(false);
  const [time, setTime] = React.useState(30); // 0..30 days ago — 30 = now
  const [hoverNode, setHoverNode] = React.useState(null);
  const [focusedQuery, setFocusedQuery] = React.useState(null);
  const [edgeDetail, setEdgeDetail] = React.useState(null);
  const [showFilters, setShowFilters] = React.useState(false);

  const KG = window.KG;

  // Layout positions
  const W = 1180, H = 620;
  const colX = { q: 280, s: W * 0.5, c: W - 160 };

  const queryNodes = KG.queries
    .filter(q => tierFilter.has(q.tier))
    .map((q, i, arr) => ({ ...q, x: colX.q, y: 50 + (i + 0.5) * ((H - 100) / arr.length) }));

  const sourceNodes = KG.sources.map((s, i, arr) => ({
    ...s, x: colX.s + (i % 2 === 0 ? -40 : 40),
    y: 50 + (i + 0.5) * ((H - 100) / arr.length),
  }));

  const compNodes = KG.competitors.map((c, i, arr) => ({
    ...c, x: colX.c, y: 50 + (i + 0.5) * ((H - 100) / arr.length),
  }));

  const nodeById = id => [...queryNodes, ...sourceNodes, ...compNodes].find(n => n.id === id);

  const visibleEdges = React.useMemo(() => {
    let edges = [];
    if (mode === 'queries') {
      // Query→Query co-occurrence (pseudo)
      return [];
    }
    if (mode !== 'sources-comp') {
      edges = edges.concat(KG.qsEdges.map(e => ({...e})));
    }
    edges = edges.concat(KG.scEdges.map(e => ({...e})));

    if (focusedQuery) {
      // Show only edges that connect to the focused query (1 + 2 hop)
      const qSrcs = KG.qsEdges.filter(e => e.source === focusedQuery).map(e => e.target);
      edges = edges.filter(e => {
        if (e.kind === 'qs') return e.source === focusedQuery;
        if (e.kind === 'sc') return qSrcs.includes(e.source);
        return false;
      });
    }
    if (strengthFilter === 'strong') edges = edges.filter(e => e.kind !== 'sc' || e.weight >= 6);
    if (strengthFilter === 'weak') edges = edges.filter(e => e.kind !== 'sc' || e.weight < 6);
    return edges;
  }, [mode, focusedQuery, strengthFilter]);

  const dimNode = (id) => {
    if (!focusedQuery && !hoverNode) return false;
    const target = focusedQuery || hoverNode;
    if (id === target) return false;
    const connected = visibleEdges.some(e =>
      (e.source === target && e.target === id) || (e.target === target && e.source === id)
    );
    if (connected) return false;
    // 2-hop check
    const hop1 = visibleEdges.filter(e => e.source === target || e.target === target)
      .map(e => e.source === target ? e.target : e.source);
    const hop2 = visibleEdges.some(e => hop1.includes(e.source) && e.target === id || hop1.includes(e.target) && e.source === id);
    return !hop2;
  };

  const tierCounts = KG.queries.reduce((acc, q) => { acc[q.tier] = (acc[q.tier]||0) + 1; return acc; }, {});

  return (
    <div className="body" style={{padding: 0}}>
      {/* Top filter bar */}
      <div style={{padding: '10px 18px', borderBottom: '1px solid var(--line)', background: 'var(--surface)', display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap'}}>
        <div className="row" style={{gap: 4}}>
          <Pill onClick={() => setMode('queries')} active={mode === 'queries'}>Queries only</Pill>
          <Pill onClick={() => setMode('tripartite')} active={mode === 'tripartite'}>Tripartite</Pill>
          <Pill onClick={() => setMode('sources-comp')} active={mode === 'sources-comp'}>Sources × Comp</Pill>
        </div>
        <div style={{width: 1, height: 16, background: 'var(--line-2)'}}/>
        <div className="row" style={{gap: 4}}>
          {['dominant','competitive','at-risk','absent'].map(t => (
            <Pill
              key={t}
              onClick={() => {
                const next = new Set(tierFilter);
                next.has(t) ? next.delete(t) : next.add(t);
                setTierFilter(next);
              }}
              active={tierFilter.has(t)}
            >
              <span style={{width: 7, height: 7, borderRadius: '50%', background: KG.tierColor[t], display: 'inline-block', marginRight: 4}}/>
              {t} <span className="mono" style={{opacity: .6, marginLeft: 3}}>{tierCounts[t]||0}</span>
            </Pill>
          ))}
        </div>
        <div style={{width: 1, height: 16, background: 'var(--line-2)'}}/>
        <div className="row" style={{gap: 4}}>
          <Pill onClick={() => setStrengthFilter('all')} active={strengthFilter === 'all'}>All edges</Pill>
          <Pill onClick={() => setStrengthFilter('strong')} active={strengthFilter === 'strong'}>Strong</Pill>
          <Pill onClick={() => setStrengthFilter('weak')} active={strengthFilter === 'weak'}>Weak</Pill>
        </div>
        <div className="grow" style={{flex: 1}}/>

        {/* Time scrubber */}
        <div className="row" style={{gap: 8, padding: '0 12px', background: 'var(--surface-2)', borderRadius: 6, border: '1px solid var(--line)', height: 30}}>
          <Icon name="clock" size={12}/>
          <input type="range" min="0" max="30" value={time} onChange={e => setTime(+e.target.value)} style={{width: 120}}/>
          <span className="mono muted" style={{fontSize: 11, minWidth: 60}}>
            {time === 30 ? 'now' : `−${30-time}d`}
          </span>
        </div>
        <Pill onClick={() => setDiffMode(!diffMode)} active={diffMode}>
          <Icon name="filter" size={10}/> Diff mode
        </Pill>
        <Btn ghost sm onClick={() => setShowFilters(!showFilters)}>
          <Icon name="filter" size={11}/> More filters
        </Btn>
      </div>

      {/* Stats bar */}
      <div style={{padding: '10px 18px', borderBottom: '1px solid var(--line)', display: 'flex', gap: 28, alignItems: 'center', background: 'var(--bg)'}}>
        <div className="stack-2">
          <div className="eyebrow">Health</div>
          <div className="row" style={{gap: 8}}>
            <span style={{fontFamily: 'var(--font-display)', fontSize: 22, lineHeight: 1}}>77%</span>
            <Delta d="+3"/>
          </div>
        </div>
        <div style={{width: 1, height: 28, background: 'var(--line)'}}/>
        <div className="stack-2">
          <div className="eyebrow">Top threat</div>
          <div className="row" style={{gap: 6}}>
            <Fav init="DL"/>
            <span style={{fontWeight: 500}}>Direct Line</span>
            <span className="muted mono" style={{fontSize: 11}}>+11 queries</span>
          </div>
        </div>
        <div style={{width: 1, height: 28, background: 'var(--line)'}}/>
        <div className="stack-2">
          <div className="eyebrow">Worst gap</div>
          <div style={{fontSize: 12.5}}>
            EV insurance UK <span className="muted" style={{fontSize: 11}}>· 0 of 14 queries cited</span>
          </div>
        </div>
        <div className="grow"/>
        <div className="row" style={{gap: 6}}>
          {focusedQuery && (
            <Pill onClick={() => setFocusedQuery(null)}>
              focused: {KG.queries.find(q => q.id === focusedQuery)?.label.slice(0, 28)}… ✕
            </Pill>
          )}
          <span className="muted mono" style={{fontSize: 11}}>{queryNodes.length}q · {sourceNodes.length}s · {compNodes.length}c</span>
        </div>
      </div>

      {/* Canvas */}
      <div style={{flex: 1, position: 'relative', background: 'var(--bg-2)', overflow: 'hidden'}}>
        <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={H} style={{display: 'block'}}>
          {/* Column labels */}
          <text x={colX.q} y={28} fontSize="10" fill="var(--ink-3)" textAnchor="middle" fontWeight="600" style={{textTransform: 'uppercase', letterSpacing: '.08em'}}>QUERIES</text>
          <text x={colX.s} y={28} fontSize="10" fill="var(--ink-3)" textAnchor="middle" fontWeight="600" style={{textTransform: 'uppercase', letterSpacing: '.08em'}}>SOURCES</text>
          <text x={colX.c} y={28} fontSize="10" fill="var(--ink-3)" textAnchor="middle" fontWeight="600" style={{textTransform: 'uppercase', letterSpacing: '.08em'}}>COMPETITORS</text>

          {/* Edges */}
          <g>
            {visibleEdges.map((e, i) => {
              const a = nodeById(e.source), b = nodeById(e.target);
              if (!a || !b) return null;
              const dim = (focusedQuery || hoverNode) && !((focusedQuery === a.id || focusedQuery === b.id) || (hoverNode === a.id || hoverNode === b.id));
              const cx = (a.x + b.x) / 2;
              const path = `M${a.x},${a.y} C${cx},${a.y} ${cx},${b.y} ${b.x},${b.y}`;
              const colorMap = { qs: '#6D28D9', sc: '#1F6FEB' };
              const isFocused = focusedQuery && (e.source === focusedQuery || (e.kind === 'sc' && KG.qsEdges.some(qs => qs.source === focusedQuery && qs.target === e.source)));
              return (
                <path
                  key={i}
                  d={path}
                  stroke={e.changed === 'gained' ? '#2f7a4f' : e.changed === 'lost' ? '#d94f3f' : colorMap[e.kind]}
                  strokeWidth={e.kind === 'sc' ? Math.max(0.6, Math.log2(e.weight + 1) * 0.7) : 1}
                  strokeDasharray={e.kind === 'qs' ? '3 3' : (e.changed ? '4 2' : '0')}
                  fill="none"
                  opacity={dim ? 0.06 : isFocused ? 0.95 : 0.32}
                  style={{cursor: 'pointer'}}
                  onClick={() => setEdgeDetail(e)}
                />
              );
            })}
          </g>

          {/* Query nodes — diamond w/ tier+visibility halo */}
          {queryNodes.map(q => {
            const dim = dimNode(q.id);
            const haloR = 18 + (q.visibility / 100) * 22;
            return (
              <g
                key={q.id}
                opacity={dim ? 0.2 : 1}
                style={{cursor: 'pointer'}}
                onMouseEnter={() => setHoverNode(q.id)}
                onMouseLeave={() => setHoverNode(null)}
                onClick={() => setFocusedQuery(focusedQuery === q.id ? null : q.id)}
              >
                <circle cx={q.x} cy={q.y} r={haloR} fill={KG.tierColor[q.tier]} opacity="0.08"/>
                <circle cx={q.x} cy={q.y} r={haloR - 8} fill={KG.tierColor[q.tier]} opacity="0.14"/>
                <rect
                  x={q.x - 7} y={q.y - 7} width="14" height="14"
                  transform={`rotate(45 ${q.x} ${q.y})`}
                  fill={KG.tierColor[q.tier]}
                  stroke={focusedQuery === q.id ? 'var(--ink-1)' : 'none'}
                  strokeWidth="2"
                />
                <text x={q.x - 16} y={q.y + 3} fontSize="10.5" fill="var(--ink-2)" textAnchor="end">
                  {q.label.length > 32 ? q.label.slice(0, 30) + '…' : q.label}
                </text>
                <text x={q.x - 16} y={q.y + 14} fontSize="9" fill="var(--ink-4)" textAnchor="end" fontFamily="var(--font-mono)">
                  vis {q.visibility} · #{q.selfRank ?? '—'} {q.trend !== '0' && `· ${q.trend}`}
                </text>
              </g>
            );
          })}

          {/* Source nodes */}
          {sourceNodes.map(s => {
            const dim = dimNode(s.id);
            const r = 7 + Math.log2(s.citations) * 1.8;
            return (
              <g
                key={s.id}
                opacity={dim ? 0.2 : 1}
                onMouseEnter={() => setHoverNode(s.id)}
                onMouseLeave={() => setHoverNode(null)}
                style={{cursor: 'pointer'}}
              >
                <circle cx={s.x} cy={s.y} r={r + 3} fill={KG.sourceTypeColor[s.type]} opacity="0.12"/>
                <circle cx={s.x} cy={s.y} r={r} fill={KG.sourceTypeColor[s.type]}/>
                <text x={s.x} y={s.y + r + 11} fontSize="9.5" fill="var(--ink-2)" textAnchor="middle">{s.domain}</text>
                <text x={s.x} y={s.y + r + 21} fontSize="8.5" fill="var(--ink-4)" textAnchor="middle" fontFamily="var(--font-mono)">
                  {s.type} · {s.citations}
                </text>
              </g>
            );
          })}

          {/* Competitor nodes — square */}
          {compNodes.map(c => {
            const dim = dimNode(c.id);
            const sz = 14 + (c.score / 100) * 14;
            return (
              <g
                key={c.id}
                opacity={dim ? 0.2 : 1}
                onMouseEnter={() => setHoverNode(c.id)}
                onMouseLeave={() => setHoverNode(null)}
                onClick={() => onOpenCompetitor && onOpenCompetitor({ id: c.id, name: c.name, init: c.init, score: c.score, sov: c.sov, claude: 1, gpt: 1, gemini: 2, perplex: 1, coverage: 138, citations: 412, you: c.you, rising: c.trend === 'up', threat: 'high', delta: '+3', domain: c.name.toLowerCase().replace(/[^a-z]/g, '') + '.com' })}
                style={{cursor: 'pointer'}}
              >
                {c.you && <rect x={c.x - sz/2 - 4} y={c.y - sz/2 - 4} width={sz + 8} height={sz + 8} fill="none" stroke="#6D28D9" strokeWidth="2" rx="4"/>}
                <rect
                  x={c.x - sz/2} y={c.y - sz/2} width={sz} height={sz}
                  fill={c.you ? '#6D28D9' : '#1F6FEB'}
                  rx="3"
                />
                <text x={c.x + sz/2 + 8} y={c.y + 3} fontSize="11" fill="var(--ink-1)" fontWeight={c.you ? '600' : '500'}>
                  {c.name}
                </text>
                <text x={c.x + sz/2 + 8} y={c.y + 15} fontSize="9" fill="var(--ink-4)" fontFamily="var(--font-mono)">
                  score {c.score} · sov {c.sov}%
                </text>

                {/* Per-LLM rank dots when query focused */}
                {focusedQuery && (() => {
                  const r = KG.ranks[focusedQuery]?.[c.id];
                  if (!r) return null;
                  return ['claude','gpt','gemini','perplex'].map((k, i) => {
                    const rank = r[k];
                    const color = rank == null ? '#c2c5cb' : rank <= 3 ? '#2f7a4f' : rank <= 7 ? '#b88a2b' : '#d94f3f';
                    return (
                      <g key={k} transform={`translate(${c.x + sz/2 + 8 + i * 16}, ${c.y + 24})`}>
                        <circle r="6" fill={color}/>
                        <text x="0" y="2.5" fontSize="7.5" fill="#fff" textAnchor="middle" fontWeight="700">
                          {rank ?? '—'}
                        </text>
                      </g>
                    );
                  });
                })()}
              </g>
            );
          })}
        </svg>

        {/* Legend bottom-right */}
        <div style={{position: 'absolute', bottom: 14, right: 14, background: 'var(--surface)', border: '1px solid var(--line)', borderRadius: 6, padding: 10, fontSize: 10.5, boxShadow: 'var(--shadow-1)', display: 'flex', flexDirection: 'column', gap: 6, minWidth: 200}}>
          <div className="eyebrow">Legend</div>
          <div className="row" style={{gap: 6, whiteSpace: 'nowrap'}}><svg width="20" height="2"><line x1="0" y1="1" x2="20" y2="1" stroke="#6D28D9" strokeDasharray="3 3"/></svg> Query → source</div>
          <div className="row" style={{gap: 6, whiteSpace: 'nowrap'}}><svg width="20" height="2"><line x1="0" y1="1" x2="20" y2="1" stroke="#1F6FEB" strokeWidth="1.6"/></svg> Source → competitor</div>
          <div className="row" style={{gap: 6, whiteSpace: 'nowrap'}}><svg width="20" height="2"><line x1="0" y1="1" x2="20" y2="1" stroke="#2f7a4f" strokeWidth="1.6" strokeDasharray="4 2"/></svg> New edge</div>
          <div className="row" style={{gap: 6, whiteSpace: 'nowrap'}}><svg width="20" height="2"><line x1="0" y1="1" x2="20" y2="1" stroke="#d94f3f" strokeWidth="1.6" strokeDasharray="4 2"/></svg> Lost edge</div>
          <div style={{height: 1, background: 'var(--line)', margin: '2px 0'}}/>
          <div style={{fontSize: 9.5, color: 'var(--ink-3)'}}>halo = visibility · color = tier</div>
        </div>

        {/* Hint */}
        <div style={{position: 'absolute', top: 14, left: '50%', transform: 'translateX(-50%)', fontSize: 11, color: 'var(--ink-3)', background: 'var(--surface)', padding: '4px 10px', borderRadius: 99, border: '1px solid var(--line)'}}>
          {focusedQuery ? 'Showing 2-hop neighborhood. Click query again to clear.' : 'Click a query to focus its neighborhood. Click any edge for citation reasons.'}
        </div>
      </div>

      {/* Edge slide-over */}
      {edgeDetail && (
        <EdgeSlideOver edge={edgeDetail} onClose={() => setEdgeDetail(null)} nodeById={nodeById}/>
      )}
    </div>
  );
};

const EdgeSlideOver = ({ edge, onClose, nodeById }) => {
  const a = nodeById(edge.source), b = nodeById(edge.target);
  const isQS = edge.kind === 'qs';
  return (
    <div style={{
      position: 'fixed', top: 0, right: 0, bottom: 0, width: 440, zIndex: 30,
      background: 'var(--surface)', borderLeft: '1px solid var(--line)', boxShadow: '-8px 0 24px rgba(0,0,0,0.06)',
      display: 'flex', flexDirection: 'column',
      animation: 'slideIn .18s ease-out',
    }}>
      <div style={{padding: '14px 18px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', gap: 10}}>
        <div className="eyebrow">Edge detail</div>
        <div className="grow"/>
        <button className="icon-btn" onClick={onClose}>✕</button>
      </div>
      <div style={{padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 14, overflowY: 'auto'}}>
        <div className="card" style={{margin: 0}}>
          <div style={{padding: 14, display: 'flex', alignItems: 'center', gap: 12}}>
            <div className="stack-2" style={{flex: 1}}>
              <div className="eyebrow">{isQS ? 'Query' : 'Source'}</div>
              <div style={{fontSize: 13, fontWeight: 500}}>{a?.label || a?.domain || a?.name}</div>
            </div>
            <Icon name="arrow" size={14}/>
            <div className="stack-2" style={{flex: 1, textAlign: 'right'}}>
              <div className="eyebrow">{isQS ? 'Source' : 'Competitor'}</div>
              <div style={{fontSize: 13, fontWeight: 500}}>{b?.label || b?.domain || b?.name}</div>
            </div>
          </div>
          <div style={{borderTop: '1px solid var(--line)', padding: 14, display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10}}>
            <div className="stack-2"><div className="eyebrow">Weight</div><div className="mono" style={{fontSize: 14, fontWeight: 600}}>{edge.weight}</div></div>
            <div className="stack-2"><div className="eyebrow">Status</div><Pill tone={edge.changed === 'gained' ? 'green' : edge.changed === 'lost' ? 'coral' : ''}>{edge.changed || 'stable'}</Pill></div>
            <div className="stack-2"><div className="eyebrow">7d trend</div><Sparkline data={trend(edge.weight, 12, 50, 8)} color="var(--navy)" w={70} h={20}/></div>
          </div>
        </div>

        <div>
          <div className="eyebrow" style={{marginBottom: 8}}>Citation reasons</div>
          <div className="stack-8">
            {[
              { reason: 'Cited as top recommendation for transparent pricing', sentiment: 'positive', frequency: 12 },
              { reason: 'Strong claims-handling reputation, mentioned alongside Aviva', sentiment: 'positive', frequency: 8 },
              { reason: 'Some forum threads mention slow renewal price hikes', sentiment: 'mixed', frequency: 4 },
            ].map((r, i) => (
              <div key={i} style={{padding: 10, background: 'var(--surface-2)', borderRadius: 6, border: '1px solid var(--line)', display: 'grid', gridTemplateColumns: '1fr auto auto', gap: 10, alignItems: 'center'}}>
                <div style={{fontSize: 12, lineHeight: 1.5}}>{r.reason}</div>
                <Pill tone={r.sentiment === 'positive' ? 'green' : r.sentiment === 'mixed' ? 'gold' : 'coral'}>{r.sentiment}</Pill>
                <span className="mono muted" style={{fontSize: 11}}>{r.frequency}×</span>
              </div>
            ))}
          </div>
        </div>

        <div>
          <div className="eyebrow" style={{marginBottom: 8}}>Per-provider rank diff</div>
          <div className="stack-8">
            {['claude','gpt','gemini','perplex'].map((k, i) => {
              const cur = [3,2,4,3][i], prev = [4,2,3,5][i];
              return (
                <div key={k} className="row" style={{gap: 10, padding: 8, background: 'var(--surface-2)', borderRadius: 6}}>
                  <LLMDot k={k}/>
                  <span style={{flex: 1, fontSize: 12}}>{({claude:'Claude',gpt:'ChatGPT',gemini:'Gemini',perplex:'Perplexity'})[k]}</span>
                  <span className="mono muted" style={{fontSize: 11}}>was #{prev}</span>
                  <Icon name="arrow" size={11}/>
                  <span className="mono" style={{fontSize: 12, fontWeight: 600}}>#{cur}</span>
                  <Delta d={cur < prev ? `+${prev-cur}` : cur > prev ? `−${cur-prev}` : '0'}/>
                </div>
              );
            })}
          </div>
        </div>

        <div>
          <div className="eyebrow" style={{marginBottom: 8}}>Raw provider excerpt</div>
          <blockquote style={{margin: 0, padding: 12, background: 'var(--surface-2)', borderRadius: 6, fontSize: 12, lineHeight: 1.55, color: 'var(--ink-2)', borderLeft: '2px solid var(--navy)'}}>
            "Among UK car insurers, the most frequently recommended providers for value and reliability include Direct Line, Admiral, and Aviva. MoneySavingExpert specifically highlights Admiral's MultiCar discount as the strongest in this category…"
            <div className="muted mono" style={{marginTop: 8, fontSize: 10}}>Claude · 2h ago · query: "Best car insurance UK 2026"</div>
          </blockquote>
        </div>

        <div>
          <div className="eyebrow" style={{marginBottom: 8}}>Source change history</div>
          <div className="stack-8">
            {[
              {when: '2d ago', kind: 'gained', txt: 'New citation appeared in Claude'},
              {when: '8d ago', kind: 'lost', txt: 'Lost from Gemini results'},
              {when: '14d ago', kind: 'gained', txt: 'First appearance in Perplexity'},
            ].map((c, i) => (
              <div key={i} className="row" style={{gap: 10, fontSize: 12}}>
                <Pill tone={c.kind === 'gained' ? 'green' : 'coral'}>{c.kind}</Pill>
                <span style={{flex: 1}}>{c.txt}</span>
                <span className="muted mono" style={{fontSize: 11}}>{c.when}</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

window.ExplorerPage = ExplorerPage;
