import { useState } from 'react';
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 { Shield, Loader2, AlertCircle, CheckCircle, Check, X, Plus, Trash2, BookOpen, Lock } from 'lucide-react';
import { Badge } from './ui/badge';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useNftables } from '../hooks/useNftables';
import { useVpn } from '../hooks/useVpn';
import { nftablesConfigApi, haproxyRoutesApi } from '../services/api/settings';

type ExtraPort = { port: number; protocol?: string; label?: string };

const WELL_KNOWN_PORTS: Record<number, string> = {
  22: 'SSH',
  53: 'DNS',
  67: 'DHCP Server',
  68: 'DHCP Client',
  80: 'HTTP',
  443: 'HTTPS',
  3389: 'RDP',
  5353: 'mDNS',
  5900: 'VNC',
  8404: 'HAProxy Stats',
};

// Ports that are always included automatically
const AUTO_TCP_PORTS = [80, 443, 8404];
const AUTO_UDP_PORTS = [53, 67, 68];

interface PortEntry {
  port: number;
  protocol: string;
  label: string;
  source: 'auto' | 'haproxy-custom' | 'user';
}

export function FirewallComponent() {
  const queryClient = useQueryClient();
  const { data: nftCfg } = useQuery({ queryKey: ['nftables', 'config'], queryFn: () => nftablesConfigApi.get() });
  const { data: haproxyRoutes } = useQuery({ queryKey: ['haproxy', 'routes'], queryFn: () => haproxyRoutesApi.get() });
  const nftMutation = useMutation({
    mutationFn: (data: Parameters<typeof nftablesConfigApi.update>[0]) => nftablesConfigApi.update(data),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['nftables', 'config'] }),
  });
  const { interfaces, refetchStatus } = useNftables();
  const { config: vpnConfig } = useVpn();

  const isEnabled = nftCfg?.enabled !== false;
  const currentWan = nftCfg?.wanInterface ?? '';
  const currentExtraPorts: ExtraPort[] = nftCfg?.extraPorts ?? [];
  const customRoutes = haproxyRoutes?.customRoutes ?? [];

  const [editingWan, setEditingWan] = useState(false);
  const [wanValue, setWanValue] = useState('');
  const [savingWan, setSavingWan] = useState(false);
  const [togglingFirewall, setTogglingFirewall] = useState(false);

  const [successMessage, setSuccessMessage] = useState<string | null>(null);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);

  const [newPort, setNewPort] = useState('');
  const [newProtocol, setNewProtocol] = useState('tcp');
  const [newLabel, setNewLabel] = useState('');
  const [portError, setPortError] = useState('');
  const [savingPorts, setSavingPorts] = useState(false);

  const showSuccess = (msg: string) => {
    setSuccessMessage(msg);
    setErrorMessage(null);
    setTimeout(() => setSuccessMessage(null), 5000);
  };

  const showError = (msg: string) => {
    setErrorMessage(msg);
    setSuccessMessage(null);
    setTimeout(() => setErrorMessage(null), 8000);
  };

  // Build unified port list from all sources
  function buildPortList(): PortEntry[] {
    const entries: PortEntry[] = [];
    const seen = new Set<string>();

    const addEntry = (port: number, protocol: string, label: string, source: PortEntry['source']) => {
      const key = `${port}/${protocol}`;
      if (!seen.has(key)) {
        seen.add(key);
        entries.push({ port, protocol, label, source });
      }
    };

    // Auto TCP ports (HAProxy base)
    for (const port of AUTO_TCP_PORTS) {
      addEntry(port, 'tcp', WELL_KNOWN_PORTS[port] || '', 'auto');
    }

    // HAProxy custom route ports
    for (const route of customRoutes) {
      addEntry(route.port, 'tcp', `HAProxy: ${route.name}`, 'haproxy-custom');
    }

    // Auto UDP ports (DNS/DHCP)
    for (const port of AUTO_UDP_PORTS) {
      addEntry(port, 'udp', WELL_KNOWN_PORTS[port] || '', 'auto');
    }

    // VPN listen port (when enabled)
    if (vpnConfig?.enabled && vpnConfig.listenPort) {
      addEntry(vpnConfig.listenPort, 'udp', 'WireGuard VPN', 'auto');
    }

    // User extra ports
    for (const ep of currentExtraPorts) {
      const proto = ep.protocol || 'tcp';
      addEntry(ep.port, proto, ep.label || WELL_KNOWN_PORTS[ep.port] || '', 'user');
    }

    return entries;
  }

  const allPorts = buildPortList();
  const tcpPorts = allPorts.filter(p => p.protocol === 'tcp');
  const udpPorts = allPorts.filter(p => p.protocol === 'udp');

  async function saveNftConfig(patch: Partial<NonNullable<typeof nftCfg>>) {
    await nftMutation.mutateAsync(patch);
    refetchStatus();
  }

  async function toggleFirewall() {
    setTogglingFirewall(true);
    try {
      await saveNftConfig({ enabled: !isEnabled });
      showSuccess(`Firewall ${isEnabled ? 'disabled' : 'enabled'}`);
    } catch (e) {
      showError(`Failed to toggle firewall: ${e instanceof Error ? e.message : String(e)}`);
    }
    setTogglingFirewall(false);
  }

  async function saveWan(value: string) {
    setSavingWan(true);
    try {
      await saveNftConfig({ wanInterface: value || undefined });
      showSuccess(value ? `WAN interface set to ${value}` : 'Strict mode disabled');
    } catch (e) {
      showError(`Failed to save WAN interface: ${e instanceof Error ? e.message : String(e)}`);
    }
    setSavingWan(false);
    setEditingWan(false);
  }

  async function addPort() {
    const port = parseInt(newPort, 10);
    if (isNaN(port) || port < 1 || port > 65535) {
      setPortError('Enter a valid port (1–65535)');
      return;
    }
    if (currentExtraPorts.some((p) => p.port === port && (p.protocol || 'tcp') === newProtocol)) {
      setPortError('Port/protocol already in list');
      return;
    }
    setPortError('');
    setSavingPorts(true);
    try {
      const entry: ExtraPort = { port, protocol: newProtocol };
      if (newLabel.trim()) entry.label = newLabel.trim();
      await saveNftConfig({ extraPorts: [...currentExtraPorts, entry] });
      showSuccess(`Port ${port}/${newProtocol} added`);
      setNewPort('');
      setNewLabel('');
    } catch (e) {
      showError(`Failed to add port: ${e instanceof Error ? e.message : String(e)}`);
    }
    setSavingPorts(false);
  }

  async function removePort(port: number, protocol: string) {
    try {
      await saveNftConfig({
        extraPorts: currentExtraPorts.filter(
          (ep) => !(ep.port === port && (ep.protocol || 'tcp') === protocol)
        ),
      });
      showSuccess(`Port ${port}/${protocol} removed`);
    } catch (e) {
      showError(`Failed to remove port: ${e instanceof Error ? e.message : String(e)}`);
    }
  }

  function renderPortRow(entry: PortEntry) {
    const isAuto = entry.source !== 'user';
    return (
      <div
        key={`${entry.port}/${entry.protocol}`}
        className={`flex items-center gap-3 px-3 py-2 rounded-md ${isAuto ? 'bg-muted/50' : 'bg-muted'}`}
      >
        <span className="font-mono text-sm w-16">{entry.port}</span>
        <span className="text-xs text-muted-foreground uppercase w-8">{entry.protocol}</span>
        <span className="text-sm flex-1 truncate">
          {entry.label}
        </span>
        {isAuto ? (
          <span title="Managed automatically"><Lock className="h-3 w-3 text-muted-foreground shrink-0" /></span>
        ) : (
          <button
            onClick={() => removePort(entry.port, entry.protocol)}
            className="text-muted-foreground hover:text-red-500 shrink-0"
            title="Remove port"
          >
            <X className="h-3.5 w-3.5" />
          </button>
        )}
      </div>
    );
  }

  return (
    <div className="space-y-6">
      {/* Header with enable/disable toggle */}
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-4">
          <div className="p-2 bg-primary/10 rounded-lg">
            <Shield className="h-6 w-6 text-primary" />
          </div>
          <div>
            <h2 className="text-2xl font-semibold">Firewall</h2>
            <p className="text-muted-foreground">nftables rules protecting Wild Central itself</p>
          </div>
        </div>
        <div className="flex items-center gap-3">
          <Badge variant={isEnabled ? 'success' : 'secondary'} className="gap-1">
            {isEnabled && <CheckCircle className="h-3 w-3" />}
            {isEnabled ? 'Enabled' : 'Disabled'}
          </Badge>
          <Button
            variant="outline"
            size="sm"
            className="h-7 text-xs gap-1"
            onClick={toggleFirewall}
            disabled={togglingFirewall}
          >
            {togglingFirewall ? <Loader2 className="h-3 w-3 animate-spin" /> : null}
            {isEnabled ? 'Disable' : 'Enable'}
          </Button>
        </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>
                Wild Central uses <strong>nftables</strong> to protect itself. Rules are generated
                automatically from your HAProxy ingress configuration — the ports HAProxy listens
                on are always allowed. The firewall is permissive by default; set a{' '}
                <strong>WAN interface</strong> to enable strict filtering that drops anything not
                explicitly listed. Make sure to add <strong>extra ports</strong> for any services
                you need to reach directly (like SSH) before enabling strict mode.
              </p>
            </div>
          </div>
        </CardContent>
      </Card>

      {successMessage && (
        <Alert className="border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950/20">
          <CheckCircle className="h-4 w-4 text-green-600" />
          <AlertDescription className="text-green-700 dark:text-green-300">{successMessage}</AlertDescription>
        </Alert>
      )}
      {errorMessage && (
        <Alert variant="error">
          <AlertCircle className="h-4 w-4" />
          <AlertDescription>{errorMessage}</AlertDescription>
        </Alert>
      )}

      {/* Allowed Ports */}
      <Card className={!isEnabled ? 'opacity-50 pointer-events-none' : ''}>
        <CardHeader>
          <CardTitle>Allowed Ports</CardTitle>
        </CardHeader>
        <CardContent className="space-y-5">
          <p className="text-sm text-muted-foreground">
            All ports the firewall allows through. Ports managed by HAProxy and system services are
            locked — add your own with the form below.
          </p>

          {/* TCP ports */}
          <div className="space-y-2">
            <h4 className="text-xs font-medium text-muted-foreground uppercase tracking-wide">TCP</h4>
            <div className="space-y-1">
              {tcpPorts.map(renderPortRow)}
            </div>
          </div>

          {/* UDP ports */}
          <div className="space-y-2">
            <h4 className="text-xs font-medium text-muted-foreground uppercase tracking-wide">UDP</h4>
            <div className="space-y-1">
              {udpPorts.map(renderPortRow)}
            </div>
          </div>

          {/* Add port form */}
          <div className="border-t pt-4 space-y-3">
            <h4 className="text-sm font-medium">Add Port</h4>
            <div className="flex flex-wrap gap-2 items-end">
              <div>
                <Label htmlFor="new-port" className="text-xs">Port</Label>
                <Input
                  id="new-port"
                  value={newPort}
                  onChange={(e) => { setNewPort(e.target.value); setPortError(''); }}
                  onKeyDown={(e) => e.key === 'Enter' && addPort()}
                  placeholder="e.g. 22"
                  className="font-mono h-8 text-sm w-24 mt-1"
                />
              </div>
              <div>
                <Label className="text-xs">Protocol</Label>
                <Select value={newProtocol} onValueChange={setNewProtocol}>
                  <SelectTrigger className="h-8 text-sm w-20 mt-1 font-mono">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="tcp">TCP</SelectItem>
                    <SelectItem value="udp">UDP</SelectItem>
                  </SelectContent>
                </Select>
              </div>
              <div>
                <Label htmlFor="new-label" className="text-xs">Label (optional)</Label>
                <Input
                  id="new-label"
                  value={newLabel}
                  onChange={(e) => setNewLabel(e.target.value)}
                  onKeyDown={(e) => e.key === 'Enter' && addPort()}
                  placeholder="e.g. SSH"
                  className="h-8 text-sm w-32 mt-1"
                />
              </div>
              <Button
                size="sm"
                className="h-8 gap-1"
                onClick={addPort}
                disabled={savingPorts || !newPort}
              >
                {savingPorts ? <Loader2 className="h-3 w-3 animate-spin" /> : <Plus className="h-3 w-3" />}
                Add
              </Button>
              {!currentExtraPorts.some((p) => p.port === 22) && (
                <Button
                  size="sm"
                  variant="outline"
                  className="h-8 gap-1 text-xs"
                  onClick={() => { setNewPort('22'); setNewProtocol('tcp'); setNewLabel('SSH'); }}
                >
                  <Plus className="h-3 w-3" />
                  Add SSH (22)
                </Button>
              )}
            </div>
            {portError && <p className="text-xs text-red-600">{portError}</p>}
          </div>
        </CardContent>
      </Card>

      {/* WAN Interface */}
      <Card className={!isEnabled ? 'opacity-50 pointer-events-none' : ''}>
        <CardHeader>
          <CardTitle>WAN Interface</CardTitle>
        </CardHeader>
        <CardContent className="space-y-4">
          <p className="text-sm text-muted-foreground">
            Your <strong>WAN interface</strong> is the network port that faces the internet — the
            one your router sends traffic to. When set, the firewall drops all inbound connections
            on that interface except for the ports listed above. Leave unset to keep the firewall
            permissive (safe default while you're getting started).
          </p>

          {currentWan && !currentExtraPorts.some((p) => p.port === 22) && (
            <Alert variant="warning">
              <AlertCircle className="h-4 w-4" />
              <AlertDescription className="text-xs">
                Strict mode is active but SSH (port 22) is not in your allowed ports.
                You may lose remote access if you don't have another way in.
              </AlertDescription>
            </Alert>
          )}

          {!editingWan ? (
            <div className="flex items-center gap-3">
              {currentWan ? (
                <span className="font-mono bg-muted px-2 py-0.5 rounded text-sm">{currentWan}</span>
              ) : (
                <span className="text-sm text-muted-foreground italic">
                  Not set — permissive mode (all inbound allowed)
                </span>
              )}
              <Button
                variant="outline"
                size="sm"
                className="h-7 text-xs gap-1"
                onClick={() => { setWanValue(currentWan); setEditingWan(true); }}
              >
                {currentWan ? 'Change' : 'Enable strict mode'}
              </Button>
              {currentWan && (
                <Button
                  variant="ghost"
                  size="sm"
                  className="h-7 text-xs gap-1 text-muted-foreground hover:text-red-500"
                  onClick={() => saveWan('')}
                >
                  <Trash2 className="h-3 w-3" />
                  Disable
                </Button>
              )}
            </div>
          ) : (
            <div className="space-y-3">
              {currentWan === '' && !currentExtraPorts.some((p) => p.port === 22) && (
                <Alert variant="warning">
                  <AlertCircle className="h-4 w-4" />
                  <AlertDescription className="text-xs">
                    SSH (port 22) is not in your extra ports list. Enabling strict mode will
                    block SSH access to Wild Central.
                  </AlertDescription>
                </Alert>
              )}
              <div className="space-y-1.5">
                <Label className="text-xs">Select interface</Label>
                {interfaces.length > 0 ? (
                  <Select value={wanValue} onValueChange={setWanValue}>
                    <SelectTrigger className="w-64 h-8 text-sm font-mono">
                      <SelectValue placeholder="Choose an interface…" />
                    </SelectTrigger>
                    <SelectContent>
                      {interfaces.map((iface) => (
                        <SelectItem key={iface.name} value={iface.name}>
                          <span className="font-mono">{iface.name}</span>
                          {iface.addresses.length > 0 && (
                            <span className="text-muted-foreground ml-2 text-xs">
                              {iface.addresses[0].split('/')[0]}
                            </span>
                          )}
                        </SelectItem>
                      ))}
                    </SelectContent>
                  </Select>
                ) : (
                  <Input
                    value={wanValue}
                    onChange={(e) => setWanValue(e.target.value)}
                    placeholder="e.g. eth0"
                    className="font-mono h-8 text-sm w-32"
                  />
                )}
                <p className="text-xs text-muted-foreground">
                  Pick the interface that faces your router/internet. Typically the one with your
                  external IP address.
                </p>
              </div>
              <div className="flex gap-2">
                <Button
                  size="sm"
                  className="h-8 gap-1"
                  onClick={() => saveWan(wanValue)}
                  disabled={savingWan || !wanValue}
                >
                  {savingWan ? <Loader2 className="h-3 w-3 animate-spin" /> : <Check className="h-3 w-3" />}
                  Save
                </Button>
                <Button
                  variant="outline"
                  size="sm"
                  className="h-8 gap-1"
                  onClick={() => setEditingWan(false)}
                >
                  <X className="h-3 w-3" />Cancel
                </Button>
              </div>
            </div>
          )}
        </CardContent>
      </Card>

    </div>
  );
}
