import { useState, useEffect, useCallback } from 'react';
import {
  Shield,
  Lock,
  Globe,
  RefreshCw,
  CheckCircle,
  AlertCircle,
  ChevronDown,
  Upload,
  ToggleLeft,
  ToggleRight,
} from 'lucide-react';
import { connectivityAPI, wireguardAPI } from '../services/api';

// ── Toast helpers (same pattern as Store.jsx) ─────────────────────────────────

function toastEvent(msg, type = 'success') {
  window.dispatchEvent(
    new CustomEvent('connectivity-toast', { detail: { msg, type } })
  );
}

function Toast({ toasts }) {
  return (
    <div className="fixed bottom-4 right-4 z-50 space-y-2 pointer-events-none">
      {toasts.map((t) => (
        <div
          key={t.id}
          className={`px-4 py-3 rounded-lg shadow-lg text-sm text-white flex items-center gap-2 pointer-events-auto ${
            t.type === 'success'
              ? 'bg-green-600'
              : t.type === 'error'
              ? 'bg-red-600'
              : 'bg-yellow-600'
          }`}
        >
          {t.type === 'success' ? (
            <CheckCircle className="h-4 w-4 shrink-0" />
          ) : (
            <AlertCircle className="h-4 w-4 shrink-0" />
          )}
          {t.msg}
        </div>
      ))}
    </div>
  );
}

function useToasts() {
  const [toasts, setToasts] = useState([]);
  useEffect(() => {
    const handler = (e) => {
      const id = Date.now();
      setToasts((prev) => [...prev, { ...e.detail, id }]);
      setTimeout(
        () => setToasts((prev) => prev.filter((t) => t.id !== id)),
        3000
      );
    };
    window.addEventListener('connectivity-toast', handler);
    return () => window.removeEventListener('connectivity-toast', handler);
  }, []);
  return toasts;
}

// ── Status badge ──────────────────────────────────────────────────────────────

function StatusBadge({ status }) {
  if (status === 'active') {
    return (
      <span className="inline-flex items-center gap-1 text-xs font-medium text-green-700 bg-green-50 border border-green-200 rounded-full px-2 py-0.5">
        <CheckCircle className="h-3 w-3" />
        Active
      </span>
    );
  }
  if (status === 'configured') {
    return (
      <span className="inline-flex items-center gap-1 text-xs font-medium text-yellow-700 bg-yellow-50 border border-yellow-200 rounded-full px-2 py-0.5">
        <AlertCircle className="h-3 w-3" />
        Configured
      </span>
    );
  }
  if (status === 'error') {
    return (
      <span className="inline-flex items-center gap-1 text-xs font-medium text-red-700 bg-red-50 border border-red-200 rounded-full px-2 py-0.5">
        <AlertCircle className="h-3 w-3" />
        Error
      </span>
    );
  }
  // not configured
  return (
    <span className="inline-flex items-center gap-1 text-xs font-medium text-gray-500 bg-gray-100 border border-gray-200 rounded-full px-2 py-0.5">
      Not configured
    </span>
  );
}

// ── WireGuard External card ───────────────────────────────────────────────────

function WireguardExitCard({ exitInfo, onUploaded }) {
  const [confText, setConfText] = useState('');
  const [uploading, setUploading] = useState(false);
  const status = exitInfo?.status || 'not_configured';

  const handleUpload = async () => {
    if (!confText.trim()) return;
    setUploading(true);
    try {
      await connectivityAPI.uploadWireguard(confText.trim());
      toastEvent('WireGuard config uploaded');
      setConfText('');
      onUploaded();
    } catch (err) {
      const msg =
        err.response?.data?.error ||
        err.response?.data?.message ||
        'Failed to upload WireGuard config';
      toastEvent(msg, 'error');
    } finally {
      setUploading(false);
    }
  };

  return (
    <div className="bg-white rounded-lg border border-gray-200 p-6 flex flex-col gap-4">
      <div className="flex items-start justify-between gap-3">
        <div className="flex items-center gap-3">
          <div className="flex items-center justify-center w-10 h-10 rounded-lg bg-primary-50 shrink-0">
            <Shield className="h-5 w-5 text-primary-600" />
          </div>
          <div>
            <h3 className="font-semibold text-gray-900">WireGuard External</h3>
            <p className="text-sm text-gray-500">
              Route traffic through an external WireGuard VPN tunnel
            </p>
          </div>
        </div>
        <StatusBadge status={status} />
      </div>

      <div className="flex flex-col gap-1.5">
        <label
          htmlFor="wg-conf"
          className="text-sm font-medium text-gray-700"
        >
          Paste .conf file contents
        </label>
        <textarea
          id="wg-conf"
          value={confText}
          onChange={(e) => setConfText(e.target.value)}
          placeholder="[Interface]&#10;PrivateKey = ...&#10;&#10;[Peer]&#10;PublicKey = ..."
          rows={6}
          className="w-full rounded-md border border-gray-300 px-3 py-2 font-mono text-xs text-gray-800 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500 resize-y"
          aria-describedby="wg-conf-hint"
        />
        <p id="wg-conf-hint" className="text-xs text-gray-400">
          Drag-and-drop not available — paste the file text directly.
        </p>
      </div>

      <div className="flex justify-end pt-2 border-t border-gray-100">
        <button
          onClick={handleUpload}
          disabled={uploading || !confText.trim()}
          className="flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-md transition-colors"
          aria-label="Upload WireGuard config"
        >
          {uploading ? (
            <RefreshCw className="h-4 w-4 animate-spin" />
          ) : (
            <Upload className="h-4 w-4" />
          )}
          {uploading ? 'Uploading…' : 'Upload Config'}
        </button>
      </div>
    </div>
  );
}

// ── OpenVPN card ──────────────────────────────────────────────────────────────

function OpenvpnExitCard({ exitInfo, onUploaded }) {
  const [ovpnText, setOvpnText] = useState('');
  const [profileName, setProfileName] = useState('default');
  const [uploading, setUploading] = useState(false);
  const status = exitInfo?.status || 'not_configured';

  const nameInvalid = profileName.trim() === '';

  const handleUpload = async () => {
    if (!ovpnText.trim() || nameInvalid) return;
    setUploading(true);
    try {
      await connectivityAPI.uploadOpenvpn(ovpnText.trim(), profileName.trim());
      toastEvent('OpenVPN config uploaded');
      setOvpnText('');
      onUploaded();
    } catch (err) {
      const msg =
        err.response?.data?.error ||
        err.response?.data?.message ||
        'Failed to upload OpenVPN config';
      toastEvent(msg, 'error');
    } finally {
      setUploading(false);
    }
  };

  return (
    <div className="bg-white rounded-lg border border-gray-200 p-6 flex flex-col gap-4">
      <div className="flex items-start justify-between gap-3">
        <div className="flex items-center gap-3">
          <div className="flex items-center justify-center w-10 h-10 rounded-lg bg-indigo-50 shrink-0">
            <Lock className="h-5 w-5 text-indigo-600" />
          </div>
          <div>
            <h3 className="font-semibold text-gray-900">OpenVPN</h3>
            <p className="text-sm text-gray-500">
              Route traffic through an OpenVPN tunnel
            </p>
          </div>
        </div>
        <StatusBadge status={status} />
      </div>

      <div className="flex flex-col gap-1.5">
        <label
          htmlFor="ovpn-name"
          className="text-sm font-medium text-gray-700"
        >
          Profile name <span className="text-red-500" aria-hidden="true">*</span>
        </label>
        <input
          id="ovpn-name"
          type="text"
          value={profileName}
          onChange={(e) => setProfileName(e.target.value)}
          placeholder="default"
          className={`w-full rounded-md border px-3 py-2 text-sm text-gray-800 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500 ${
            nameInvalid
              ? 'border-red-300 focus:ring-red-400 focus:border-red-400'
              : 'border-gray-300 focus:border-primary-500'
          }`}
          aria-required="true"
          aria-describedby={nameInvalid ? 'ovpn-name-error' : undefined}
        />
        {nameInvalid && (
          <p id="ovpn-name-error" className="text-xs text-red-600" role="alert">
            Profile name is required
          </p>
        )}
      </div>

      <div className="flex flex-col gap-1.5">
        <label
          htmlFor="ovpn-conf"
          className="text-sm font-medium text-gray-700"
        >
          Paste .ovpn file contents
        </label>
        <textarea
          id="ovpn-conf"
          value={ovpnText}
          onChange={(e) => setOvpnText(e.target.value)}
          placeholder="client&#10;dev tun&#10;proto udp&#10;remote ..."
          rows={6}
          className="w-full rounded-md border border-gray-300 px-3 py-2 font-mono text-xs text-gray-800 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500 resize-y"
        />
      </div>

      <div className="flex justify-end pt-2 border-t border-gray-100">
        <button
          onClick={handleUpload}
          disabled={uploading || !ovpnText.trim() || nameInvalid}
          className="flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-md transition-colors"
          aria-label="Upload OpenVPN config"
        >
          {uploading ? (
            <RefreshCw className="h-4 w-4 animate-spin" />
          ) : (
            <Upload className="h-4 w-4" />
          )}
          {uploading ? 'Uploading…' : 'Upload Config'}
        </button>
      </div>
    </div>
  );
}

// ── Tor card ──────────────────────────────────────────────────────────────────

function TorExitCard({ exitInfo, onToggled }) {
  const [toggling, setToggling] = useState(false);
  const status = exitInfo?.status || 'not_configured';
  const isEnabled = status === 'active' || status === 'configured';

  const handleToggle = async () => {
    setToggling(true);
    try {
      // Tor doesn't need a config upload — apply routes enables/disables it
      await connectivityAPI.applyRoutes();
      toastEvent(isEnabled ? 'Tor exit disabled' : 'Tor exit enabled');
      onToggled();
    } catch (err) {
      const msg =
        err.response?.data?.error ||
        err.response?.data?.message ||
        'Failed to toggle Tor';
      toastEvent(msg, 'error');
    } finally {
      setToggling(false);
    }
  };

  return (
    <div className="bg-white rounded-lg border border-gray-200 p-6 flex flex-col gap-4">
      <div className="flex items-start justify-between gap-3">
        <div className="flex items-center gap-3">
          <div className="flex items-center justify-center w-10 h-10 rounded-lg bg-purple-50 shrink-0">
            <Globe className="h-5 w-5 text-purple-600" />
          </div>
          <div>
            <h3 className="font-semibold text-gray-900">Tor</h3>
            <p className="text-sm text-gray-500">
              Route selected peers through the Tor anonymity network
            </p>
          </div>
        </div>
        <StatusBadge status={status} />
      </div>

      <p className="text-sm text-gray-500">
        No configuration file required. Toggle the exit on or off — peers
        assigned to Tor will have their traffic routed accordingly.
      </p>

      <div className="flex justify-end pt-2 border-t border-gray-100">
        <button
          onClick={handleToggle}
          disabled={toggling}
          className={`flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed ${
            isEnabled
              ? 'text-gray-700 bg-gray-100 hover:bg-gray-200'
              : 'text-white bg-primary-600 hover:bg-primary-700'
          }`}
          aria-label={isEnabled ? 'Disable Tor exit' : 'Enable Tor exit'}
          aria-pressed={isEnabled}
        >
          {toggling ? (
            <RefreshCw className="h-4 w-4 animate-spin" />
          ) : isEnabled ? (
            <ToggleRight className="h-4 w-4" />
          ) : (
            <ToggleLeft className="h-4 w-4" />
          )}
          {toggling ? 'Applying…' : isEnabled ? 'Disable' : 'Enable'}
        </button>
      </div>
    </div>
  );
}

// ── Peer exit row ─────────────────────────────────────────────────────────────

const EXIT_OPTIONS = [
  { value: 'default', label: 'Default (direct)' },
  { value: 'wireguard', label: 'WireGuard External' },
  { value: 'openvpn', label: 'OpenVPN' },
  { value: 'tor', label: 'Tor' },
];

function PeerExitRow({ peer, currentExit, onSaved }) {
  const [selected, setSelected] = useState(currentExit || 'default');
  const [saving, setSaving] = useState(false);
  const isDirty = selected !== (currentExit || 'default');

  const handleSave = async () => {
    setSaving(true);
    try {
      await connectivityAPI.setPeerExit(peer.name, selected);
      toastEvent(`Exit for ${peer.name} set to ${selected}`);
      onSaved(peer.name, selected);
    } catch (err) {
      const msg =
        err.response?.data?.error ||
        err.response?.data?.message ||
        `Failed to update exit for ${peer.name}`;
      toastEvent(msg, 'error');
    } finally {
      setSaving(false);
    }
  };

  return (
    <tr className="border-t border-gray-100">
      <td className="py-3 px-4 text-sm font-medium text-gray-900 truncate max-w-[180px]">
        {peer.name}
      </td>
      <td className="py-3 px-4">
        <span className="text-sm text-gray-500">
          {EXIT_OPTIONS.find((o) => o.value === (currentExit || 'default'))
            ?.label || 'Default (direct)'}
        </span>
      </td>
      <td className="py-3 px-4">
        <div className="relative inline-block">
          <select
            value={selected}
            onChange={(e) => setSelected(e.target.value)}
            className="appearance-none bg-white border border-gray-300 text-sm text-gray-800 rounded-md pl-3 pr-8 py-1.5 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
            aria-label={`Change exit for ${peer.name}`}
          >
            {EXIT_OPTIONS.map((opt) => (
              <option key={opt.value} value={opt.value}>
                {opt.label}
              </option>
            ))}
          </select>
          <ChevronDown className="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" />
        </div>
      </td>
      <td className="py-3 px-4 text-right">
        <button
          onClick={handleSave}
          disabled={saving || !isDirty}
          className="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 disabled:opacity-40 disabled:cursor-not-allowed rounded-md transition-colors ml-auto"
          aria-label={`Save exit assignment for ${peer.name}`}
        >
          {saving && <RefreshCw className="h-3.5 w-3.5 animate-spin" />}
          {saving ? 'Saving…' : 'Save'}
        </button>
      </td>
    </tr>
  );
}

// ── Main Connectivity component ───────────────────────────────────────────────

function Connectivity() {
  const toasts = useToasts();

  const [exits, setExits] = useState({});           // keyed by exit type
  const [peerExits, setPeerExits] = useState({});   // peer_name -> exit_via
  const [peers, setPeers] = useState([]);           // WireGuard peer list
  const [isLoading, setIsLoading] = useState(true);
  const [loadError, setLoadError] = useState(null);
  const [applying, setApplying] = useState(false);

  const loadAll = useCallback(async () => {
    setLoadError(null);
    try {
      const [exitsRes, peerExitsRes, peersRes] = await Promise.all([
        connectivityAPI.listExits().catch(() => ({ data: {} })),
        connectivityAPI.getPeerExits().catch(() => ({ data: {} })),
        wireguardAPI.getPeers().catch(() => ({ data: { peers: [] } })),
      ]);

      const exitsData = exitsRes.data || {};
      // API may return array or object — normalise to object keyed by type
      if (Array.isArray(exitsData)) {
        const map = {};
        exitsData.forEach((e) => { map[e.type] = e; });
        setExits(map);
      } else {
        setExits(exitsData);
      }

      const peerExitsData = peerExitsRes.data || {};
      setPeerExits(
        Array.isArray(peerExitsData)
          ? Object.fromEntries(peerExitsData.map((p) => [p.name, p.exit_via]))
          : peerExitsData
      );

      const peersData = peersRes.data;
      const peersList = Array.isArray(peersData)
        ? peersData
        : Array.isArray(peersData?.peers)
        ? peersData.peers
        : [];
      setPeers(peersList);
    } catch (err) {
      const msg =
        err.response?.data?.error ||
        err.response?.data?.message ||
        'Could not load connectivity data. Check that the API is reachable.';
      setLoadError(msg);
    } finally {
      setIsLoading(false);
    }
  }, []);

  useEffect(() => {
    loadAll();
  }, [loadAll]);

  const handleApplyRoutes = async () => {
    setApplying(true);
    try {
      await connectivityAPI.applyRoutes();
      toastEvent('Routes applied successfully');
      await loadAll();
    } catch (err) {
      const msg =
        err.response?.data?.error ||
        err.response?.data?.message ||
        'Failed to apply routes';
      toastEvent(msg, 'error');
    } finally {
      setApplying(false);
    }
  };

  const handlePeerExitSaved = (peerName, exitVia) => {
    setPeerExits((prev) => ({ ...prev, [peerName]: exitVia }));
  };

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

  return (
    <div>
      <Toast toasts={toasts} />

      {/* Page header */}
      <div className="mb-6">
        <h1 className="text-2xl font-bold text-gray-900">Connectivity</h1>
        <p className="mt-1 text-sm text-gray-500">
          Configure exit tunnels and control how each peer's traffic is routed
        </p>
      </div>

      {/* Loading skeleton */}
      {isLoading && (
        <div className="space-y-4 animate-pulse">
          <div className="h-6 bg-gray-200 rounded w-48 mb-4" />
          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
            {[1, 2, 3].map((n) => (
              <div
                key={n}
                className="bg-white rounded-lg border border-gray-200 p-6 h-48"
              >
                <div className="h-4 bg-gray-200 rounded w-1/2 mb-3" />
                <div className="h-3 bg-gray-100 rounded w-3/4 mb-2" />
                <div className="h-3 bg-gray-100 rounded w-2/3" />
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Error state */}
      {!isLoading && loadError && (
        <div className="bg-white rounded-lg border border-red-200 bg-red-50 p-6">
          <div className="flex items-start gap-3">
            <AlertCircle className="h-5 w-5 text-red-500 mt-0.5 shrink-0" />
            <div className="flex-1">
              <p className="text-sm font-medium text-red-800">
                Failed to load connectivity data
              </p>
              <p className="text-sm text-red-600 mt-1">{loadError}</p>
            </div>
            <button
              onClick={() => { setIsLoading(true); loadAll(); }}
              className="btn-secondary text-sm shrink-0"
            >
              Retry
            </button>
          </div>
        </div>
      )}

      {/* Main content */}
      {!isLoading && !loadError && (
        <div className="space-y-10">

          {/* Section 1: Exit Tunnels */}
          <section>
            <div className="mb-4 flex items-center justify-between gap-4">
              <div>
                <h2 className="text-base font-semibold text-gray-900">
                  Exit Tunnels
                </h2>
                <p className="text-sm text-gray-500">
                  Upload VPN configs or enable Tor to create exit options for your
                  peers
                </p>
              </div>
            </div>

            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
              <WireguardExitCard
                exitInfo={exits['wireguard'] || exits['wireguard_external']}
                onUploaded={loadAll}
              />
              <OpenvpnExitCard
                exitInfo={exits['openvpn']}
                onUploaded={loadAll}
              />
              <TorExitCard
                exitInfo={exits['tor']}
                onToggled={loadAll}
              />
            </div>

            {/* Apply Routes */}
            <div className="mt-6 flex items-center justify-between gap-4 bg-gray-50 border border-gray-200 rounded-lg px-5 py-4">
              <div>
                <p className="text-sm font-medium text-gray-800">
                  Apply exit routes
                </p>
                <p className="text-xs text-gray-500 mt-0.5">
                  Commit all exit-tunnel changes to the routing table
                </p>
              </div>
              <button
                onClick={handleApplyRoutes}
                disabled={applying}
                className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 disabled:opacity-50 disabled:cursor-not-allowed rounded-md transition-colors shrink-0"
                aria-label="Apply exit routes"
              >
                <RefreshCw
                  className={`h-4 w-4 ${applying ? 'animate-spin' : ''}`}
                />
                {applying ? 'Applying…' : 'Apply Routes'}
              </button>
            </div>
          </section>

          {/* Section 2: Peer Exit Assignment */}
          <section>
            <div className="mb-4">
              <h2 className="text-base font-semibold text-gray-900">
                Peer Exit Assignment
              </h2>
              <p className="text-sm text-gray-500">
                Choose which exit tunnel each WireGuard peer uses
              </p>
            </div>

            {peers.length === 0 ? (
              <div className="bg-white rounded-lg border border-gray-200 py-12 text-center">
                <Shield className="h-10 w-10 text-gray-300 mx-auto mb-3" />
                <p className="text-sm font-medium text-gray-500">
                  No WireGuard peers found
                </p>
                <p className="text-xs text-gray-400 mt-1">
                  Add peers on the WireGuard page first, then return here to
                  assign exits.
                </p>
              </div>
            ) : (
              <div className="bg-white rounded-lg border border-gray-200 overflow-x-auto">
                <table className="min-w-full">
                  <thead>
                    <tr className="bg-gray-50 border-b border-gray-200">
                      <th className="py-3 px-4 text-left text-xs font-semibold text-gray-500 uppercase tracking-wide">
                        Peer Name
                      </th>
                      <th className="py-3 px-4 text-left text-xs font-semibold text-gray-500 uppercase tracking-wide">
                        Current Exit
                      </th>
                      <th className="py-3 px-4 text-left text-xs font-semibold text-gray-500 uppercase tracking-wide">
                        Change Exit
                      </th>
                      <th className="py-3 px-4" />
                    </tr>
                  </thead>
                  <tbody>
                    {peers.map((peer) => (
                      <PeerExitRow
                        key={peer.name}
                        peer={peer}
                        currentExit={peerExits[peer.name] || 'default'}
                        onSaved={handlePeerExitSaved}
                      />
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </section>
        </div>
      )}
    </div>
  );
}

export default Connectivity;
