'use client';

import { useEffect, useState, useCallback } from 'react';
import { SidebarNav } from '@/components/sidebar-nav';
import {
  vmApi,
  type VmHealthResult,
  type VmCheckLevel,
  type CronStatusResponse,
  type UnhealthyContainer,
  type OllamaModelsResponse,
} from '@/lib/api';
import {
  CheckCircle,
  AlertTriangle,
  XCircle,
  RefreshCw,
  HardDrive,
  Cpu,
  Database,
  Server,
  Activity,
  Layers,
  ScrollText,
  Trash2,
  Terminal,
  ChevronDown,
  ChevronUp,
  Clock,
  Bot,
  RotateCw,
  Gauge,
  Shield,
  Zap,
  MemoryStick,
} from 'lucide-react';

// ── Types ──────────────────────────────────────────────────────────────────

type Level = VmCheckLevel;

// ── Shared helpers ─────────────────────────────────────────────────────────

function levelColor(level: Level) {
  switch (level) {
    case 'OK':   return 'text-green-700  bg-green-50  border-green-200';
    case 'WARN': return 'text-yellow-700 bg-yellow-50 border-yellow-200';
    case 'CRIT': return 'text-red-700    bg-red-50    border-red-200';
  }
}

function levelBadge(level: Level) {
  switch (level) {
    case 'OK':   return 'bg-green-100  text-green-800';
    case 'WARN': return 'bg-yellow-100 text-yellow-800';
    case 'CRIT': return 'bg-red-100    text-red-800';
  }
}

function LevelIcon({ level, className = 'w-5 h-5' }: { level: Level; className?: string }) {
  switch (level) {
    case 'OK':   return <CheckCircle   className={`${className} text-green-600`} />;
    case 'WARN': return <AlertTriangle className={`${className} text-yellow-600`} />;
    case 'CRIT': return <XCircle       className={`${className} text-red-600`} />;
  }
}

function relativeTime(iso: string | null | undefined): string {
  if (!iso) return '—';
  const diff = Date.now() - new Date(iso).getTime();
  if (isNaN(diff)) return '—';
  const mins = Math.floor(diff / 60000);
  if (mins < 2)  return 'just now';
  if (mins < 60) return `${mins}m ago`;
  const hrs = Math.floor(mins / 60);
  if (hrs < 24)  return `${hrs}h ago`;
  const days = Math.floor(hrs / 24);
  return `${days}d ago`;
}

function formatDate(iso: string | null | undefined): string {
  if (!iso) return '—';
  const d = new Date(iso);
  if (isNaN(d.getTime())) return '—';
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
}

// ── Score card ─────────────────────────────────────────────────────────────

interface ScoreDim { label: string; pts: number; maxPts: number; detail: string }

function computeScore(
  health: VmHealthResult | null,
  unhealthyCount: number,
  cronData: CronStatusResponse | null,
): { total: number; dims: ScoreDim[] } {
  const checks = health?.checks ?? {};

  // 1. CPU efficiency — steal %
  const stealPct = parseFloat((checks.steal?.value ?? '0%').match(/^([\d.]+)/)?.[1] ?? '0');
  const cpuPts = stealPct < 2 ? 20 : stealPct < 5 ? 15 : stealPct < 10 ? 10 : 5;

  // 2. Memory pressure — available GB (first number in value, e.g. "11G / 15G")
  const ramAvailGb = parseFloat((checks.ram?.value ?? '0G').match(/^([\d.]+)/)?.[1] ?? '0');
  const ramPts = ramAvailGb > 6 ? 20 : ramAvailGb > 3 ? 15 : ramAvailGb > 1 ? 5 : 0;

  // 3. Disk health — used % (first number in value, e.g. "37% used, 123G free")
  const diskPct = parseInt((checks.disk?.value ?? '100%').match(/^(\d+)/)?.[1] ?? '100', 10);
  const diskPts = diskPct < 40 ? 15 : diskPct < 55 ? 10 : diskPct < 70 ? 5 : 0;

  // 4. Service health — unhealthy container count
  const svcPts = unhealthyCount === 0 ? 20 : unhealthyCount <= 2 ? 15 : unhealthyCount <= 5 ? 8 : 2;

  // 5. Maintenance hygiene — last successful cleanup
  let maintPts = 0;
  let maintDetail = 'no history';
  if (cronData?.recentRuns?.length) {
    const lastRun = cronData.recentRuns.find(r => r.success);
    if (lastRun) {
      const daysSince = (Date.now() - new Date(lastRun.timestamp).getTime()) / 86_400_000;
      maintPts = daysSince < 7 && lastRun.freedMB > 0 ? 15 : daysSince < 30 ? 8 : 0;
      maintDetail = `${Math.floor(daysSince)}d ago, freed ${lastRun.freedMB > 0 ? lastRun.freedMB + ' MB' : '0 MB'}`;
    }
  }

  // 6. LLM readiness — available RAM
  const llmPts = ramAvailGb > 8 ? 10 : ramAvailGb > 4 ? 7 : ramAvailGb > 2 ? 4 : 1;

  const total = cpuPts + ramPts + diskPts + svcPts + maintPts + llmPts;

  return {
    total,
    dims: [
      { label: 'CPU Efficiency',   pts: cpuPts,  maxPts: 20, detail: `${stealPct.toFixed(1)}% steal` },
      { label: 'Memory Pressure',  pts: ramPts,  maxPts: 20, detail: `${ramAvailGb}G available` },
      { label: 'Disk Health',      pts: diskPts, maxPts: 15, detail: `${diskPct}% used` },
      { label: 'Service Health',   pts: svcPts,  maxPts: 20, detail: `${unhealthyCount} unhealthy` },
      { label: 'Maintenance',      pts: maintPts, maxPts: 15, detail: maintDetail },
      { label: 'LLM Readiness',    pts: llmPts,  maxPts: 10, detail: `${ramAvailGb}G free` },
    ],
  };
}

function ScoreCard({
  health,
  unhealthyCount,
  cronData,
}: {
  health: VmHealthResult | null;
  unhealthyCount: number;
  cronData: CronStatusResponse | null;
}) {
  const { total, dims } = computeScore(health, unhealthyCount, cronData);
  const scoreColor = total >= 80 ? 'text-green-600' : total >= 60 ? 'text-yellow-600' : 'text-red-600';
  const scoreBg    = total >= 80 ? 'bg-green-50 border-green-200' : total >= 60 ? 'bg-yellow-50 border-yellow-200' : 'bg-red-50 border-red-200';

  return (
    <div className={`rounded-lg border p-5 ${scoreBg}`}>
      <div className="flex items-start gap-5">
        {/* Score gauge */}
        <div className="flex flex-col items-center gap-1 min-w-[80px]">
          <Gauge className="w-5 h-5 text-gray-400" />
          <span className={`text-5xl font-bold tabular-nums ${scoreColor}`}>{total}</span>
          <span className="text-xs text-gray-400 font-medium">/ 100</span>
        </div>

        {/* Dimension breakdown */}
        <div className="flex-1 grid grid-cols-2 sm:grid-cols-3 gap-x-6 gap-y-2">
          {dims.map(d => {
            const pct = Math.round((d.pts / d.maxPts) * 100);
            const barColor = pct >= 75 ? 'bg-green-400' : pct >= 50 ? 'bg-yellow-400' : 'bg-red-400';
            return (
              <div key={d.label}>
                <div className="flex items-center justify-between mb-0.5">
                  <span className="text-xs font-medium text-gray-600 truncate">{d.label}</span>
                  <span className="text-xs font-bold text-gray-700 ml-2 tabular-nums">{d.pts}/{d.maxPts}</span>
                </div>
                <div className="h-1.5 bg-gray-200 rounded-full overflow-hidden">
                  <div className={`h-full rounded-full ${barColor}`} style={{ width: `${pct}%` }} />
                </div>
                <p className="text-xs text-gray-400 mt-0.5">{d.detail}</p>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ── Unhealthy containers panel ─────────────────────────────────────────────

function UnhealthyContainersPanel({
  containers,
  onRestart,
  restarting,
}: {
  containers: UnhealthyContainer[];
  onRestart: (name: string) => Promise<void>;
  restarting: Set<string>;
}) {
  const [expanded, setExpanded] = useState<Set<string>>(new Set());

  const toggle = (name: string) =>
    setExpanded(prev => {
      const next = new Set(prev);
      next.has(name) ? next.delete(name) : next.add(name);
      return next;
    });

  if (containers.length === 0) return null;

  return (
    <div className="bg-white border border-yellow-200 rounded-lg overflow-hidden">
      <div className="px-6 py-4 bg-yellow-50 border-b border-yellow-200 flex items-center justify-between">
        <div className="flex items-center gap-2">
          <AlertTriangle className="w-5 h-5 text-yellow-600" />
          <span className="font-semibold text-yellow-900">
            {containers.length} Unhealthy Container{containers.length !== 1 ? 's' : ''}
          </span>
          <span className="text-xs text-yellow-600">process alive, health endpoint failing</span>
        </div>
      </div>

      <div className="divide-y divide-gray-100">
        {containers.map(c => (
          <div key={c.name}>
            <div className="px-6 py-3 flex items-center gap-3">
              <button
                onClick={() => toggle(c.name)}
                className="flex-1 flex items-center gap-3 text-left"
              >
                {expanded.has(c.name)
                  ? <ChevronUp   className="w-4 h-4 text-gray-400 flex-shrink-0" />
                  : <ChevronDown className="w-4 h-4 text-gray-400 flex-shrink-0" />}
                <span className="font-mono text-sm font-medium text-gray-900">{c.name}</span>
                <span className="text-xs text-gray-500">
                  unhealthy {relativeTime(c.unhealthySince)} · {c.restartCount} restarts
                </span>
              </button>
              <button
                onClick={() => onRestart(c.name)}
                disabled={restarting.has(c.name)}
                className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-blue-700 bg-blue-50 border border-blue-200 rounded-md hover:bg-blue-100 disabled:opacity-50 flex-shrink-0"
              >
                <RotateCw className={`w-3.5 h-3.5 ${restarting.has(c.name) ? 'animate-spin' : ''}`} />
                Restart
              </button>
            </div>

            {expanded.has(c.name) && c.lastHealthLogs.length > 0 && (
              <div className="px-6 pb-3">
                <p className="text-xs font-medium text-gray-500 mb-1">Last health check output:</p>
                <pre className="text-xs font-mono bg-gray-50 rounded p-2 whitespace-pre-wrap text-gray-700 max-h-32 overflow-y-auto">
                  {c.lastHealthLogs.join('\n') || '(no output)'}
                </pre>
              </div>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}

// ── Cron status panel ──────────────────────────────────────────────────────

function CronStatusPanel({ data }: { data: CronStatusResponse | null }) {
  const [expandedRun, setExpandedRun] = useState<string | null>(null);

  if (!data) {
    return (
      <div className="bg-white border border-gray-200 rounded-lg p-6">
        <div className="flex items-center gap-2 text-gray-400">
          <Clock className="w-5 h-5" />
          <span className="text-sm">Maintenance schedule not available</span>
        </div>
      </div>
    );
  }

  const { jobs, recentRuns } = data;

  return (
    <div className="bg-white border border-gray-200 rounded-lg overflow-hidden">
      <div className="px-6 py-4 border-b border-gray-100 flex items-center gap-2">
        <Clock className="w-5 h-5 text-gray-500" />
        <h2 className="font-semibold text-gray-900">Maintenance Schedule</h2>
        {recentRuns.length > 0 && (
          <span className="ml-auto text-xs text-gray-400">
            {recentRuns.length} run{recentRuns.length !== 1 ? 's' : ''} in log
          </span>
        )}
      </div>

      {/* Jobs table */}
      <div className="overflow-x-auto">
        <table className="w-full text-sm">
          <thead>
            <tr className="border-b border-gray-100 bg-gray-50">
              <th className="px-6 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wide">Job</th>
              <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wide">Schedule</th>
              <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wide">Last Run</th>
              <th className="px-4 py-2 text-right text-xs font-medium text-gray-500 uppercase tracking-wide">Freed</th>
              <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wide">Status</th>
              <th className="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wide">Next Run</th>
            </tr>
          </thead>
          <tbody className="divide-y divide-gray-50">
            {jobs.map(job => {
              const lr = job.lastRun;
              return (
                <tr key={job.name} className="hover:bg-gray-50">
                  <td className="px-6 py-3">
                    <p className="font-medium text-gray-900">{job.description}</p>
                    <p className="text-xs text-gray-400 font-mono">{job.name}</p>
                  </td>
                  <td className="px-4 py-3 font-mono text-xs text-gray-600">{job.schedule}</td>
                  <td className="px-4 py-3 text-gray-600">
                    {lr ? (
                      <span title={lr.timestamp}>{relativeTime(lr.timestamp)}</span>
                    ) : (
                      <span className="text-gray-400">never</span>
                    )}
                  </td>
                  <td className="px-4 py-3 text-right font-mono text-xs">
                    {lr && lr.freedMB > 0
                      ? <span className="text-green-700">+{lr.freedMB} MB</span>
                      : <span className="text-gray-400">—</span>}
                  </td>
                  <td className="px-4 py-3">
                    {lr ? (
                      <span className={`inline-flex items-center gap-1 text-xs font-medium ${lr.success ? 'text-green-700' : 'text-red-700'}`}>
                        {lr.success
                          ? <CheckCircle className="w-3.5 h-3.5" />
                          : <XCircle    className="w-3.5 h-3.5" />}
                        {lr.success ? 'OK' : 'Failed'}
                      </span>
                    ) : (
                      <span className="text-gray-400 text-xs">—</span>
                    )}
                  </td>
                  <td className="px-4 py-3 text-xs text-gray-500">
                    {job.nextRun ? (
                      <span title={job.nextRun}>{relativeTime(job.nextRun).replace('ago', '').trim() || formatDate(job.nextRun)}</span>
                    ) : '—'}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      {/* Recent runs — collapsible step log */}
      {recentRuns.length > 0 && (
        <div className="border-t border-gray-100">
          <div className="px-6 py-3">
            <p className="text-xs font-medium text-gray-500 uppercase tracking-wide mb-2">Recent Runs</p>
            <div className="space-y-1">
              {recentRuns.slice(0, 10).map((run, i) => (
                <div key={`${run.timestamp}-${i}`} className="rounded-md border border-gray-100 overflow-hidden">
                  <button
                    className="w-full flex items-center gap-3 px-4 py-2 text-left hover:bg-gray-50 text-sm"
                    onClick={() => setExpandedRun(expandedRun === run.timestamp ? null : run.timestamp)}
                  >
                    {run.success
                      ? <CheckCircle className="w-4 h-4 text-green-500 flex-shrink-0" />
                      : <XCircle    className="w-4 h-4 text-red-500 flex-shrink-0" />}
                    <span className="text-gray-600">{formatDate(run.timestamp)}</span>
                    <span className={`text-xs px-1.5 py-0.5 rounded font-medium ${run.mode === 'full' ? 'bg-orange-100 text-orange-700' : 'bg-blue-100 text-blue-700'}`}>
                      {run.mode}
                    </span>
                    {run.freedMB > 0 && (
                      <span className="text-xs text-green-700 font-medium">freed {run.freedMB} MB</span>
                    )}
                    <span className="ml-auto text-xs text-gray-400">{run.durationSecs}s</span>
                    {expandedRun === run.timestamp
                      ? <ChevronUp   className="w-3.5 h-3.5 text-gray-400" />
                      : <ChevronDown className="w-3.5 h-3.5 text-gray-400" />}
                  </button>
                  {expandedRun === run.timestamp && run.steps.length > 0 && (
                    <div className="px-4 pb-3 border-t border-gray-100">
                      <pre className="text-xs font-mono bg-gray-50 rounded p-2 mt-2 whitespace-pre-wrap text-gray-700 max-h-40 overflow-y-auto">
                        {run.steps.join('\n')}
                      </pre>
                    </div>
                  )}
                </div>
              ))}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Ollama / LLM panel ─────────────────────────────────────────────────────

function OllamaPanel({
  data,
  ramAvailGb,
  onUnload,
  unloading,
}: {
  data: OllamaModelsResponse | null;
  ramAvailGb: number;
  onUnload: (name: string) => Promise<void>;
  unloading: Set<string>;
}) {
  if (!data) return null;
  const { models, running } = data;
  if (models.length === 0 && running.length === 0) return null;

  return (
    <div className="bg-white border border-gray-200 rounded-lg overflow-hidden">
      <div className="px-6 py-4 border-b border-gray-100 flex items-center gap-2">
        <Bot className="w-5 h-5 text-gray-500" />
        <h2 className="font-semibold text-gray-900">LLM Models (Ollama)</h2>
        <span className="ml-auto text-xs text-gray-400">
          {models.length} on disk · {running.length} loaded
        </span>
      </div>

      {/* Currently loaded */}
      {running.length > 0 && (
        <div className="px-6 py-3 bg-purple-50 border-b border-purple-100">
          <p className="text-xs font-medium text-purple-700 uppercase tracking-wide mb-2">Currently Loaded</p>
          {running.map(r => {
            const ramAfterUnloadGb = ramAvailGb + r.sizeGB;
            const pressureAfter = ramAfterUnloadGb < 2;
            return (
              <div key={r.name} className="flex items-center gap-3">
                <div className="flex-1">
                  <span className="font-mono text-sm font-medium text-gray-900">{r.name}</span>
                  <span className="ml-2 text-xs text-gray-500">{r.sizeGB} GB · {r.processor || 'CPU'}</span>
                  {r.expiresAt && (
                    <span className="ml-2 text-xs text-gray-400">expires {relativeTime(r.expiresAt)}</span>
                  )}
                  {ramAvailGb < 4 && (
                    <span className="ml-2 text-xs text-yellow-700 bg-yellow-100 px-1.5 py-0.5 rounded">
                      low RAM — swap pressure likely
                    </span>
                  )}
                  {pressureAfter && (
                    <span className="ml-1 text-xs text-gray-400">(unloading frees {r.sizeGB} GB)</span>
                  )}
                </div>
                <button
                  onClick={() => onUnload(r.name)}
                  disabled={unloading.has(r.name)}
                  className="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-purple-700 bg-white border border-purple-200 rounded-md hover:bg-purple-50 disabled:opacity-50"
                >
                  {unloading.has(r.name)
                    ? <RefreshCw className="w-3.5 h-3.5 animate-spin" />
                    : <Zap className="w-3.5 h-3.5" />}
                  Unload
                </button>
              </div>
            );
          })}
        </div>
      )}

      {/* RAM bar */}
      {ramAvailGb > 0 && (
        <div className="px-6 py-3 border-b border-gray-100">
          <div className="flex items-center justify-between text-xs text-gray-500 mb-1">
            <span>RAM available</span>
            <span className="font-mono">{ramAvailGb.toFixed(1)} GB free</span>
          </div>
          <div className="h-2 bg-gray-200 rounded-full overflow-hidden">
            {running.map(r => (
              <div
                key={r.name}
                className="h-full bg-purple-400 rounded-full float-right"
                style={{ width: `${Math.min((r.sizeGB / 16) * 100, 100)}%` }}
                title={`${r.name}: ${r.sizeGB} GB`}
              />
            ))}
          </div>
          <p className="text-xs text-gray-400 mt-0.5">
            {running.length > 0
              ? `${running.reduce((s, r) => s + r.sizeGB, 0).toFixed(1)} GB used by loaded models`
              : 'No models loaded'}
          </p>
        </div>
      )}

      {/* All models */}
      <div className="divide-y divide-gray-50">
        {models.map(m => {
          const isLoaded = running.some(r => r.name === m.name);
          return (
            <div key={m.name} className="px-6 py-2.5 flex items-center gap-3">
              <MemoryStick className="w-4 h-4 text-gray-300 flex-shrink-0" />
              <span className="font-mono text-sm text-gray-700 flex-1">{m.name}</span>
              <span className="text-xs text-gray-400">{m.sizeGB} GB</span>
              {isLoaded && (
                <span className="text-xs bg-purple-100 text-purple-700 px-1.5 py-0.5 rounded font-medium">loaded</span>
              )}
              {m.modifiedAt && (
                <span className="text-xs text-gray-300" title={m.modifiedAt}>
                  {relativeTime(m.modifiedAt)}
                </span>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── Check card meta ────────────────────────────────────────────────────────

const CHECK_META: Record<string, { label: string; icon: React.ElementType }> = {
  disk:               { label: 'Disk',             icon: HardDrive },
  load:               { label: 'CPU Load',         icon: Cpu },
  steal:              { label: 'CPU Steal',        icon: Shield },
  ram:                { label: 'Memory',           icon: Database },
  swap:               { label: 'Swap',             icon: Server },
  container_loops:    { label: 'Crash Loops',      icon: Activity },
  container_health:   { label: 'Container Health', icon: Layers },
  docker_daemon:      { label: 'Docker Daemon',    icon: Activity },
  build_cache:        { label: 'Build Cache',      icon: Layers },
  docker_images:      { label: 'Docker Images',    icon: Layers },
  journal:            { label: 'Journal Logs',     icon: ScrollText },
  syslog:             { label: 'Syslog',           icon: ScrollText },
  failed_units:       { label: 'Systemd Units',    icon: Activity },
  cron_missing_paths: { label: 'Cron Paths',       icon: Clock },
};

const CHECK_ORDER = [
  'disk', 'load', 'steal', 'ram', 'swap',
  'container_loops', 'container_health', 'docker_daemon',
  'build_cache', 'docker_images',
  'journal', 'syslog',
  'failed_units', 'cron_missing_paths',
];

// ── Main page ──────────────────────────────────────────────────────────────

export default function VmHealthPage() {
  const [health,     setHealth]     = useState<VmHealthResult | null>(null);
  const [cronData,   setCronData]   = useState<CronStatusResponse | null>(null);
  const [unhealthy,  setUnhealthy]  = useState<UnhealthyContainer[]>([]);
  const [ollamaData, setOllamaData] = useState<OllamaModelsResponse | null>(null);
  const [cleanupLog, setCleanupLog] = useState<string>('');
  const [loading,    setLoading]    = useState(true);
  const [refreshing, setRefreshing] = useState(false);

  const [cleanupRunning, setCleanupRunning] = useState(false);
  const [cleanupResult,  setCleanupResult]  = useState<{ success: boolean; output: string } | null>(null);

  const [restarting, setRestarting] = useState<Set<string>>(new Set());
  const [restartMsg, setRestartMsg] = useState<{ name: string; ok: boolean; msg: string } | null>(null);

  const [unloading, setUnloading] = useState<Set<string>>(new Set());

  const [showLog, setShowLog] = useState(false);
  const [lastRefreshed, setLastRefreshed] = useState<Date | null>(null);

  const loadAll = useCallback(async () => {
    try {
      const [healthData, logData, cronResult, unhealthyResult, ollamaResult] = await Promise.allSettled([
        vmApi.getHealth(),
        vmApi.getCleanupLog(40),
        vmApi.getCronStatus(),
        vmApi.getUnhealthyContainers(),
        vmApi.getOllamaModels(),
      ]);

      if (healthData.status    === 'fulfilled') setHealth(healthData.value);
      if (logData.status       === 'fulfilled') setCleanupLog(logData.value.log);
      if (cronResult.status    === 'fulfilled') setCronData(cronResult.value);
      if (unhealthyResult.status === 'fulfilled') setUnhealthy(unhealthyResult.value.containers);
      if (ollamaResult.status  === 'fulfilled') setOllamaData(ollamaResult.value);

      setLastRefreshed(new Date());
    } catch (e) {
      console.error('Failed to load VM data:', e);
    } finally {
      setLoading(false);
      setRefreshing(false);
    }
  }, []);

  useEffect(() => {
    loadAll();
    const interval = setInterval(loadAll, 60_000);
    return () => clearInterval(interval);
  }, [loadAll]);

  const handleRefresh = () => { setRefreshing(true); loadAll(); };

  const handleCleanup = async (mode: 'weekly' | 'monthly' | 'dry-run') => {
    const msg =
      mode === 'monthly'  ? 'Run MONTHLY full cleanup? This removes build cache, pnpm store, old logs, and HOLD node_modules.' :
      mode === 'dry-run'  ? 'Run cleanup in DRY-RUN mode? Nothing will be deleted.' :
                            'Run weekly cleanup? This prunes Docker build cache, journal, apt, and .next/cache.';
    if (!confirm(msg)) return;
    setCleanupRunning(true);
    setCleanupResult(null);
    try {
      const result = await vmApi.runCleanup(mode);
      setCleanupResult(result);
      await loadAll();
    } catch (e) {
      setCleanupResult({ success: false, output: String(e) });
    } finally {
      setCleanupRunning(false);
    }
  };

  const handleRestart = async (name: string) => {
    setRestarting(prev => new Set(prev).add(name));
    setRestartMsg(null);
    try {
      const result = await vmApi.restartContainer(name);
      setRestartMsg({ name, ok: result.success, msg: result.message });
      if (result.success) {
        await new Promise(r => setTimeout(r, 3000));
        await loadAll();
      }
    } catch (e) {
      setRestartMsg({ name, ok: false, msg: String(e) });
    } finally {
      setRestarting(prev => { const s = new Set(prev); s.delete(name); return s; });
    }
  };

  const handleUnload = async (name: string) => {
    setUnloading(prev => new Set(prev).add(name));
    try {
      await vmApi.unloadOllamaModel(name);
      await loadAll();
    } catch (e) {
      console.error('Failed to unload model:', e);
    } finally {
      setUnloading(prev => { const s = new Set(prev); s.delete(name); return s; });
    }
  };

  // ── Loading ───────────────────────────────────────────────────────────────

  if (loading) {
    return (
      <div className="flex min-h-screen bg-gray-50">
        <SidebarNav />
        <main className="flex-1 flex items-center justify-center">
          <div className="text-gray-500">Loading VM health…</div>
        </main>
      </div>
    );
  }

  const overall    = health?.overall ?? 'CRIT';
  const checks     = health?.checks ?? {};
  const sortedKeys = [
    ...CHECK_ORDER.filter(k => k in checks),
    ...Object.keys(checks).filter(k => !CHECK_ORDER.includes(k)),
  ];
  const warnings = sortedKeys.filter(k => checks[k]?.level === 'WARN');
  const crits    = sortedKeys.filter(k => checks[k]?.level === 'CRIT');

  // Parse available RAM for Ollama panel RAM bar
  const ramAvailGb = parseFloat(
    (checks.ram?.value ?? '0G').match(/^([\d.]+)/)?.[1] ?? '0'
  );

  // ── Render ────────────────────────────────────────────────────────────────

  return (
    <div className="flex min-h-screen bg-gray-50">
      <SidebarNav />

      <main className="flex-1 min-w-0 overflow-y-auto">
        <div className="p-8 max-md:p-4 space-y-6">

          {/* ── Header ── */}
          <div className="flex items-center justify-between">
            <div>
              <h1 className="text-2xl font-bold text-gray-900">VM Health</h1>
              <p className="text-sm text-gray-500">
                {health?.hostname ?? 'srv1491630'} ·{' '}
                {lastRefreshed
                  ? `last checked ${lastRefreshed.toLocaleTimeString()}`
                  : 'checking…'}
              </p>
            </div>
            <button
              onClick={handleRefresh}
              disabled={refreshing}
              className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50"
            >
              <RefreshCw className={`w-4 h-4 ${refreshing ? 'animate-spin' : ''}`} />
              Refresh
            </button>
          </div>

          {/* ── Score card ── */}
          <ScoreCard health={health} unhealthyCount={unhealthy.length} cronData={cronData} />

          {/* ── Overall status banner ── */}
          <div className={`rounded-lg border p-4 flex items-center gap-3 ${levelColor(overall)}`}>
            <LevelIcon level={overall} className="w-6 h-6 flex-shrink-0" />
            <div className="flex-1">
              <p className="font-semibold">
                {overall === 'OK'
                  ? 'All checks passing'
                  : overall === 'WARN'
                    ? `${warnings.length} warning${warnings.length !== 1 ? 's' : ''}`
                    : `${crits.length} critical issue${crits.length !== 1 ? 's' : ''}`}
              </p>
              {health?.error && (
                <p className="text-sm mt-1 opacity-80">{health.error}</p>
              )}
              {(crits.length > 0 || warnings.length > 0) && !health?.error && (
                <p className="text-sm mt-1 opacity-80">
                  {[...crits, ...warnings].map(k => checks[k]?.message).join(' · ')}
                </p>
              )}
            </div>
            <span className={`px-3 py-1 rounded-full text-sm font-bold ${levelBadge(overall)}`}>
              {overall}
            </span>
          </div>

          {/* ── Check cards grid ── */}
          <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4">
            {sortedKeys.map(key => {
              const check = checks[key];
              if (!check) return null;
              const meta = CHECK_META[key] ?? { label: key, icon: Activity };
              const Icon = meta.icon;
              return (
                <div key={key} className={`rounded-lg border p-4 ${levelColor(check.level)}`}>
                  <div className="flex items-start gap-3">
                    <div className="mt-0.5">
                      <LevelIcon level={check.level} />
                    </div>
                    <div className="flex-1 min-w-0">
                      <div className="flex items-center gap-2 mb-1">
                        <Icon className="w-4 h-4 opacity-60 flex-shrink-0" />
                        <span className="text-sm font-semibold">{meta.label}</span>
                        <span className={`ml-auto px-2 py-0.5 rounded text-xs font-bold ${levelBadge(check.level)}`}>
                          {check.level}
                        </span>
                      </div>
                      <p className="text-sm leading-snug">{check.message}</p>
                      <p className="text-xs opacity-60 mt-1 font-mono truncate">{check.value}</p>
                    </div>
                  </div>
                </div>
              );
            })}
          </div>

          {/* ── Restart feedback ── */}
          {restartMsg && (
            <div className={`rounded-lg border p-3 flex items-center gap-2 text-sm ${restartMsg.ok ? 'bg-green-50 border-green-200 text-green-800' : 'bg-red-50 border-red-200 text-red-800'}`}>
              {restartMsg.ok
                ? <CheckCircle className="w-4 h-4 flex-shrink-0" />
                : <XCircle    className="w-4 h-4 flex-shrink-0" />}
              <span><strong>{restartMsg.name}:</strong> {restartMsg.msg}</span>
              <button onClick={() => setRestartMsg(null)} className="ml-auto text-gray-400 hover:text-gray-600">✕</button>
            </div>
          )}

          {/* ── Unhealthy containers ── */}
          <UnhealthyContainersPanel
            containers={unhealthy}
            onRestart={handleRestart}
            restarting={restarting}
          />

          {/* ── Cron status ── */}
          <CronStatusPanel data={cronData} />

          {/* ── Ollama ── */}
          <OllamaPanel
            data={ollamaData}
            ramAvailGb={ramAvailGb}
            onUnload={handleUnload}
            unloading={unloading}
          />

          {/* ── Cleanup section ── */}
          <div className="bg-white border border-gray-200 rounded-lg p-6">
            <div className="flex items-center gap-3 mb-5">
              <Trash2 className="w-5 h-5 text-gray-500" />
              <div>
                <h2 className="text-lg font-semibold text-gray-900">VM Cleanup</h2>
                <p className="text-sm text-gray-500">
                  Cron runs automatically: daily build-cache prune, weekly cleanup, monthly full cleanup.
                  Use buttons below to trigger manually.
                </p>
              </div>
            </div>

            {cleanupResult && (
              <div className={`rounded-lg border p-4 mb-4 ${cleanupResult.success ? 'bg-green-50 border-green-200' : 'bg-red-50 border-red-200'}`}>
                <div className="flex items-center gap-2 mb-2">
                  {cleanupResult.success
                    ? <CheckCircle className="w-4 h-4 text-green-600" />
                    : <XCircle    className="w-4 h-4 text-red-600" />}
                  <span className={`text-sm font-medium ${cleanupResult.success ? 'text-green-800' : 'text-red-800'}`}>
                    {cleanupResult.success ? 'Cleanup completed' : 'Cleanup failed'}
                  </span>
                  <button onClick={() => setCleanupResult(null)} className="ml-auto text-gray-400 hover:text-gray-600 text-xs">
                    Dismiss
                  </button>
                </div>
                {cleanupResult.output && (
                  <pre className="text-xs font-mono whitespace-pre-wrap text-gray-700 bg-white/60 rounded p-2 max-h-48 overflow-y-auto">
                    {cleanupResult.output}
                  </pre>
                )}
              </div>
            )}

            <div className="flex flex-wrap gap-3">
              <button
                onClick={() => handleCleanup('dry-run')}
                disabled={cleanupRunning}
                className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-700 bg-gray-50 border border-gray-300 rounded-md hover:bg-gray-100 disabled:opacity-50"
              >
                <Terminal className="w-4 h-4" />
                Dry Run
              </button>
              <button
                onClick={() => handleCleanup('weekly')}
                disabled={cleanupRunning}
                className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-blue-700 bg-blue-50 border border-blue-300 rounded-md hover:bg-blue-100 disabled:opacity-50"
              >
                {cleanupRunning
                  ? <RefreshCw className="w-4 h-4 animate-spin" />
                  : <Trash2 className="w-4 h-4" />}
                Weekly Cleanup
              </button>
              <button
                onClick={() => handleCleanup('monthly')}
                disabled={cleanupRunning}
                className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-orange-700 bg-orange-50 border border-orange-300 rounded-md hover:bg-orange-100 disabled:opacity-50"
              >
                {cleanupRunning
                  ? <RefreshCw className="w-4 h-4 animate-spin" />
                  : <Trash2 className="w-4 h-4" />}
                Monthly Full Cleanup
              </button>
            </div>
          </div>

          {/* ── Cleanup log ── */}
          {cleanupLog && (
            <div className="bg-white border border-gray-200 rounded-lg overflow-hidden">
              <button
                className="w-full flex items-center justify-between px-6 py-4 text-left"
                onClick={() => setShowLog(v => !v)}
              >
                <div className="flex items-center gap-2">
                  <ScrollText className="w-5 h-5 text-gray-500" />
                  <span className="font-semibold text-gray-900">Cleanup Log</span>
                  <span className="text-xs text-gray-400">(last 40 lines of /var/log/vm-cleanup.log)</span>
                </div>
                {showLog
                  ? <ChevronUp   className="w-4 h-4 text-gray-400" />
                  : <ChevronDown className="w-4 h-4 text-gray-400" />}
              </button>
              {showLog && (
                <div className="border-t border-gray-100 px-6 py-4">
                  <pre className="text-xs font-mono whitespace-pre-wrap text-gray-700 bg-gray-50 rounded p-3 max-h-80 overflow-y-auto">
                    {cleanupLog}
                  </pre>
                </div>
              )}
            </div>
          )}

        </div>
      </main>
    </div>
  );
}
