import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid } from 'recharts';
import { Card, CardHeader, CardTitle, CardContent } from './ui/card';
import { Button } from './ui/button';
import { Input } from './ui/input';
import { Label } from './ui/label';
import { Alert, AlertDescription } from './ui/alert';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import {
  ShieldAlert, Loader2, AlertCircle, CheckCircle, Ban, X, Trash2,
  BookOpen, Shield, Activity, Zap, RefreshCw, Globe,
} from 'lucide-react';
import { Badge } from './ui/badge';
// Instance provisioning removed — Central-only mode
import { useCrowdSec } from '../hooks/useCrowdSec';
import type { AddDecisionRequest, CrowdSecAlert } from '../services/api/networking';

type Timescale = '1h' | '6h' | '24h' | '7d';

const TIMESCALES: { key: Timescale; label: string; ms: number; bucketMs: number }[] = [
  { key: '1h',  label: '1h',  ms: 60 * 60 * 1000,           bucketMs: 5 * 60 * 1000 },
  { key: '6h',  label: '6h',  ms: 6 * 60 * 60 * 1000,       bucketMs: 30 * 60 * 1000 },
  { key: '24h', label: '24h', ms: 24 * 60 * 60 * 1000,      bucketMs: 2 * 60 * 60 * 1000 },
  { key: '7d',  label: '7d',  ms: 7 * 24 * 60 * 60 * 1000,  bucketMs: 24 * 60 * 60 * 1000 },
];

const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;

function formatBucketLabel(ts: number, scale: Timescale): string {
  const d = new Date(ts);
  if (scale === '7d') return d.toLocaleDateString(undefined, { timeZone: tz, weekday: 'short', month: 'short', day: 'numeric' });
  return d.toLocaleTimeString(undefined, { timeZone: tz, hour: '2-digit', minute: '2-digit', hour12: false });
}

function binAlerts(alerts: CrowdSecAlert[], scale: Timescale) {
  const cfg = TIMESCALES.find(t => t.key === scale)!;
  const now = Date.now();
  // Align start to a clean bucket boundary so labels fall on round times
  const rawStart = now - cfg.ms;
  const alignedStart = Math.floor(rawStart / cfg.bucketMs) * cfg.bucketMs;
  const numBuckets = Math.ceil((now - alignedStart) / cfg.bucketMs);
  const buckets = Array.from({ length: numBuckets }, (_, i) => ({
    label: formatBucketLabel(alignedStart + i * cfg.bucketMs, scale),
    detections: 0,
  }));
  for (const a of alerts) {
    const t = new Date(a.createdAt).getTime();
    if (t < alignedStart) continue;
    const idx = Math.floor((t - alignedStart) / cfg.bucketMs);
    if (idx >= 0 && idx < numBuckets) buckets[idx].detections++;
  }
  return buckets;
}

function formatRelativeTime(isoString?: string): string {
  if (!isoString) return 'unknown';
  const diff = Date.now() - new Date(isoString).getTime();
  const minutes = Math.floor(diff / 60000);
  if (minutes < 1) return 'just now';
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  return `${Math.floor(hours / 24)}d ago`;
}

function scenarioHubUrl(scenario: string): string | null {
  if (!scenario) return null;
  const parts = scenario.split('/');
  // Only link hub scenarios in author/name format (e.g. crowdsecurity/http-probing-classb)
  // CAPI category labels (http:scan, ssh:bruteforce) have no individual hub pages
  if (parts.length === 2 && parts[0] !== 'custom') {
    return `https://app.crowdsec.net/hub/author/${parts[0]}/configurations/name/${parts[1]}`;
  }
  return null;
}

const KNOWN_ABBREV = new Set(['ssh', 'http', 'https', 'tcp', 'udp', 'ftp', 'ip', 'dns', 'tls', 'dos', 'ddos', 'bf']);

function titleCaseScenario(s: string): string {
  return s.replace(/[:\-_]/g, ' ').split(' ').filter(Boolean).map(w =>
    KNOWN_ABBREV.has(w.toLowerCase()) ? w.toUpperCase() : w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
  ).join(' ');
}

function friendlyReason(reason: string): string {
  if (!reason) return 'Community blocklist';
  const map: Record<string, string> = {
    'crowdsecurity/http-scan-classb': 'HTTP Scanner',
    'crowdsecurity/ssh-slow-bruteforce': 'SSH Brute Force (slow)',
    'crowdsecurity/ssh-bf': 'SSH Brute Force',
    'crowdsecurity/ssh-bruteforce': 'SSH Brute Force',
    'crowdsecurity/http-dos': 'HTTP DoS',
    'crowdsecurity/http-bruteforce': 'HTTP Brute Force',
    'crowdsecurity/http-exploit': 'HTTP Exploit',
    'crowdsecurity/http-crawl': 'HTTP Crawler',
    'crowdsecurity/http-bad-user-agent': 'Bad User Agent',
    'crowdsecurity/iptables-scan-multi_ports': 'Port Scanner',
  };
  if (map[reason]) return map[reason];
  const short = reason.includes('/') ? reason.split('/').slice(1).join(' ') : reason;
  return titleCaseScenario(short);
}

function parseDuration(dur?: string): string {
  if (!dur) return '';
  // Go duration: "164h1m31s" → "6d 20h"
  const h = dur.match(/(\d+)h/);
  const m = dur.match(/(\d+)m/);
  const hours = h ? parseInt(h[1]) : 0;
  const mins = m ? parseInt(m[1]) : 0;
  if (hours >= 24) {
    const days = Math.floor(hours / 24);
    const rem = hours % 24;
    return rem > 0 ? `${days}d ${rem}h` : `${days}d`;
  }
  if (hours > 0) return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
  return `${mins}m`;
}

interface BlockFormValues {
  ip: string;
  type: 'ban' | 'allow';
  duration: string;
  reason: string;
}

export function CrowdSecComponent() {
  const instances: string[] = [];
  const {
    status,
    isLoadingStatus,
    summary,
    isLoadingSummary,
    alerts,
    isLoadingAlerts,
    refetchAlerts,
    graphAlerts,
    isLoadingGraphAlerts,
    decisions,
    addDecision,
    isAddingDecision,
    addDecisionError,
    deleteDecision,
    deleteBouncer,
    deleteMachine,
    provision,
    isProvisioning,
  } = useCrowdSec();

  const [provisionResults, setProvisionResults] = useState<Record<string, { ok: boolean; message: string }>>({});
  const [provisioningInstance, setProvisioningInstance] = useState<string | null>(null);
  const [blockSuccess, setBlockSuccess] = useState<string | null>(null);
  const [timescale, setTimescale] = useState<Timescale>('24h');

  const { register, handleSubmit, reset, setValue, watch, formState: { errors } } = useForm<BlockFormValues>({
    defaultValues: { ip: '', type: 'ban', duration: '24h', reason: 'manual' },
  });
  const selectedType = watch('type');

  function handleProvision(instanceName: string) {
    setProvisioningInstance(instanceName);
    provision(instanceName, {
      onSuccess: (data) => {
        setProvisionResults((prev) => ({ ...prev, [instanceName]: { ok: true, message: data.message } }));
        setProvisioningInstance(null);
      },
      onError: (err) => {
        setProvisionResults((prev) => ({ ...prev, [instanceName]: { ok: false, message: (err as Error).message } }));
        setProvisioningInstance(null);
      },
    });
  }

  function onBlockSubmit(values: BlockFormValues) {
    setBlockSuccess(null);
    const req: AddDecisionRequest = { ip: values.ip, type: values.type, reason: values.reason, duration: values.duration };
    addDecision(req, {
      onSuccess: (data) => {
        setBlockSuccess(data.message);
        reset();
      },
    });
  }

  const sortedReasons = summary
    ? Object.entries(summary.byReason).sort(([, a], [, b]) => b - a)
    : [];

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-4 mb-6">
        <div className="p-2 bg-primary/10 rounded-lg">
          <ShieldAlert className="h-6 w-6 text-primary" />
        </div>
        <div>
          <h2 className="text-2xl font-semibold">CrowdSec</h2>
          <p className="text-muted-foreground">Collaborative intrusion detection — protecting your infrastructure with community threat intelligence</p>
        </div>
      </div>

      {/* Educational card */}
      <Card className="bg-gradient-to-br from-cyan-50 to-blue-50 dark:from-cyan-900/20 dark:to-blue-900/20 border-cyan-200 dark:border-cyan-800">
        <CardContent className="pt-4">
          <div className="flex gap-3">
            <BookOpen className="h-5 w-5 text-cyan-600 dark:text-cyan-400 shrink-0 mt-0.5" />
            <div className="space-y-1 text-sm text-cyan-900 dark:text-cyan-100">
              <p>
                <strong>CrowdSec</strong> analyzes logs from your k8s clusters, detects attacks, and shares threat
                intelligence with the community. Wild Central runs the <strong>LAPI</strong> — the hub that agents
                report to and that bouncers query for the live blocklist. Your cluster contributes to and benefits
                from a shared blocklist of millions of known malicious IPs.
              </p>
            </div>
          </div>
        </CardContent>
      </Card>

      {/* LAPI Status */}
      <Card>
        <CardHeader>
          <div className="flex items-center justify-between">
            <div className="flex items-center gap-2">
              <Shield className="h-4 w-4 text-muted-foreground" />
              <CardTitle>LAPI Status</CardTitle>
            </div>
            {isLoadingStatus ? (
              <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
            ) : (
              <Badge variant={status?.active ? 'success' : 'warning'} className="gap-1">
                {status?.active && <CheckCircle className="h-3 w-3" />}
                {status?.active ? 'Running' : 'Stopped'}
              </Badge>
            )}
          </div>
        </CardHeader>
        <CardContent className="space-y-3">
          {!status?.active && !isLoadingStatus && (
            <p className="text-sm text-muted-foreground">
              CrowdSec is not running on Wild Central. Install it with{' '}
              <code className="font-mono text-xs bg-muted px-1 rounded">apt install crowdsec crowdsec-firewall-bouncer</code>
              {' '}or reinstall wild-cloud-central.
            </p>
          )}
          {status?.active && (
            <>
              <div className="flex items-center justify-between py-2 px-3 rounded bg-muted">
                <div className="flex items-center gap-2 text-sm">
                  <Shield className="h-4 w-4 text-muted-foreground" />
                  <span>Perimeter firewall enforcement</span>
                  <span className="text-xs text-muted-foreground">(nftables on Wild Central)</span>
                </div>
                <Badge variant={status.firewallBouncer ? 'success' : 'warning'} className="gap-1">
                  {status.firewallBouncer && <CheckCircle className="h-3 w-3" />}
                  {status.firewallBouncer ? 'Active' : 'Stopped'}
                </Badge>
              </div>
              {!status.firewallBouncer && (
                <p className="text-xs text-muted-foreground">
                  Install the firewall bouncer to block threats at the perimeter:{' '}
                  <code className="font-mono bg-muted px-1 rounded">apt install crowdsec-firewall-bouncer</code>
                </p>
              )}
              <p className="text-xs text-muted-foreground">
                {status.machines.length > 0 && `${status.machines.length} k8s agent${status.machines.length !== 1 ? 's' : ''} reporting. `}
                {status.bouncers.length > 0 && `${status.bouncers.length} enforcement point${status.bouncers.length !== 1 ? 's' : ''} active.`}
              </p>
            </>
          )}
        </CardContent>
      </Card>

      {status?.active && (
        <>
          {/* Active Protection Summary */}
          <Card>
            <CardHeader>
              <div className="flex items-center gap-2">
                <Activity className="h-4 w-4 text-muted-foreground" />
                <CardTitle>Active Protection</CardTitle>
              </div>
            </CardHeader>
            <CardContent className="space-y-3">
              <p className="text-sm text-muted-foreground">
                IPs currently blocked by the community blocklist (CAPI) and local decisions, enforced by your bouncers.
              </p>
              {isLoadingSummary ? (
                <div className="flex items-center gap-2 text-sm text-muted-foreground">
                  <Loader2 className="h-4 w-4 animate-spin" />Loading ban statistics…
                </div>
              ) : summary ? (
                <div className="space-y-3">
                  <div className="flex items-center gap-3">
                    <div className="flex items-center gap-2">
                      <Ban className="h-5 w-5 text-red-500" />
                      <span className="text-2xl font-bold">{summary.total.toLocaleString()}</span>
                    </div>
                    <span className="text-sm text-muted-foreground">IPs blocked right now</span>
                  </div>
                  {sortedReasons.length > 0 && (
                    <div className="space-y-1.5">
                      <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">By threat type</p>
                      {sortedReasons.slice(0, 8).map(([reason, count]) => {
                        const pct = Math.round((count / summary.total) * 100);
                        return (
                          <div key={reason} className="flex items-center gap-2 text-sm">
                            <div className="flex-1 flex items-center gap-2">
                              <div
                                className="h-1.5 bg-red-400 rounded"
                                style={{ width: `${Math.max(pct, 2)}%`, maxWidth: '60%' }}
                              />
                              {scenarioHubUrl(reason) ? (
                                <a href={scenarioHubUrl(reason)!} target="_blank" rel="noopener noreferrer" className="text-muted-foreground hover:text-foreground hover:underline">
                                  {friendlyReason(reason)}
                                </a>
                              ) : (
                                <span className="text-muted-foreground">{friendlyReason(reason)}</span>
                              )}
                            </div>
                            <span className="font-mono text-xs tabular-nums">{count.toLocaleString()}</span>
                            <span className="text-xs text-muted-foreground w-8 text-right">{pct}%</span>
                          </div>
                        );
                      })}
                      {sortedReasons.length > 8 && (
                        <p className="text-xs text-muted-foreground">+{sortedReasons.length - 8} more categories</p>
                      )}
                    </div>
                  )}
                </div>
              ) : null}
            </CardContent>
          </Card>

          {/* Detections Over Time */}
          <Card>
            <CardHeader>
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <Activity className="h-4 w-4 text-muted-foreground" />
                  <CardTitle>Detections Over Time</CardTitle>
                </div>
                <div className="flex gap-1">
                  {TIMESCALES.map(t => (
                    <Button
                      key={t.key}
                      variant={timescale === t.key ? 'default' : 'ghost'}
                      size="sm"
                      className="h-7 px-2 text-xs"
                      onClick={() => setTimescale(t.key)}
                    >
                      {t.label}
                    </Button>
                  ))}
                </div>
              </div>
            </CardHeader>
            <CardContent>
              {isLoadingGraphAlerts ? (
                <div className="flex items-center justify-center h-40 gap-2 text-sm text-muted-foreground">
                  <Loader2 className="h-4 w-4 animate-spin" />Loading…
                </div>
              ) : (
                <ResponsiveContainer width="100%" height={160}>
                  <BarChart data={binAlerts(graphAlerts, timescale)} margin={{ top: 4, right: 8, left: -20, bottom: 0 }}>
                    <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
                    <XAxis dataKey="label" tick={{ fontSize: 10 }} interval="preserveStartEnd" className="fill-muted-foreground" />
                    <YAxis allowDecimals={false} tick={{ fontSize: 10 }} className="fill-muted-foreground" />
                    <Tooltip
                      contentStyle={{ fontSize: 12 }}
                      formatter={(v) => [v, 'detections']}
                    />
                    <Bar dataKey="detections" fill="#ef4444" radius={[2, 2, 0, 0]} />
                  </BarChart>
                </ResponsiveContainer>
              )}
            </CardContent>
          </Card>

          {/* Recent Alert Feed */}
          <Card>
            <CardHeader>
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <Activity className="h-4 w-4 text-muted-foreground" />
                  <CardTitle>Recent Detections</CardTitle>
                </div>
                <Button variant="ghost" size="sm" onClick={() => refetchAlerts()} className="h-7 gap-1.5 text-xs">
                  <RefreshCw className="h-3 w-3" />Refresh
                </Button>
              </div>
            </CardHeader>
            <CardContent>
              <p className="text-sm text-muted-foreground mb-3">
                Attack events detected by your k8s agents and reported to this LAPI. Each event results in a ban
                that is shared with the CrowdSec community.
              </p>
              {isLoadingAlerts ? (
                <div className="flex items-center gap-2 text-sm text-muted-foreground">
                  <Loader2 className="h-4 w-4 animate-spin" />Loading…
                </div>
              ) : alerts.length === 0 ? (
                <div className="text-center py-6">
                  <Shield className="h-10 w-10 text-muted-foreground mx-auto mb-2" />
                  <p className="text-sm text-muted-foreground">No detections yet.</p>
                  <p className="text-xs text-muted-foreground mt-1">
                    Events appear here when k8s agents detect and report attacks. The community blocklist
                    (CAPI) is already protecting you even before local detections.
                  </p>
                </div>
              ) : (
                <div className="space-y-1 max-h-72 overflow-y-auto">
                  <div className="grid grid-cols-[1fr_auto_auto_auto] gap-x-3 text-xs text-muted-foreground font-medium px-2 pb-1">
                    <span>Source IP</span>
                    <span>Threat</span>
                    <span>Agent</span>
                    <span>When</span>
                  </div>
                  {alerts.map((alert) => (
                    <div key={alert.id} className="grid grid-cols-[1fr_auto_auto_auto] gap-x-3 items-center py-1.5 px-2 bg-muted rounded text-sm">
                      <div className="flex items-center gap-1.5 min-w-0">
                        <span className="font-mono text-xs truncate">{alert.sourceIP || '—'}</span>
                        {alert.country && (
                          <span className="text-xs text-muted-foreground flex items-center gap-0.5 shrink-0">
                            <Globe className="h-3 w-3" />{alert.country}
                          </span>
                        )}
                      </div>
                      {scenarioHubUrl(alert.scenario) ? (
                        <a href={scenarioHubUrl(alert.scenario)!} target="_blank" rel="noopener noreferrer" className="text-xs text-muted-foreground whitespace-nowrap hover:text-foreground hover:underline">
                          {friendlyReason(alert.scenario)}
                        </a>
                      ) : (
                        <span className="text-xs text-muted-foreground whitespace-nowrap">{friendlyReason(alert.scenario)}</span>
                      )}
                      <span className="text-xs font-mono text-muted-foreground whitespace-nowrap">{alert.machineId?.replace(/^wc-/, '') ?? '—'}</span>
                      <span className="text-xs text-muted-foreground whitespace-nowrap">{formatRelativeTime(alert.createdAt)}</span>
                    </div>
                  ))}
                </div>
              )}
            </CardContent>
          </Card>

          {/* Manual Decisions: Block / Allow */}
          <Card>
            <CardHeader>
              <div className="flex items-center gap-2">
                <Ban className="h-4 w-4 text-muted-foreground" />
                <CardTitle>Block or Allow an IP</CardTitle>
              </div>
            </CardHeader>
            <CardContent className="space-y-4">
              <p className="text-sm text-muted-foreground">
                Manually ban an IP (e.g. an attacker you've identified) or add a local allowlist entry
                to prevent a legitimate IP from being blocked by the community list.
              </p>

              <form onSubmit={handleSubmit(onBlockSubmit)} className="space-y-3">
                <div className="grid grid-cols-2 gap-3">
                  <div>
                    <Label htmlFor="ip">IP Address</Label>
                    <Input
                      id="ip"
                      placeholder="1.2.3.4"
                      className="mt-1 font-mono"
                      {...register('ip', {
                        required: 'Required',
                        pattern: { value: /^[\d.:/a-fA-F]+$/, message: 'Invalid IP' },
                      })}
                    />
                    {errors.ip && <p className="text-xs text-red-600 mt-1">{errors.ip.message}</p>}
                  </div>
                  <div>
                    <Label htmlFor="reason">Reason</Label>
                    <Input
                      id="reason"
                      placeholder="manual"
                      className="mt-1"
                      {...register('reason')}
                    />
                  </div>
                </div>

                <div className="grid grid-cols-2 gap-3">
                  <div>
                    <Label>Action</Label>
                    <Select value={selectedType} onValueChange={(v) => setValue('type', v as 'ban' | 'allow')}>
                      <SelectTrigger className="mt-1">
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        <SelectItem value="ban">Ban (block)</SelectItem>
                        <SelectItem value="allow">Allow (whitelist)</SelectItem>
                      </SelectContent>
                    </Select>
                  </div>
                  <div>
                    <Label>Duration</Label>
                    <Select defaultValue="24h" onValueChange={(v) => setValue('duration', v)}>
                      <SelectTrigger className="mt-1">
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        <SelectItem value="1h">1 hour</SelectItem>
                        <SelectItem value="24h">24 hours</SelectItem>
                        <SelectItem value="168h">7 days</SelectItem>
                        <SelectItem value="720h">30 days</SelectItem>
                        <SelectItem value="8760h">1 year</SelectItem>
                      </SelectContent>
                    </Select>
                  </div>
                </div>

                {blockSuccess && (
                  <Alert>
                    <CheckCircle className="h-4 w-4" />
                    <AlertDescription className="text-xs">{blockSuccess}</AlertDescription>
                  </Alert>
                )}
                {addDecisionError && (
                  <Alert variant="error">
                    <AlertCircle className="h-4 w-4" />
                    <AlertDescription className="text-xs">{(addDecisionError as Error).message}</AlertDescription>
                  </Alert>
                )}

                <Button type="submit" size="sm" disabled={isAddingDecision} className="gap-2">
                  {isAddingDecision ? <Loader2 className="h-4 w-4 animate-spin" /> : <Ban className="h-4 w-4" />}
                  {selectedType === 'allow' ? 'Add to Allowlist' : 'Block IP'}
                </Button>
              </form>

              {/* Local decisions list */}
              {decisions.length > 0 && (
                <div className="border-t pt-3 space-y-2">
                  <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Active local decisions</p>
                  <div className="space-y-1 max-h-48 overflow-y-auto">
                    {decisions.map((d) => (
                      <div key={d.id} className="flex items-center justify-between py-1.5 px-2 bg-muted rounded text-xs">
                        <div className="flex items-center gap-2 min-w-0">
                          {d.type === 'allow'
                            ? <CheckCircle className="h-3 w-3 text-green-500 shrink-0" />
                            : <Ban className="h-3 w-3 text-red-500 shrink-0" />
                          }
                          <span className="font-mono truncate">{d.value}</span>
                          <Badge variant="outline" className="text-xs h-4 shrink-0">{d.type}</Badge>
                          <span className="text-muted-foreground truncate">{friendlyReason(d.reason)}</span>
                        </div>
                        <div className="flex items-center gap-2 shrink-0">
                          {d.duration && (
                            <span className="text-muted-foreground">{parseDuration(d.duration)}</span>
                          )}
                          <Button
                            variant="ghost"
                            size="sm"
                            className="h-5 w-5 p-0 text-muted-foreground hover:text-red-500"
                            onClick={() => deleteDecision(d.id)}
                          >
                            <X className="h-3 w-3" />
                          </Button>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </CardContent>
          </Card>

          {/* Registered Agents */}
          <Card>
            <CardHeader>
              <div className="flex items-center gap-2">
                <Zap className="h-4 w-4 text-muted-foreground" />
                <CardTitle>Registered Agents</CardTitle>
              </div>
            </CardHeader>
            <CardContent className="space-y-3">
              <p className="text-sm text-muted-foreground">
                <strong>Agents</strong> are CrowdSec engines running inside your k8s clusters. They analyze
                logs, detect threats, and report to this LAPI. Each provisioned Wild Cloud instance registers
                one agent here.
              </p>
              {status.machines.length === 0 ? (
                <p className="text-sm text-muted-foreground text-center py-3">No agents registered. Provision an instance below.</p>
              ) : (
                <div className="space-y-2">
                  {status.machines.map((m) => (
                    <div key={m.machineId} className="flex items-center justify-between py-2 px-3 bg-muted rounded">
                      <div className="space-y-0.5">
                        <div className="flex items-center gap-2">
                          <span className="font-mono text-sm">{m.machineId}</span>
                          <Badge variant={m.isValidated ? 'default' : 'secondary'} className="text-xs h-4">
                            {m.isValidated ? 'validated' : 'pending'}
                          </Badge>
                        </div>
                        <div className="flex items-center gap-3 text-xs text-muted-foreground">
                          {m.last_heartbeat && <span>heartbeat {formatRelativeTime(m.last_heartbeat)}</span>}
                          {m.version && <span>{m.version}</span>}
                          {m.ipAddress && <span className="font-mono">{m.ipAddress}</span>}
                        </div>
                      </div>
                      <Button
                        variant="ghost"
                        size="sm"
                        className="h-7 w-7 p-0 text-muted-foreground hover:text-red-500"
                        onClick={() => deleteMachine(m.machineId)}
                      >
                        <Trash2 className="h-3 w-3" />
                      </Button>
                    </div>
                  ))}
                </div>
              )}
            </CardContent>
          </Card>

          {/* Registered Bouncers */}
          <Card>
            <CardHeader>
              <div className="flex items-center gap-2">
                <Shield className="h-4 w-4 text-muted-foreground" />
                <CardTitle>Registered Bouncers</CardTitle>
              </div>
            </CardHeader>
            <CardContent className="space-y-3">
              <p className="text-sm text-muted-foreground">
                <strong>Bouncers</strong> enforce the blocklist. Wild Central's nftables bouncer blocks at
                the network perimeter. Each k8s cluster also runs a Traefik bouncer as a second layer.
              </p>
              {status.bouncers.length === 0 ? (
                <p className="text-sm text-muted-foreground text-center py-3">No bouncers registered. Provision an instance to add them.</p>
              ) : (
                <div className="space-y-2">
                  {status.bouncers.map((b) => {
                    const isFirewallBouncer = b.type === 'crowdsec-firewall-bouncer';
                    return (
                      <div key={b.name} className="flex items-center justify-between py-2 px-3 bg-muted rounded">
                        <div className="space-y-0.5">
                          <div className="flex items-center gap-2">
                            <span className="font-mono text-sm truncate max-w-64">{b.name}</span>
                            <Badge variant={b.revoked ? 'secondary' : 'default'} className="text-xs h-4">
                              {b.revoked ? 'revoked' : 'active'}
                            </Badge>
                            {isFirewallBouncer && (
                              <Badge variant="outline" className="text-xs h-4 border-orange-400 text-orange-600 dark:text-orange-400">
                                perimeter
                              </Badge>
                            )}
                          </div>
                          <p className="text-xs text-muted-foreground">
                            {isFirewallBouncer ? 'Crowdsec Firewall Bouncer' : b.type?.replace(/-/g, ' ') || ''}
                          </p>
                        </div>
                        {!isFirewallBouncer && (
                          <Button
                            variant="ghost"
                            size="sm"
                            className="h-7 w-7 p-0 text-muted-foreground hover:text-red-500"
                            onClick={() => deleteBouncer(b.name)}
                          >
                            <Trash2 className="h-3 w-3" />
                          </Button>
                        )}
                      </div>
                    );
                  })}
                </div>
              )}
            </CardContent>
          </Card>

          {/* Provision Instances */}
          <Card>
            <CardHeader>
              <div className="flex items-center gap-2">
                <Zap className="h-4 w-4 text-muted-foreground" />
                <CardTitle>Provision Instances</CardTitle>
              </div>
            </CardHeader>
            <CardContent className="space-y-3">
              <p className="text-sm text-muted-foreground">
                <strong>Provisioning</strong> connects a Wild Cloud instance's CrowdSec agent to this LAPI —
                generating credentials, registering agent and bouncer, and writing the LAPI URL into the
                instance's app config. After provisioning, redeploy the CrowdSec app on the instance.
                Safe to re-run.
              </p>
              <div className="space-y-2">
                {instances.length === 0 ? (
                  <p className="text-sm text-muted-foreground">No instances found.</p>
                ) : (
                  instances.map((instanceName) => {
                    const agentName = `wc-${instanceName}`;
                    const bouncerName = `wc-bouncer-${instanceName}`;
                    const agent = status.machines.find((m) => m.machineId === agentName);
                    const bouncer = status.bouncers.find((b) => b.name === bouncerName);
                    const isActive = provisioningInstance === instanceName;
                    const result = provisionResults[instanceName];

                    return (
                      <div key={instanceName} className="rounded border p-3 space-y-2">
                        <div className="flex items-center justify-between">
                          <span className="font-mono text-sm font-medium">{instanceName}</span>
                          <Button
                            size="sm"
                            variant={agent ? 'outline' : 'default'}
                            onClick={() => handleProvision(instanceName)}
                            disabled={isProvisioning || isActive}
                            className="gap-2 h-7"
                          >
                            {isActive
                              ? <><Loader2 className="h-3 w-3 animate-spin" />Provisioning…</>
                              : agent
                              ? <><Zap className="h-3 w-3" />Re-provision</>
                              : <><Zap className="h-3 w-3" />Provision</>
                            }
                          </Button>
                        </div>
                        <div className="flex items-center gap-4 text-xs">
                          <div className="flex items-center gap-1.5">
                            <span className="text-muted-foreground">Agent:</span>
                            {agent ? (
                              <Badge variant={agent.isValidated ? 'default' : 'secondary'} className="text-xs h-4">
                                {agent.isValidated ? 'validated' : 'pending'}
                                {agent.last_heartbeat && ` · ${formatRelativeTime(agent.last_heartbeat)}`}
                              </Badge>
                            ) : (
                              <span className="text-muted-foreground italic">not provisioned</span>
                            )}
                          </div>
                          <div className="flex items-center gap-1.5">
                            <span className="text-muted-foreground">Bouncer:</span>
                            {bouncer ? (
                              <Badge variant={bouncer.revoked ? 'secondary' : 'default'} className="text-xs h-4">
                                {bouncer.revoked ? 'revoked' : 'active'}
                              </Badge>
                            ) : (
                              <span className="text-muted-foreground italic">not provisioned</span>
                            )}
                          </div>
                        </div>
                        {result && (
                          <Alert variant={result.ok ? 'default' : 'error'} className="py-2">
                            {result.ok ? <CheckCircle className="h-3 w-3" /> : <AlertCircle className="h-3 w-3" />}
                            <AlertDescription className="text-xs">{result.message}</AlertDescription>
                          </Alert>
                        )}
                      </div>
                    );
                  })
                )}
              </div>
            </CardContent>
          </Card>
        </>
      )}
    </div>
  );
}
