// Draggable node with avatar
function Node({
  node, selected, onSelect, onDrag, onDragEnd,
  onStartConnect, onFinishConnect, onDelete,
  dragging, worldZoom, underAttack
}){
  const ref = React.useRef(null);
  const moveState = React.useRef(null);

  const onPointerDown = (e) => {
    if (e.target.closest('.connect-handle')) return;
    if (e.target.closest('.delete-btn')) return;
    e.stopPropagation();
    onSelect(node.id);
    const startX = e.clientX, startY = e.clientY;
    const ox = node.x, oy = node.y;
    moveState.current = { startX, startY, ox, oy, moved:false };
    e.currentTarget.setPointerCapture(e.pointerId);
  };

  const onPointerMove = (e) => {
    if (!moveState.current) return;
    const { startX, startY, ox, oy } = moveState.current;
    const dx = (e.clientX - startX) / worldZoom;
    const dy = (e.clientY - startY) / worldZoom;
    if (Math.abs(dx)+Math.abs(dy) > 2) moveState.current.moved = true;
    onDrag(node.id, ox+dx, oy+dy);
  };

  const onPointerUp = (e) => {
    if (!moveState.current) return;
    const moved = moveState.current.moved;
    moveState.current = null;
    onDragEnd(node.id, moved);
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch(_){}
  };

  const handleConnectDown = (e) => {
    e.stopPropagation();
    e.preventDefault();
    onStartConnect(node.id, e);
  };

  const handleFinishUp = (e) => {
    // If currently connecting, finish here. Parent handles routing on pointerup too.
    onFinishConnect(node.id);
  };

  const isBranch = node.variant === 'branch';
  const shapeClass = isBranch ? 'circle' : (node.shape || 'circle');
  const avatarStyle = { background: node.color || '#fff' };

  let inner;
  if (isBranch){
    // Branch nodes are just a colored dot with an optional emoji. No image,
    // no fallback initial — keeps them visually "minor" vs full cards.
    inner = node.emoji ? <div className="emoji">{node.emoji}</div> : null;
  } else if (node.image){
    inner = <img src={node.image} alt="" draggable="false" crossOrigin="anonymous"/>;
  } else if (node.emoji){
    inner = <div className="emoji">{node.emoji}</div>;
  } else {
    const ini = (node.name||'?').trim().charAt(0).toUpperCase();
    inner = <div className="initial" style={{color: contrastInk(node.color)}}>{ini}</div>;
  }

  return (
    <div
      ref={ref}
      className={`node${selected?' selected':''}${dragging?' dragging':''}${isBranch?' branch':''}${(node.effect||(node.aura?'buff':'none'))==='damage'?' under-attack':''}`}
      style={{ left:node.x, top:node.y }}
      onPointerDown={onPointerDown}
      onPointerMove={onPointerMove}
      onPointerUp={onPointerUp}
      onPointerCancel={onPointerUp}
    >
      {/* Effect aura: particles that visualize a buff / damage / status state on the card.
          Legacy boolean node.aura is treated as 'buff' for backward compatibility. */}
      {(() => {
        const effect = node.effect || (node.aura ? 'buff' : 'none');
        if (effect === 'none') return null;
        const palette = {
          buff:   '#E9C46A',  // gold
          damage: '#FF4545',  // rust red
          status: '#C77DFF',  // arcane purple
          heal:   '#7FE3A9',  // healing green
          shield: '#6DD3F0',  // shield cyan
        };
        const color = palette[effect] || '#E9C46A';
        return (
          <div className={`node-aura effect-${effect}`} style={{color}}>
            {[0,1,2,3,4,5].map(i => (
              <span key={i} className="particle"
                    style={{
                      left: `${28 + (i*13)%48}%`,
                      animationDelay: `${i*0.42}s`,
                      '--drift': `${(i%2?1:-1) * ((i*5)%14)}px`,
                      '--dx': `${((i*37)%7 - 3) * 8}px`,
                      '--dy': `${((i*29)%5 - 2) * 8}px`,
                    }}/>
            ))}
          </div>
        );
      })()}
      <div
        className={`avatar ${shapeClass==='square'?'square':shapeClass==='bubble'?'bubble':''}`}
        style={avatarStyle}
        onPointerUp={handleFinishUp}
      >
        {inner}
      </div>
      <div className={`ring-wrap ${shapeClass==='square'?'square':shapeClass==='bubble'?'bubble':''}`}>
        <div className={`ring ${shapeClass==='square'?'square':shapeClass==='bubble'?'bubble':''}`}></div>
        <div className={`ring2 ${shapeClass==='square'?'square':shapeClass==='bubble'?'bubble':''}`}></div>
      </div>
      {(node.name || node.subtitle) ? (
        <div className={`labels${node.labelPos && node.labelPos !== 'bottom' ? ' pos-' + node.labelPos : ''}`}>
          {node.name ? <div className="name">{node.name}</div> : null}
          {node.subtitle ? <div className="subtitle">{node.subtitle}</div> : null}
        </div>
      ) : null}

      <div
        className="connect-handle"
        onPointerDown={handleConnectDown}
        title="ドラッグで矢印を作成"
      >＋</div>
      <div
        className="delete-btn"
        onClick={(e)=>{e.stopPropagation(); onDelete(node.id);}}
        title="削除"
      >×</div>
    </div>
  );
}

function contrastInk(bg){
  if (!bg) return '#fff';
  const m = bg.match(/^#([0-9a-f]{6})$/i);
  if (!m) return '#fff';
  const r = parseInt(m[1].slice(0,2),16);
  const g = parseInt(m[1].slice(2,4),16);
  const b = parseInt(m[1].slice(4,6),16);
  const L = (0.299*r+0.587*g+0.114*b)/255;
  return L>0.6 ? '#3a2c3a' : '#fff';
}

window.Node = Node;
window.contrastInk = contrastInk;
