import { motion, AnimatePresence } from 'framer-motion';
import { useState, useMemo } from 'react';
import { ControlFleetHost, ControlPerfSample } from '@/hooks/useControlStream';
import { useReducedMotion } from '@/hooks/useReducedMotion';
import { VramGauge } from './VramGauge';
import { TtlRing } from './TtlRing';
import { PerfChart } from './PerfChart';
import { cn } from '@/lib/utils';
import type { GpuData } from './FleetTab';
import { Play, Eraser, Check, Loader2, AlertTriangle, Circle, ChevronDown, ChevronRight } from 'lucide-react';

interface HostCardProps {
  host: ControlFleetHost;
  gpuData: GpuData | null;
  perfSamples?: ControlPerfSample[];
}

// C1: redundant (non-color) signifier per model state for color-blind users.
const STATE_ICON: Record<string, typeof Check> = {
  ready: Check,
  starting: Loader2,
  stopping: Loader2,
  error: AlertTriangle,
  stopped: Circle,
  down: Circle,
};

const STATE_COLORS: Record<string, { bg: string; glowVar: string; animate: boolean }> = {
  starting: { bg: 'bg-amber-500', glowVar: '--glow-amber', animate: true },
  ready: { bg: 'bg-green-500', glowVar: '--glow-green', animate: false },
  error: { bg: 'bg-red-500', glowVar: '--glow-red', animate: false },
  down: { bg: 'bg-gray-500', glowVar: '--glow-gray', animate: false },
  stopped: { bg: 'bg-gray-400', glowVar: '--glow-gray', animate: false },
  stopping: { bg: 'bg-amber-400', glowVar: '--glow-amber', animate: true },
};

const FALLBACK_STATE = { bg: 'bg-gray-500', glowVar: '--glow-gray', animate: false };

function relTime(iso: string | null): string {
  if (!iso) return '';
  const diff = Date.now() - new Date(iso).getTime();
  const seconds = Math.floor(diff / 1000);
  if (seconds < 60) return `${seconds}s ago`;
  const minutes = Math.floor(seconds / 60);
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  return `${days}d ago`;
}

function livenessLabel(state: string): string {
  switch (state) {
    case 'connected': return 'connected';
    case 'down': return 'down';
    default: return state;
  }
}

function getGlowColor(glowVar: string): string {
  return getComputedStyle(document.documentElement).getPropertyValue(glowVar).trim();
}

export function HostCard({ host, gpuData, perfSamples = [] }: HostCardProps) {
  const reducedMotion = useReducedMotion();
  const [showPerf, setShowPerf] = useState(false);

  // B2: build perf history series for this host from buffered samples.
  const perf = useMemo(() => {
    const mine = perfSamples.filter((s) => s.providerId === host.providerId).slice(-120);
    const timestamps = mine.map((s) => s.ts);
    const num = (g: unknown, k: string): number => {
      const v = (g as Record<string, unknown> | null)?.[k];
      return typeof v === 'number' ? v : 0;
    };
    return {
      timestamps,
      hasData: mine.length > 1,
      series: [
        { name: 'VRAM MB', data: mine.map((s) => num(s.gpu, 'vram_used')), color: '#60a5fa' },
        { name: 'Temp C', data: mine.map((s) => num(s.gpu, 'temperature')), color: '#f87171' },
        { name: 'Power W', data: mine.map((s) => num(s.gpu, 'power')), color: '#fbbf24' },
      ],
    };
  }, [perfSamples, host.providerId]);
  const livenessKey = host.liveness === 'connected' ? 'ready' : host.liveness;
  const stateConfig = STATE_COLORS[livenessKey] ?? FALLBACK_STATE;
  // AUD1: getComputedStyle is a forced style read; memoize so it doesn't fire on
  // every WS-delta re-render (only the theme token changes it, which is rare).
  const glowColor = useMemo(() => getGlowColor(stateConfig.glowVar), [stateConfig.glowVar]);

  const vramUsed = gpuData?.vram_used ?? 0;
  const vramTotal = gpuData?.vram_total ?? 0;
  const gpuTemp = gpuData?.temperature ?? null;
  const gpuPower = gpuData?.power ?? null;

  return (
    <motion.div
      layout
      initial={reducedMotion ? undefined : { opacity: 0, y: 12 }}
      animate={{ opacity: 1, y: 0 }}
      exit={reducedMotion ? undefined : { opacity: 0, scale: 0.97 }}
      transition={reducedMotion ? undefined : { type: 'spring', stiffness: 300, damping: 25 }}
      className={cn(
        'rounded-xl border border-border/60 bg-card p-4',
        'shadow-sm',
      )}
    >
      {/* Header: provider ID + liveness chip + last seen */}
      <div className="flex items-center gap-3 mb-3">
        <h2 className="text-sm font-semibold tracking-tight">{host.providerId}</h2>

        <motion.div
          className={cn(
            'inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-[11px] font-medium',
            'border border-border/40',
          )}
          animate={
            reducedMotion
              ? undefined
              : stateConfig.animate
                ? { boxShadow: ['0 0 0px transparent', `0 0 8px ${glowColor}33`, '0 0 0px transparent'] }
                : { boxShadow: [`0 0 6px ${glowColor}33`] }
          }
          transition={
            reducedMotion
              ? undefined
              : { duration: 1.5, repeat: stateConfig.animate ? Infinity : 0 }
          }
        >
          <span
            className={cn(
              'w-1.5 h-1.5 rounded-full',
              stateConfig.bg,
            )}
          />
          <span className="capitalize">{livenessLabel(host.liveness)}</span>
        </motion.div>

        {host.liveness === 'down' && host.lastSeenAt && (
          <span className="text-[11px] text-muted-foreground">
            last seen {relTime(host.lastSeenAt)}
          </span>
        )}

        {perf.hasData && (
          <button
            type="button"
            onClick={() => setShowPerf((v) => !v)}
            className="text-[10px] text-muted-foreground ml-auto inline-flex items-center gap-0.5 hover:text-foreground"
          >
            {showPerf ? <ChevronDown className="size-3" /> : <ChevronRight className="size-3" />}
            perf
          </button>
        )}
      </div>

      <div className="flex flex-col lg:flex-row gap-4">
        {/* Left: VRAM gauge + GPU readouts */}
        <div className="flex items-start gap-4 shrink-0">
          {vramTotal > 0 ? (
            <VramGauge used={vramUsed} total={vramTotal} size={110} />
          ) : (
            <div className="w-[110px] h-[110px] flex items-center justify-center text-[11px] text-muted-foreground">
              no GPU data
            </div>
          )}

          {/* GPU readouts */}
          <div className="space-y-2 pt-2">
            {gpuTemp != null && (
              <GpuReadout label="Temp" value={`${gpuTemp.toFixed(0)}\u00B0C`} />
            )}
            {gpuPower != null && (
              <GpuReadout label="Power" value={`${gpuPower.toFixed(0)}W`} />
            )}
            <GpuReadout label="VRAM" value={`${vramUsed.toFixed(0)} / ${vramTotal.toFixed(0)} MB`} />
          </div>
        </div>

        {/* Right: model chips + TTL rings */}
        <div className="flex-1 min-w-0">
          <div className="text-[10px] uppercase tracking-wider text-muted-foreground mb-2 font-medium">
            Models
          </div>
          <div className="flex flex-wrap gap-2">
            <AnimatePresence mode="popLayout">
              {host.models.map((m) => (
                <ModelChip key={`${m.model}-${m.state}`} model={m} providerId={host.providerId} />
              ))}
            </AnimatePresence>
          </div>

          {/* TTL rings */}
          {host.models.some((m) => m.ttlDeadline) && (
            <div className="mt-3">
              <div className="text-[10px] uppercase tracking-wider text-muted-foreground mb-2 font-medium">
                TTL
              </div>
              <div className="flex gap-3">
                {host.models.filter((m) => m.ttlDeadline).map((m) => (
                  <div key={`ttl-${m.model}`} className="flex flex-col items-center gap-1">
                    <TtlRing deadline={m.ttlDeadline} size={64} />
                    <span className="text-[10px] text-muted-foreground truncate max-w-[80px]">
                      {m.model}
                    </span>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      </div>

      {/* B2: perf history (collapsible) */}
      {showPerf && perf.hasData && (
        <div className="mt-4 border-t border-border/30 pt-3">
          <PerfChart series={perf.series} timestamps={perf.timestamps} height={180} />
        </div>
      )}
    </motion.div>
  );
}

function GpuReadout({ label, value }: { label: string; value: string }) {
  return (
    <div className="flex items-baseline gap-1.5">
      <span className="text-[10px] uppercase tracking-wider text-muted-foreground font-medium">
        {label}
      </span>
      <span className="text-sm font-bold font-[Orbitron] tabular-nums text-foreground">
        {value}
      </span>
    </div>
  );
}

interface ModelChipProps {
  model: {
    model: string;
    state: string;
    ts: string;
    ttlDeadline: string | null;
    inflight: number;
  };
  providerId: string;
}

function ModelChip({ model, providerId }: ModelChipProps) {
  const reducedMotion = useReducedMotion();
  const StateIcon = STATE_ICON[model.state] ?? Circle;
  const spin = model.state === 'starting' || model.state === 'stopping';
  const [actionError, setActionError] = useState<string | null>(null);
  const [confirmUnload, setConfirmUnload] = useState(false);

  // P2.2: Optimistic UI — API calls only, no local state mutation.
  // The control_fleet delta from WS updates the UI.
  const handleWarm = async () => {
    try {
      const res = await fetch('/api/control/action/submit', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ type: 'warm', providerId, model: model.model }),
      });
      if (!res.ok) {
        const data = await res.json().catch(() => ({}));
        setActionError(data.error || `Warm failed: ${res.status}`);
        setTimeout(() => setActionError(null), 3000);
      }
    } catch {
      setActionError('Network error');
      setTimeout(() => setActionError(null), 3000);
    }
  };

  const handleUnload = async (confirmed: boolean) => {
    try {
      const res = await fetch('/api/control/action/submit', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          type: 'unload',
          providerId,
          model: model.model,
          confirmed,
        }),
      });
      if (!res.ok) {
        const data = await res.json().catch(() => ({}));
        if (data.requiresConfirmation) {
          setConfirmUnload(true);
          return;
        }
        setActionError(data.error || `Unload failed: ${res.status}`);
        setTimeout(() => setActionError(null), 3000);
      } else {
        setConfirmUnload(false);
      }
    } catch {
      setActionError('Network error');
      setTimeout(() => setActionError(null), 3000);
    }
  };

  const handleConfirmedUnload = async () => {
    await handleUnload(true);
    setConfirmUnload(false);
  };

  return (
    <motion.span
      layout
      initial={reducedMotion ? undefined : { scale: 0.8, opacity: 0 }}
      animate={{ scale: 1, opacity: 1 }}
      exit={reducedMotion ? undefined : { scale: 0.8, opacity: 0 }}
      transition={reducedMotion ? undefined : { type: 'spring', stiffness: 400, damping: 20 }}
      className={cn(
        'relative inline-flex items-center gap-1.5 px-2 py-1 rounded-md text-xs',
        'border border-border/40 bg-muted/30',
        'font-medium',
      )}
    >
      <StateIcon
        aria-label={model.state}
        className={cn(
          'size-3 shrink-0',
          spin && 'animate-spin',
          model.state === 'ready' && 'text-green-400',
          model.state === 'error' && 'text-red-400',
          (model.state === 'starting' || model.state === 'stopping') && 'text-amber-400',
          (model.state === 'stopped' || model.state === 'down') && 'text-gray-400',
        )}
      />
      <span className="truncate max-w-[160px]" title={`${model.model} (${model.state})`}>{model.model}</span>
      {model.inflight > 0 && (
        <span className="text-[10px] text-muted-foreground ml-0.5">
          ({model.inflight})
        </span>
      )}

      {/* Action buttons — fire-and-forget, UI updates from control_fleet delta */}
      <button
        type="button"
        onClick={handleWarm}
        className="p-0.5 rounded hover:bg-muted/50 text-muted-foreground hover:text-foreground transition-colors"
        title={`Warm ${model.model}`}
      >
        <Play className="size-2.5" />
      </button>
      <button
        type="button"
        onClick={() => handleUnload(false)}
        className="p-0.5 rounded hover:bg-muted/50 text-muted-foreground hover:text-red-400 transition-colors"
        title={`Unload ${model.model}`}
      >
        <Eraser className="size-2.5" />
      </button>

      {actionError && (
        <span className="text-[9px] text-red-400 absolute -top-4 left-0 whitespace-nowrap">
          {actionError}
        </span>
      )}

      {confirmUnload && (
        <div className="absolute top-full left-0 mt-1 z-10 bg-background border border-border rounded-md p-2 shadow-lg flex flex-col gap-1 min-w-[180px]">
          <p className="text-[11px] text-foreground">
            Model has active requests. Force unload?
          </p>
          <div className="flex gap-1">
            <button
              type="button"
              onClick={handleConfirmedUnload}
              className="px-2 py-0.5 text-[10px] rounded bg-red-500/20 text-red-400 hover:bg-red-500/30 transition-colors"
            >
              Force unload
            </button>
            <button
              type="button"
              onClick={() => setConfirmUnload(false)}
              className="px-2 py-0.5 text-[10px] rounded bg-muted/30 text-muted-foreground hover:text-foreground transition-colors"
            >
              Cancel
            </button>
          </div>
        </div>
      )}
    </motion.span>
  );
}
