import { useState, useEffect } from 'react';
import { Link2, Link2Off, Copy, CheckCheck, RefreshCw, Plug, Unplug, Globe, Wifi, Calendar, FolderOpen, Mail, HardDrive, ChevronDown, ChevronRight, ArrowUpFromLine } from 'lucide-react';
import { cellLinkAPI } from '../services/api';
import { useConfig } from '../contexts/ConfigContext';
import QRCode from 'qrcode';

const relativeTime = (ts) => {
  if (!ts) return null;
  const diff = Math.floor((Date.now() / 1000) - (typeof ts === 'string' ? new Date(ts).getTime() / 1000 : ts));
  if (diff < 60) return `${diff}s ago`;
  if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
  if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
  return `${Math.floor(diff / 86400)}d ago`;
};

const SERVICE_DEFS = [
  { key: 'calendar', label: 'Calendar', Icon: Calendar },
  { key: 'files',    label: 'Files',    Icon: FolderOpen },
  { key: 'mail',     label: 'Mail',     Icon: Mail },
  { key: 'webdav',   label: 'WebDAV',   Icon: HardDrive },
];

function CopyButton({ text, small }) {
  const [copied, setCopied] = useState(false);
  const copy = () => {
    if (navigator.clipboard && window.isSecureContext) {
      navigator.clipboard.writeText(text);
    } else {
      const el = document.createElement('textarea');
      el.value = text;
      el.style.cssText = 'position:fixed;opacity:0;top:0;left:0';
      document.body.appendChild(el);
      el.select();
      document.execCommand('copy');
      document.body.removeChild(el);
    }
    setCopied(true);
    setTimeout(() => setCopied(false), 1500);
  };
  const sz = small ? 'h-3.5 w-3.5' : 'h-4 w-4';
  return (
    <button onClick={copy} className="text-gray-400 hover:text-gray-600 ml-1.5" title="Copy">
      {copied ? <CheckCheck className={`${sz} text-green-500`} /> : <Copy className={sz} />}
    </button>
  );
}

function StatusDot({ online }) {
  if (online === null || online === undefined) {
    return <span className="inline-block h-2 w-2 rounded-full bg-gray-300 mr-1.5" title="Unknown" />;
  }
  return online
    ? <span className="inline-block h-2 w-2 rounded-full bg-green-500 mr-1.5" title="Online" />
    : <span className="inline-block h-2 w-2 rounded-full bg-red-400 mr-1.5" title="Offline" />;
}

function SyncBadge({ conn }) {
  const { last_push_status, last_push_at, last_push_error, pending_push, next_retry_at } = conn;
  let color, label, tip;
  if (last_push_status === 'never' || (!last_push_at && !pending_push)) {
    color = 'bg-gray-300'; label = 'Sync pending';
    tip = 'Permissions not yet synced to remote cell';
  } else if (!pending_push && last_push_status === 'ok') {
    color = 'bg-green-500'; label = `Synced${last_push_at ? ' ' + relativeTime(last_push_at) : ''}`;
    tip = `Permissions last synced ${last_push_at ? relativeTime(last_push_at) : ''}`;
  } else {
    color = 'bg-amber-400'; label = 'Out of sync';
    tip = last_push_error ? `Sync failed: ${last_push_error}` : 'Permissions pending sync';
    if (next_retry_at) {
      const retryIn = Math.max(0, Math.round((new Date(next_retry_at) - Date.now()) / 60000));
      tip += ` — next retry in ~${retryIn}m`;
    }
  }
  return (
    <span className="inline-flex items-center gap-1 text-xs text-gray-500" title={tip}>
      <span className={`inline-block h-2 w-2 rounded-full ${color}`} />
      {label}
    </span>
  );
}

function Toast({ toasts }) {
  return (
    <div className="fixed bottom-4 right-4 z-50 space-y-2">
      {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 ${
          t.type === 'error' ? 'bg-red-600' : 'bg-green-600'
        }`}>
          {t.msg}
        </div>
      ))}
    </div>
  );
}

function useToasts() {
  const [toasts, setToasts] = useState([]);
  const add = (msg, type = 'success') => {
    const id = Date.now();
    setToasts(p => [...p, { id, msg, type }]);
    setTimeout(() => setToasts(p => p.filter(t => t.id !== id)), 4000);
  };
  return [toasts, add];
}

function DisconnectConfirmModal({ cellName, onConfirm, onCancel }) {
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
      <div className="bg-white rounded-xl shadow-2xl p-6 max-w-sm w-full mx-4">
        <div className="flex items-center gap-3 mb-3">
          <Unplug className="h-6 w-6 text-red-500 flex-shrink-0" />
          <h3 className="text-base font-semibold text-gray-900">Disconnect "{cellName}"?</h3>
        </div>
        <p className="text-sm text-gray-600 mb-2">
          This will remove the WireGuard tunnel and all sharing permissions between your cells.
        </p>
        <p className="text-xs text-gray-400 mb-5">
          The other cell's admin will need to remove the connection on their end too. Shared services will become inaccessible immediately.
        </p>
        <div className="flex gap-3 justify-end">
          <button onClick={onCancel} autoFocus className="btn btn-secondary text-sm">Cancel</button>
          <button onClick={onConfirm} className="btn btn-danger text-sm flex items-center gap-2">
            <Unplug className="h-4 w-4" /> Disconnect
          </button>
        </div>
      </div>
    </div>
  );
}

function ServiceShareToggle({ serviceKey, label, Icon, enabled, saving, onChange }) {
  return (
    <label className="flex items-center gap-3 cursor-pointer select-none py-1">
      <div
        role="switch"
        aria-checked={enabled}
        aria-label={`Share ${label}`}
        tabIndex={0}
        onClick={() => !saving && onChange(!enabled)}
        onKeyDown={e => (e.key === 'Enter' || e.key === ' ') && !saving && onChange(!enabled)}
        className={`relative w-10 h-5 rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-primary-400 focus:ring-offset-1 ${
          enabled ? 'bg-primary-500' : 'bg-gray-300'
        } ${saving ? 'opacity-60 cursor-wait' : 'cursor-pointer'}`}
      >
        <span className={`absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full shadow transition-transform ${
          enabled ? 'translate-x-5' : ''
        }`} />
      </div>
      <Icon className="h-4 w-4 text-gray-500 shrink-0" />
      <span className="text-sm text-gray-700">{label}</span>
    </label>
  );
}

function InboundServiceBadge({ label, Icon, active }) {
  return (
    <span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium ${
      active ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-400'
    }`}>
      <Icon className="h-3 w-3" />
      {label}
    </span>
  );
}

function CellPanel({ conn, onDisconnect, addToast }) {
  const [open, setOpen] = useState(false);
  const [inboundPerms, setInboundPerms] = useState(conn.permissions?.inbound || {});
  const [saving, setSaving] = useState({});
  const [confirmDisconnect, setConfirmDisconnect] = useState(false);
  const [exitOffered, setExitOffered] = useState(!!conn.exit_offered);
  const [savingExit, setSavingExit] = useState(false);

  const handleToggle = async (serviceKey, newValue) => {
    setSaving(s => ({ ...s, [serviceKey]: true }));
    const newInbound = { ...inboundPerms, [serviceKey]: newValue };
    try {
      await cellLinkAPI.updatePermissions(conn.cell_name, newInbound, conn.permissions?.outbound || {});
      setInboundPerms(newInbound);
      addToast(`${serviceKey} sharing ${newValue ? 'enabled' : 'disabled'}`, 'success');
    } catch {
      addToast('Failed to save sharing permission', 'error');
    } finally {
      setSaving(s => ({ ...s, [serviceKey]: false }));
    }
  };

  const handleExitToggle = async (newValue) => {
    setSavingExit(true);
    try {
      await cellLinkAPI.setExitOffer(conn.cell_name, newValue);
      setExitOffered(newValue);
      addToast(`Internet sharing ${newValue ? 'offered' : 'withdrawn'}`);
    } catch {
      addToast('Failed to update internet sharing', 'error');
    } finally {
      setSavingExit(false);
    }
  };

  const hasRevokedService = Object.values(inboundPerms).some(v => !v);

  return (
    <>
      {confirmDisconnect && (
        <DisconnectConfirmModal
          cellName={conn.cell_name}
          onConfirm={() => { setConfirmDisconnect(false); onDisconnect(conn.cell_name); }}
          onCancel={() => setConfirmDisconnect(false)}
        />
      )}
      <div className="border border-gray-100 rounded-lg overflow-hidden">
        <button
          className="w-full flex items-center justify-between px-4 py-3 bg-gray-50 hover:bg-gray-100 transition-colors text-left"
          onClick={() => setOpen(v => !v)}
          aria-expanded={open}
        >
          <div className="flex items-center gap-3 min-w-0">
            <StatusDot online={conn.online} />
            <div className="min-w-0">
              <div className="flex items-center gap-1.5 min-w-0">
                <span className="font-medium text-gray-900 truncate">{conn.cell_name}</span>
                <span className="text-xs text-gray-400 font-mono shrink-0">.{conn.domain}</span>
              </div>
              <div className="flex flex-wrap gap-x-3 text-xs text-gray-500 mt-0.5">
                <span className={conn.online ? 'text-green-600 font-medium' : 'text-gray-400'}>
                  {conn.online ? 'Online' : 'Offline'}
                </span>
                {conn.last_handshake && (
                  <span>{relativeTime(conn.last_handshake)}</span>
                )}
                <SyncBadge conn={conn} />
              </div>
            </div>
          </div>
          {open
            ? <ChevronDown className="h-4 w-4 text-gray-400 shrink-0" />
            : <ChevronRight className="h-4 w-4 text-gray-400 shrink-0" />}
        </button>

        {open && (
          <div className="px-4 py-4 bg-white border-t border-gray-100">
            <div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
              <div>
                <p className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3">
                  I share with {conn.cell_name}
                </p>
                <div className="space-y-1">
                  {SERVICE_DEFS.map(({ key, label, Icon }) => (
                    <ServiceShareToggle
                      key={key}
                      serviceKey={key}
                      label={label}
                      Icon={Icon}
                      enabled={!!inboundPerms[key]}
                      saving={!!saving[key]}
                      onChange={v => handleToggle(key, v)}
                    />
                  ))}
                </div>
                {hasRevokedService && (
                  <p className="text-xs text-gray-400 mt-2">
                    Services you stop sharing become unreachable from {conn.cell_name} immediately.
                  </p>
                )}
              </div>
              <div>
                <p className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3">
                  {conn.cell_name} shares with me
                </p>
                {(conn.permissions?.outbound && Object.values(conn.permissions.outbound).some(Boolean)) ? (
                  <div className="flex flex-wrap gap-1.5">
                    {SERVICE_DEFS.map(({ key, label, Icon }) => (
                      <InboundServiceBadge
                        key={key}
                        label={label}
                        Icon={Icon}
                        active={!!conn.permissions.outbound[key]}
                      />
                    ))}
                  </div>
                ) : (
                  <p className="text-xs text-gray-400">Nothing shared yet.</p>
                )}
                <p className="text-xs text-gray-400 mt-3">
                  Inbound sharing is set by the other cell's admin.
                </p>
              </div>
            </div>
            {/* ── Internet sharing ───────────────────────────────────── */}
            <div className="mt-4 pt-4 border-t border-gray-100">
              <p className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-3 flex items-center gap-1.5">
                <ArrowUpFromLine className="h-3.5 w-3.5" /> Internet Sharing
              </p>
              <div className="flex flex-col sm:flex-row sm:items-center gap-4">
                <ServiceShareToggle
                  serviceKey="internet"
                  label={`Offer my internet to ${conn.cell_name}`}
                  Icon={Globe}
                  enabled={exitOffered}
                  saving={savingExit}
                  onChange={handleExitToggle}
                />
                <div className={`flex items-center gap-1.5 text-sm ${conn.remote_exit_offered ? 'text-green-700' : 'text-gray-400'}`}>
                  <span className={`inline-block h-2 w-2 rounded-full flex-shrink-0 ${conn.remote_exit_offered ? 'bg-green-500' : 'bg-gray-300'}`} />
                  {conn.remote_exit_offered
                    ? <>{conn.cell_name} offers internet</>
                    : <>{conn.cell_name} doesn't offer internet</>}
                </div>
              </div>
              {exitOffered && (
                <p className="text-xs text-gray-400 mt-2">
                  The {conn.cell_name} admin can route their peers' internet through your connection via Peers → Edit peer → Internet Exit.
                </p>
              )}
              {conn.remote_exit_offered && (
                <p className="text-xs text-gray-400 mt-2">
                  You can route any local peer's internet through {conn.cell_name} via Peers → Edit peer → Internet Exit.
                </p>
              )}
            </div>

            <div className="mt-4 pt-3 border-t border-gray-100 flex flex-wrap items-center justify-between gap-3">
              <dl className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-gray-500">
                {conn.vpn_subnet && <div><dt className="inline text-gray-400">Subnet </dt><dd className="inline font-mono">{conn.vpn_subnet}</dd></div>}
                {conn.endpoint && <div><dt className="inline text-gray-400">Endpoint </dt><dd className="inline font-mono">{conn.endpoint}</dd></div>}
              </dl>
              <button
                onClick={() => setConfirmDisconnect(true)}
                className="btn btn-danger flex items-center gap-2 text-sm py-1.5"
              >
                <Unplug className="h-4 w-4" />
                Disconnect
              </button>
            </div>
          </div>
        )}
      </div>
    </>
  );
}

export default function CellNetwork() {
  const { cell_name = 'mycell', domain = 'cell' } = useConfig();
  const [toasts, addToast] = useToasts();

  const [invite, setInvite] = useState(null);
  const [inviteQr, setInviteQr] = useState('');
  const [inviteLoading, setInviteLoading] = useState(true);

  const [connections, setConnections] = useState([]);
  const [connsLoading, setConnsLoading] = useState(true);

  const [pasteText, setPasteText] = useState('');
  const [pasteError, setPasteError] = useState('');
  const [connecting, setConnecting] = useState(false);

  useEffect(() => {
    loadInvite();
    loadConnections();
  }, []);

  const loadInvite = async () => {
    setInviteLoading(true);
    try {
      const r = await cellLinkAPI.getInvite();
      setInvite(r.data);
      const qr = await QRCode.toDataURL(JSON.stringify(r.data), { width: 200, margin: 1 });
      setInviteQr(qr);
    } catch (e) {
      addToast('Failed to load invite', 'error');
    } finally {
      setInviteLoading(false);
    }
  };

  const loadConnections = async () => {
    setConnsLoading(true);
    try {
      const r = await cellLinkAPI.listConnections();
      const conns = r.data || [];

      // Fetch all WireGuard peer statuses in one call and index by public_key
      let statusByKey = {};
      try {
        const { wireguardAPI } = await import('../services/api');
        const sr = await wireguardAPI.getPeerStatuses();
        // API returns {pubkey: {online, last_handshake, ...}} — no .peers wrapper
        const raw = sr.data || {};
        const entries = Array.isArray(raw.peers)
          ? raw.peers.map(p => [p.public_key, p])
          : Object.entries(raw);
        entries.forEach(([pk, info]) => { if (pk) statusByKey[pk] = info; });
      } catch {
        // Status enrichment is best-effort; continue without it
      }

      const enriched = conns.map(conn => {
        const wg = conn.public_key ? statusByKey[conn.public_key] : null;
        return {
          ...conn,
          online: wg ? wg.online : false,
          last_handshake: wg ? wg.last_handshake : null,
        };
      });

      setConnections(enriched);
    } catch {
      addToast('Failed to load connections', 'error');
    } finally {
      setConnsLoading(false);
    }
  };

  const validatePaste = (text) => {
    if (!text.trim()) { setPasteError(''); return; }
    try {
      const p = JSON.parse(text.trim());
      if (!p.cell_name || !p.public_key || !p.vpn_subnet) {
        setPasteError('JSON is missing required fields (cell_name, public_key, vpn_subnet)');
      } else {
        setPasteError('');
      }
    } catch {
      setPasteError('Not valid JSON — paste the complete invite from the other cell');
    }
  };

  const handleConnect = async () => {
    if (!pasteText.trim()) return;
    let parsed;
    try {
      parsed = JSON.parse(pasteText.trim());
    } catch {
      addToast('Invalid JSON — paste the full invite from the other cell', 'error');
      return;
    }
    setConnecting(true);
    try {
      await cellLinkAPI.addConnection(parsed);
      addToast(`Connected to cell "${parsed.cell_name}"`);
      setPasteText('');
      setPasteError('');
      loadConnections();
    } catch (e) {
      addToast(e?.response?.data?.error || 'Connection failed', 'error');
    } finally {
      setConnecting(false);
    }
  };

  // Confirmation is handled inside CellPanel via DisconnectConfirmModal
  const handleDisconnect = async (name) => {
    try {
      await cellLinkAPI.removeConnection(name);
      addToast(`Disconnected from "${name}"`);
      loadConnections();
    } catch (e) {
      addToast(e?.response?.data?.error || 'Disconnect failed', 'error');
    }
  };

  const inviteJson = invite ? JSON.stringify(invite, null, 2) : '';

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

      <div className="mb-8">
        <h1 className="text-2xl font-bold text-gray-900">Cell Network</h1>
        <p className="mt-2 text-gray-600">
          Connect multiple PIC cells into a mesh — site-to-site WireGuard tunnels with automatic DNS forwarding.
        </p>
      </div>

      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">

        {/* ── This cell's invite ─────────────────────────────────────────── */}
        <div className="card">
          <div className="flex items-center justify-between mb-4">
            <div className="flex items-center gap-2">
              <Plug className="h-5 w-5 text-primary-500" />
              <h3 className="text-lg font-medium text-gray-900">Your Cell's Invite</h3>
            </div>
            <button onClick={loadInvite} className="text-gray-400 hover:text-gray-600" title="Refresh">
              <RefreshCw className="h-4 w-4" />
            </button>
          </div>

          {inviteLoading ? (
            <div className="flex justify-center py-8">
              <div className="animate-spin rounded-full h-6 w-6 border-b-2 border-primary-600" />
            </div>
          ) : invite ? (
            <div className="space-y-4">
              <div className="bg-gray-50 rounded-lg p-3 space-y-1 text-xs">
                <div className="flex justify-between gap-2 min-w-0">
                  <span className="text-gray-500 shrink-0">Cell</span>
                  <span className="font-mono font-medium truncate" title={invite.cell_name}>{invite.cell_name}</span>
                </div>
                <div className="flex justify-between gap-2 min-w-0">
                  <span className="text-gray-500 shrink-0">Domain</span>
                  <span className="font-mono font-medium truncate" title={invite.domain}>{invite.domain}</span>
                </div>
                <div className="flex justify-between gap-2 min-w-0">
                  <span className="text-gray-500 shrink-0">Endpoint</span>
                  <span className="font-mono font-medium truncate" title={invite.endpoint || '(no external IP)'}>{invite.endpoint || '(no external IP)'}</span>
                </div>
                <div className="flex justify-between gap-2 min-w-0">
                  <span className="text-gray-500 shrink-0">VPN subnet</span>
                  <span className="font-mono font-medium truncate" title={invite.vpn_subnet}>{invite.vpn_subnet}</span>
                </div>
              </div>

              <div>
                <div className="flex items-center justify-between mb-1">
                  <span className="text-sm text-gray-600">Invite JSON</span>
                  <CopyButton text={inviteJson} />
                </div>
                <pre className="bg-gray-900 text-green-400 text-xs rounded-lg p-3 overflow-x-auto max-h-40">
                  {inviteJson}
                </pre>
              </div>

              {inviteQr && (
                <div className="text-center">
                  <p className="text-xs text-gray-500 mb-2">Or scan with phone camera</p>
                  <img src={inviteQr} alt="Invite QR" className="inline-block border rounded-lg p-1 bg-white" />
                </div>
              )}

              <p className="text-xs text-gray-400">
                Share this JSON with the admin of another PIC cell. They paste it in "Connect to Cell" on their side.
              </p>
            </div>
          ) : (
            <p className="text-gray-500 text-sm">Could not load invite.</p>
          )}
        </div>

        {/* ── Connect to another cell ────────────────────────────────────── */}
        <div className="card">
          <div className="flex items-center gap-2 mb-4">
            <Link2 className="h-5 w-5 text-primary-500" />
            <h3 className="text-lg font-medium text-gray-900">Connect to Another Cell</h3>
          </div>

          <div className="space-y-3">
            <p className="text-sm text-gray-600">
              Paste the invite JSON from the other cell's "Your Cell's Invite" panel:
            </p>
            <div>
              <textarea
                value={pasteText}
                onChange={e => { setPasteText(e.target.value); if (pasteError) validatePaste(e.target.value); }}
                onBlur={e => validatePaste(e.target.value)}
                placeholder={'{\n  "cell_name": "...",\n  "public_key": "...",\n  ...\n}'}
                rows={8}
                className={`w-full text-xs font-mono border rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-primary-400 resize-none bg-white ${
                  pasteError ? 'border-red-400 focus:ring-red-400' : ''
                }`}
              />
              {pasteError && <p className="text-xs text-red-600 mt-1">{pasteError}</p>}
            </div>
            <button
              onClick={handleConnect}
              disabled={connecting || !pasteText.trim() || !!pasteError}
              className="w-full btn btn-primary flex items-center justify-center gap-2 disabled:opacity-50"
            >
              {connecting
                ? <><div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white" /> Connecting…</>
                : <><Link2 className="h-4 w-4" /> Connect</>
              }
            </button>
          </div>

          <div className="mt-4 bg-blue-50 border border-blue-200 rounded-lg p-3">
            <p className="text-xs text-blue-800 font-medium mb-1">How it works</p>
            <ol className="text-xs text-blue-700 space-y-1 list-decimal list-inside">
              <li>Cell A copies its invite and sends it to Cell B's admin</li>
              <li>Cell B pastes the invite and clicks Connect</li>
              <li>Cell B copies its invite and sends it back to Cell A</li>
              <li>Cell A pastes Cell B's invite and clicks Connect</li>
              <li>Both cells can now reach each other's VPN peers and services</li>
            </ol>
          </div>
        </div>

        {/* ── Connected cells ────────────────────────────────────────────── */}
        <div className="card lg:col-span-2">
          <div className="flex items-center justify-between mb-4">
            <div className="flex items-center gap-2">
              <Globe className="h-5 w-5 text-primary-500" />
              <h3 className="text-lg font-medium text-gray-900">Connected Cells</h3>
            </div>
            <button onClick={loadConnections} className="text-gray-400 hover:text-gray-600" title="Refresh">
              <RefreshCw className="h-4 w-4" />
            </button>
          </div>

          {connsLoading ? (
            <div className="flex justify-center py-6">
              <div className="animate-spin rounded-full h-6 w-6 border-b-2 border-primary-600" />
            </div>
          ) : connections.length === 0 ? (
            <div className="text-center py-8 text-gray-400">
              <Wifi className="h-10 w-10 mx-auto mb-2 opacity-30" />
              <p className="text-sm">No cells connected yet.</p>
              <p className="text-xs mt-1">Use the panels above to establish the first cell link.</p>
            </div>
          ) : (
            <div className="space-y-3">
              {connections.map(conn => (
                <CellPanel
                  key={conn.cell_name}
                  conn={conn}
                  onDisconnect={handleDisconnect}
                  addToast={addToast}
                />
              ))}
            </div>
          )}
        </div>

      </div>
    </div>
  );
}
