import { useNodeStatus, type NodeStatus } from '../../hooks/useNodeStatus';
import { useNodeAnimationState } from '../../hooks/useNodeAnimationState';
import { useActivePet } from '../../hooks/useActivePet';
import { usePetFrameAnalysis } from '../../hooks/usePetFrameAnalysis';
import { PetSprite } from '../pets/PetSprite';

/**
 * Side Info Panel widget that surfaces the BackendStatusRegistry feed:
 * for every direct worker and every proxy backend, one row with Pet
 * sprite, status icon, slots, model, throughput.
 *
 * Polling cadence matches the server-side registry tick (5s) so the
 * client cache stays roughly aligned with the cache the server is
 * already maintaining — see hooks/useNodeStatus for the rationale.
 */
export function NodeStatusWidget() {
  const { nodes, isLoading, isError, isUnavailable } = useNodeStatus();

  if (isLoading) return <div className="text-xs text-slate-500 p-3">読み込み中...</div>;
  if (isUnavailable) {
    return (
      <div className="text-xs text-slate-500 p-3">
        node-status registry が未構成です。<br />
        config.yaml の provider.workers を確認してください。
      </div>
    );
  }
  if (isError) return <div className="text-xs text-red-600 p-3">取得に失敗しました</div>;
  if (nodes.length === 0) {
    return (
      <div className="text-xs text-slate-500 p-3">
        ノードが登録されていません。<br />
        config.yaml の provider.workers を確認してください。
      </div>
    );
  }

  return (
    <div className="flex flex-col gap-1 p-2 overflow-auto h-full">
      {nodes.map((n) => (
        <NodeRow key={`${n.workerId}|${n.nodeId}`} node={n} />
      ))}
    </div>
  );
}

function statusEmoji(node: NodeStatus): { icon: string; label: string; color: string } {
  if (!node.online) return { icon: '⚫', label: 'offline', color: 'text-slate-500' };
  if (node.totalSlots > 0 && node.busySlots >= node.totalSlots) {
    return { icon: '🔴', label: 'full', color: 'text-rose-600' };
  }
  if (node.busySlots > 0) return { icon: '🟡', label: 'busy', color: 'text-amber-600' };
  return { icon: '🟢', label: 'idle', color: 'text-emerald-600' };
}

function usePrefersReducedMotion(): boolean {
  if (typeof window === 'undefined' || !window.matchMedia) return false;
  return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}

function NodeRow({ node }: { node: NodeStatus }) {
  // The Pet selection logic in useActivePet already prefers
  // workerPets[backendId] over workerPets[workerId]; passing nodeId as
  // the "backend" argument hands the resolver the most specific key.
  const { data: pet } = useActivePet(node.workerId, node.nodeId);
  const framesPerRow = usePetFrameAnalysis(
    pet?.spriteUrl ?? null,
    pet?.gridCols ?? null,
    pet?.gridRows ?? null,
  );
  const systemReducedMotion = usePrefersReducedMotion();
  const petReducedMotion = (pet?.settings as { reducedMotion?: boolean } | undefined)?.reducedMotion ?? false;
  const reducedMotion = petReducedMotion || systemReducedMotion;

  const { icon, label, color } = statusEmoji(node);
  // Phase C: derive the animation state through useNodeAnimationState so
  // both this widget and the ChatPetOverlay see the same idle/running
  // decision against the registry feed. The hook reads the shared
  // useNodeStatus query (React Query dedups), so N rows here don't
  // multiply polling traffic.
  const petState = useNodeAnimationState(node.nodeId);
  const showPet = pet?.pet && pet.imageUrl;

  return (
    <div className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-surface-2">
      <div className="w-8 h-8 flex-shrink-0 flex items-center justify-center">
        {showPet ? (
          <PetSprite
            name={pet.pet!.name}
            imageUrl={pet.imageUrl}
            frameWidth={pet.frameWidth}
            frameHeight={pet.frameHeight}
            gridCols={pet.gridCols}
            gridRows={pet.gridRows}
            framesPerRow={framesPerRow}
            state={petState}
            size={32}
            reducedMotion={reducedMotion}
          />
        ) : (
          <span className={`text-base ${color}`} aria-label={label}>{icon}</span>
        )}
      </div>
      <div className="flex-1 min-w-0">
        <div className="text-xs font-medium text-slate-800 truncate flex items-center gap-1.5">
          {node.nodeId}
          {node.source === 'proxy' && (
            <span className="text-[9px] uppercase tracking-wide text-slate-400">via {node.workerId}</span>
          )}
        </div>
        <div className="text-[10px] text-slate-500 font-mono truncate">
          {node.loadedModel ?? '-'}
          {node.lastProbeError && (
            <span className="text-rose-500 ml-1" title={node.lastProbeError}>(probe error)</span>
          )}
        </div>
      </div>
      <div className="flex flex-col items-end text-[11px] font-mono leading-tight">
        <div className="flex items-center gap-1">
          <span className={color}>{icon}</span>
          {node.totalSlots > 0
            ? <span className="text-slate-600">{node.busySlots}/{node.totalSlots}</span>
            : <span className="text-slate-400">-</span>}
        </div>
        <span className="text-slate-400">
          {node.throughputTps != null ? `${node.throughputTps.toFixed(0)} tok/s` : ' '}
        </span>
      </div>
    </div>
  );
}
