// Map component — renders nodes, edges, fields with pan/zoom + interactions.
const { useState, useEffect, useRef, useMemo, useCallback } = React;

const YEARS = [2020, 2021, 2022, 2023, 2024, 2025, 2026];
const FIELD_TINTS = {
  "f-structure": "#EEEAE0",
  "f-design":    "#E6EAE0",
  "f-antibody":  "#EBE4DC",
  "f-drug":      "#EDE7DC",
  "f-cell":      "#E4E8E4",
  "f-genomics":  "#EAE5DC"
};

// Canvas design size for coordinate math
const CW = 1400, CH = 720;

function MapCanvas({ data, query, yearCap, activeKinds, onOpen, hoverId, setHoverId }) {
  const svgRef = useRef(null);
  const [transform, setTransform] = useState({ k:1, x:0, y:0 });
  const [dragging, setDragging] = useState(false);
  const dragState = useRef(null);

  // filter
  const visibleNodes = useMemo(()=>{
    const q = (query||"").toLowerCase().trim();
    return data.nodes.filter(n=>{
      if (n.year > yearCap) return false;
      if (!activeKinds[n.kind]) return false;
      if (!q) return true;
      const hay = [n.label, n.zh, n.org, n.desc].join(" ").toLowerCase();
      return hay.includes(q);
    });
  },[data, query, yearCap, activeKinds]);

  const visibleIds = useMemo(()=>new Set(visibleNodes.map(n=>n.id)),[visibleNodes]);
  const visibleEdges = useMemo(()=>
    data.edges.filter(([a,b])=>visibleIds.has(a)&&visibleIds.has(b)),
  [data.edges, visibleIds]);

  const nbrMap = useMemo(()=>{
    const m = new Map();
    data.edges.forEach(([a,b])=>{
      if(!m.has(a)) m.set(a,new Set()); if(!m.has(b)) m.set(b,new Set());
      m.get(a).add(b); m.get(b).add(a);
    });
    return m;
  },[data.edges]);

  const hovered = hoverId;
  const neighbors = hovered ? (nbrMap.get(hovered) || new Set()) : null;

  // pan & zoom
  const onWheel = (e)=>{
    e.preventDefault();
    const rect = svgRef.current.getBoundingClientRect();
    const mx = e.clientX - rect.left, my = e.clientY - rect.top;
    const scale = Math.exp(-e.deltaY * 0.0015);
    setTransform(t=>{
      const k2 = Math.min(3.2, Math.max(0.6, t.k * scale));
      const x2 = mx - (mx - t.x) * (k2 / t.k);
      const y2 = my - (my - t.y) * (k2 / t.k);
      return { k:k2, x:x2, y:y2 };
    });
  };
  const onDown = (e)=>{
    if (e.button!==0) return;
    setDragging(true);
    dragState.current = { x:e.clientX, y:e.clientY, tx:transform.x, ty:transform.y };
  };
  const onMove = (e)=>{
    if (!dragging || !dragState.current) return;
    const dx = e.clientX - dragState.current.x;
    const dy = e.clientY - dragState.current.y;
    setTransform(t=>({ ...t, x: dragState.current.tx + dx, y: dragState.current.ty + dy }));
  };
  const onUp = ()=>{ setDragging(false); dragState.current = null; };

  // expose zoom controls via window
  useEffect(()=>{
    window.__zoom = (dir)=>{
      setTransform(t=>{
        const k2 = Math.min(3.2, Math.max(0.6, t.k * (dir>0?1.2:1/1.2)));
        const cx = CW/2, cy = CH/2;
        const x2 = cx - (cx - t.x) * (k2 / t.k);
        const y2 = cy - (cy - t.y) * (k2 / t.k);
        return { k:k2, x:x2, y:y2 };
      });
    };
    window.__zoomReset = ()=> setTransform({k:1,x:0,y:0});
  },[]);

  const N = (n)=> ({ ...n, cx: n.x * CW, cy: n.y * CH });
  const nodeMap = useMemo(()=> new Map(data.nodes.map(n=>[n.id, N(n)])),[data.nodes]);

  const isActive = (id)=> hovered === id;
  const isNeighbor = (id)=> neighbors && neighbors.has(id);
  const edgeActive = ([a,b])=> hovered && (a===hovered || b===hovered);

  const shapeForKind = (n, active) => {
    const pos = nodeMap.get(n.id);
    if (n.kind === "model") {
      return <circle cx={pos.cx} cy={pos.cy} r={active?14:12} fill="#1A1A1A" stroke="none" />;
    }
    if (n.kind === "tool") {
      return <circle cx={pos.cx} cy={pos.cy} r={active?9:7.5} fill="var(--accent)" stroke="none" />;
    }
    // company: small outlined square
    const s = active?13:11;
    return <rect x={pos.cx - s/2} y={pos.cy - s/2} width={s} height={s}
                 fill="transparent" stroke="#1A1A1A" strokeWidth="1.2" />;
  };

  return (
    <svg
      ref={svgRef}
      viewBox={`0 0 ${CW} ${CH}`}
      preserveAspectRatio="xMidYMid meet"
      className={dragging?"grabbing":""}
      onWheel={onWheel} onMouseDown={onDown} onMouseMove={onMove}
      onMouseUp={onUp} onMouseLeave={onUp}
    >
      <g transform={`translate(${transform.x} ${transform.y}) scale(${transform.k})`}>
        {/* field rectangles */}
        {data.fields.map(f=>{
          const x = f.x*CW, y = f.y*CH, w = f.w*CW, h = f.h*CH;
          return (
            <g key={f.id}>
              <rect className="field-rect"
                x={x} y={y} width={w} height={h}
                fill={FIELD_TINTS[f.id]} opacity={hovered?0.35:0.55}
                rx="2" />
              <text className="field-label"
                x={x+12} y={y+20}>{f.name}</text>
              <text className="field-label-zh"
                x={x+12} y={y+34}>{f.zh}</text>
            </g>
          );
        })}

        {/* edges */}
        <g>
        {visibleEdges.map(([a,b],i)=>{
          const A = nodeMap.get(a), B = nodeMap.get(b);
          if (!A || !B) return null;
          const active = edgeActive([a,b]);
          return <line key={i} className={"edge"+(active?" active":"")}
            x1={A.cx} y1={A.cy} x2={B.cx} y2={B.cy} />;
        })}
        </g>

        {/* nodes */}
        <g className={hovered?"dimmed":""}>
        {visibleNodes.map(n=>{
          const pos = nodeMap.get(n.id);
          const active = isActive(n.id), nb = isNeighbor(n.id);
          const cls = "node" + (active?" active":"") + (nb?" neighbor":"");
          const labelCls = "node-label" + (active?" active":"") + (nb?" neighbor":"")
                          + (n.zh && !n.label.match(/[A-Za-z]/)?" han":"");
          const labelDy = n.kind==="model" ? 26 : 20;
          return (
            <g key={n.id} className={cls}
               onMouseEnter={()=>setHoverId(n.id)}
               onMouseLeave={()=>setHoverId(null)}
               onClick={()=>onOpen(n.id)}>
              {shapeForKind(n, active||nb)}
              <text className={labelCls}
                x={pos.cx} y={pos.cy + labelDy}
                textAnchor="middle">{n.label}</text>
            </g>
          );
        })}
        </g>
      </g>
    </svg>
  );
}

function Legend({ kinds, setKinds, counts }){
  const rows = [
    { key:"model",   name:"Foundation Models", zh:"基础模型",
      swatch: <span style={{width:14,height:14,borderRadius:"50%",background:"#1A1A1A",display:"inline-block"}}/> },
    { key:"tool",    name:"Tools & Platforms", zh:"工具 · 平台",
      swatch: <span style={{width:10,height:10,borderRadius:"50%",background:"var(--accent)",display:"inline-block"}}/> },
    { key:"company", name:"Companies & Labs",  zh:"公司 · 实验室",
      swatch: <span style={{width:11,height:11,border:"1.2px solid #1A1A1A",display:"inline-block"}}/> },
    { key:"field",   name:"Research Fields",   zh:"研究方向",
      swatch: <span style={{width:16,height:10,background:"#E8E6E0",display:"inline-block"}}/>, staticField:true }
  ];
  return (
    <div className="legend">
      <h4>Legend · 图例</h4>
      {rows.map(r=>(
        <div key={r.key}
             className={"legend-row"+(!r.staticField && !kinds[r.key]?" dim":"")}
             onClick={()=> !r.staticField && setKinds({...kinds,[r.key]:!kinds[r.key]})}>
          <span className="legend-swatch">{r.swatch}</span>
          <span>
            {r.name}
            <div style={{fontFamily:"var(--han-sans)",fontSize:10.5,color:"var(--gray)",letterSpacing:"0.02em",marginTop:1}}>{r.zh}</div>
          </span>
          {!r.staticField && <span className="legend-count">{counts[r.key]||0}</span>}
          {r.staticField && <span className="legend-count">6</span>}
        </div>
      ))}
    </div>
  );
}

function Timeline({ year, setYear }){
  const trackRef = useRef(null);
  const [drag, setDrag] = useState(false);
  const pctOf = (y)=> (y - YEARS[0]) / (YEARS[YEARS.length-1] - YEARS[0]);
  const setFromX = (clientX)=>{
    const r = trackRef.current.getBoundingClientRect();
    const p = Math.max(0, Math.min(1, (clientX - r.left) / r.width));
    const y = Math.round(YEARS[0] + p*(YEARS[YEARS.length-1]-YEARS[0]));
    setYear(y);
  };
  useEffect(()=>{
    if (!drag) return;
    const mv = e=> setFromX(e.clientX);
    const up = ()=> setDrag(false);
    window.addEventListener("mousemove", mv);
    window.addEventListener("mouseup", up);
    return ()=>{ window.removeEventListener("mousemove", mv); window.removeEventListener("mouseup", up); };
  },[drag]);
  return (
    <div className="timeline">
      <div className="timeline-label">
        <span>Timeline · 年表</span>
        <span className="cur">showing ≤ {year}</span>
      </div>
      <div className="timeline-track" ref={trackRef}
           onMouseDown={e=>{ setDrag(true); setFromX(e.clientX); }}>
        <div className="timeline-line"/>
        <div className="timeline-fill" style={{width: `${pctOf(year)*100}%`}}/>
        {YEARS.map(y=>(
          <React.Fragment key={y}>
            <div className={"timeline-tick"+(y<=year?" active":"")} style={{left:`${pctOf(y)*100}%`}}/>
            <div className={"timeline-year"+(y===year?" active":"")} style={{left:`${pctOf(y)*100}%`}}>{y}</div>
          </React.Fragment>
        ))}
        <div className={"timeline-handle"+(drag?" dragging":"")}
             style={{left:`${pctOf(year)*100}%`}}/>
      </div>
    </div>
  );
}

function Tooltip({ node, x, y }){
  if (!node) return null;
  return (
    <div className={"tip on"} style={{ left:x+14, top:y+14 }}>
      <div className="tip-name">{node.label}{node.zh && <span className="tip-zh">{node.zh}</span>}</div>
      <div className="tip-meta">{node.kind} · {node.year} · {node.org}</div>
      <div className="tip-desc">{node.desc}</div>
    </div>
  );
}

function Drawer({ node, onClose, onOpen, data }){
  if (!node) return null;
  const related = useMemo(()=>{
    const r = new Set();
    data.edges.forEach(([a,b])=>{
      if (a===node.id) r.add(b);
      if (b===node.id) r.add(a);
    });
    return data.nodes.filter(n=>r.has(n.id));
  },[node, data]);
  const kindLabel = { model:"Foundation Model", tool:"Tool / Platform", company:"Company / Lab" }[node.kind];
  const field = data.fields.find(f=>f.id===node.field);
  return (
    <aside className={"drawer"+(node?" on":"")}>
      <button className="drawer-close" onClick={onClose}>×</button>
      <div className="drawer-eyebrow">{kindLabel} · {field?.name}</div>
      <h2 className="drawer-name">
        {node.label}
        {node.zh && <span className="zh">{node.zh}</span>}
      </h2>
      <div className="drawer-year">RELEASED · {node.year}</div>
      <hr/>
      <div className="drawer-field">
        <h5>Overview</h5>
        <p className="serif">{node.desc}</p>
      </div>
      <div className="drawer-field">
        <h5>Organization</h5>
        <p>{node.org}</p>
      </div>
      <div className="drawer-field">
        <h5>Key paper / product</h5>
        <p>{node.paper}</p>
      </div>
      {related.length > 0 && (
        <div className="drawer-field">
          <h5>Connected to</h5>
          <div className="related">
            {related.map(r=> <button key={r.id} onClick={()=>onOpen(r.id)}>{r.label}</button>)}
          </div>
        </div>
      )}
    </aside>
  );
}

Object.assign(window, { MapCanvas, Legend, Timeline, Tooltip, Drawer });
