'use client';

import { useCallback, useEffect, useRef, useState } from 'react';
import { formatBytes, formatDate } from '@/lib/format';
import type { TunnelItem } from '@/lib/admin/list';
import { SortHeader, downloadUrl, type SortOrder } from '../table-ui';

const PER_PAGE = 25;

type BulkResult = { ok: number; fail: number };

export function TunnelsTable({
  initialTunnels,
  initialTotal,
}: {
  initialTunnels: TunnelItem[];
  initialTotal: number;
}) {
  const [tunnels, setTunnels] = useState<TunnelItem[]>(initialTunnels);
  const [total, setTotal] = useState(initialTotal);
  const [page, setPage] = useState(1);
  const [search, setSearch] = useState('');
  const [status, setStatus] = useState('');
  const [sort, setSort] = useState('created_at');
  const [order, setOrder] = useState<SortOrder>('desc');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [notice, setNotice] = useState<string | null>(null);
  const [busyId, setBusyId] = useState<string | null>(null);
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [bulkBusy, setBulkBusy] = useState(false);
  const [bulkProgress, setBulkProgress] = useState<string | null>(null);

  const queryParams = useCallback(() => {
    const params = new URLSearchParams({
      page: String(page),
      perPage: String(PER_PAGE),
      sort,
      order,
    });
    if (search.trim()) params.set('search', search.trim());
    if (status) params.set('status', status);
    return params;
  }, [page, search, status, sort, order]);

  const load = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const res = await fetch(`/api/admin/tunnels?${queryParams().toString()}`, {
        credentials: 'same-origin',
      });
      if (!res.ok) {
        const b = (await res.json().catch(() => ({}))) as { error?: string };
        throw new Error(b.error ?? `Request failed (${res.status})`);
      }
      const data = (await res.json()) as { tunnels: TunnelItem[]; total: number };
      setTunnels(data.tunnels);
      setTotal(data.total);
      setSelected(new Set());
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setLoading(false);
    }
  }, [queryParams]);

  // First page is server-rendered; skip the on-mount fetch to avoid racing the
  // SSR session-cookie refresh (which intermittently 401'd).
  const didMount = useRef(false);
  useEffect(() => {
    if (!didMount.current) {
      didMount.current = true;
      return;
    }
    const t = setTimeout(load, 250);
    return () => clearTimeout(t);
  }, [load]);

  function onSort(col: string) {
    setPage(1);
    if (col === sort) {
      setOrder((o) => (o === 'asc' ? 'desc' : 'asc'));
    } else {
      setSort(col);
      setOrder('asc');
    }
  }

  async function act(
    id: string,
    label: string,
    url: string,
    init: RequestInit,
    confirmMsg?: string,
  ): Promise<unknown | null> {
    if (confirmMsg && !window.confirm(confirmMsg)) return null;
    setBusyId(id);
    setError(null);
    setNotice(null);
    try {
      const res = await fetch(url, { credentials: 'same-origin', ...init });
      const body = (await res.json().catch(() => ({}))) as {
        error?: string;
        [k: string]: unknown;
      };
      if (!res.ok) throw new Error(body.error ?? `Request failed (${res.status})`);
      setNotice(`${label} succeeded`);
      await load();
      return body;
    } catch (e) {
      setError((e as Error).message);
      return null;
    } finally {
      setBusyId(null);
    }
  }

  const jsonInit = (body: unknown): RequestInit => ({
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });

  async function onToggleActive(t: TunnelItem) {
    await act(
      t.user_id,
      t.is_active ? 'Deactivate' : 'Activate',
      `/api/admin/tunnels/${t.user_id}/active`,
      jsonInit({ is_active: !t.is_active }),
    );
  }

  async function onRegenerate(t: TunnelItem) {
    const body = (await act(
      t.user_id,
      'Regenerate token',
      `/api/admin/tunnels/${t.user_id}/regenerate-token`,
      { method: 'POST' },
      `Regenerate the token for ${t.subdomain}? The old token stops working.`,
    )) as { token?: string } | null;
    if (body?.token) {
      window.prompt('New token (copy it now):', body.token);
    }
  }

  async function onResetUsage(t: TunnelItem) {
    await act(
      t.user_id,
      'Reset usage',
      `/api/admin/tunnels/${t.user_id}/reset-usage`,
      { method: 'POST' },
      `Reset bandwidth usage for ${t.subdomain} to zero?`,
    );
  }

  async function onSetQuota(t: TunnelItem) {
    const input = window.prompt(
      `New quota in GiB for ${t.subdomain}:`,
      String(Math.round(t.quota_bytes / 1024 ** 3)),
    );
    if (input === null) return;
    const gib = Number(input);
    if (!Number.isFinite(gib) || gib <= 0) {
      setError('Quota must be a positive number of GiB');
      return;
    }
    await act(
      t.user_id,
      'Set quota',
      `/api/admin/tunnels/${t.user_id}/quota`,
      jsonInit({ quota_bytes: Math.round(gib * 1024 ** 3) }),
    );
  }

  async function onReassign(t: TunnelItem) {
    const input = window.prompt(
      `New subdomain for ${t.owner_email ?? t.subdomain}:`,
      t.subdomain,
    );
    if (input === null) return;
    await act(
      t.user_id,
      'Reassign',
      `/api/admin/tunnels/${t.user_id}/reassign`,
      jsonInit({ subdomain: input.trim().toLowerCase() }),
    );
  }

  async function onDelete(t: TunnelItem) {
    await act(
      t.user_id,
      'Delete tunnel',
      `/api/admin/tunnels/${t.user_id}`,
      { method: 'DELETE' },
      `Delete the tunnel ${t.subdomain}? This frees the subdomain.`,
    );
  }

  function toggleRow(id: string) {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }

  const pageIds = tunnels.map((t) => t.user_id);
  const allSelected =
    pageIds.length > 0 && pageIds.every((id) => selected.has(id));
  function toggleAll() {
    setSelected((prev) => {
      if (pageIds.every((id) => prev.has(id))) return new Set();
      return new Set(pageIds);
    });
  }

  async function runBulk(
    label: string,
    perItem: (t: TunnelItem) => Promise<boolean>,
    confirmMsg?: string,
  ) {
    const targets = tunnels.filter((t) => selected.has(t.user_id));
    if (targets.length === 0) return;
    if (confirmMsg && !window.confirm(confirmMsg)) return;
    setBulkBusy(true);
    setError(null);
    setNotice(null);
    const result: BulkResult = { ok: 0, fail: 0 };
    for (let i = 0; i < targets.length; i++) {
      setBulkProgress(`${label}: ${i + 1}/${targets.length}…`);
      try {
        const ok = await perItem(targets[i]);
        if (ok) result.ok++;
        else result.fail++;
      } catch {
        result.fail++;
      }
    }
    setBulkProgress(null);
    setBulkBusy(false);
    const parts = [`${result.ok} ${label.toLowerCase()}`];
    if (result.fail) parts.push(`${result.fail} failed`);
    setNotice(parts.join(', '));
    await load();
  }

  async function setActiveOne(t: TunnelItem, active: boolean): Promise<boolean> {
    const res = await fetch(`/api/admin/tunnels/${t.user_id}/active`, {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ is_active: active }),
    });
    return res.ok;
  }

  async function deleteOne(t: TunnelItem): Promise<boolean> {
    const res = await fetch(`/api/admin/tunnels/${t.user_id}`, {
      method: 'DELETE',
      credentials: 'same-origin',
    });
    return res.ok;
  }

  function exportCsv() {
    const params = new URLSearchParams({ sort, order });
    if (search.trim()) params.set('search', search.trim());
    if (status) params.set('status', status);
    downloadUrl(`/api/admin/tunnels/export?${params.toString()}`);
  }

  const totalPages = Math.max(1, Math.ceil(total / PER_PAGE));
  const selectedCount = selected.size;

  return (
    <div>
      <h1>Tunnels</h1>

      <div className="row" style={{ marginBottom: '1rem', flexWrap: 'wrap' }}>
        <input
          type="text"
          placeholder="Search subdomain…"
          value={search}
          onChange={(e) => {
            setPage(1);
            setSearch(e.target.value);
          }}
          style={{ maxWidth: 260 }}
        />
        <select
          value={status}
          onChange={(e) => {
            setPage(1);
            setStatus(e.target.value);
          }}
          style={{
            padding: '0.6rem 0.75rem',
            background: 'var(--bg)',
            color: 'var(--fg)',
            border: '1px solid var(--border)',
            borderRadius: 6,
          }}
        >
          <option value="">All statuses</option>
          <option value="active">Active</option>
          <option value="inactive">Inactive</option>
          <option value="over_quota">Over quota</option>
        </select>
        <button className="secondary btn-sm" type="button" onClick={load}>
          Refresh
        </button>
        <div className="toolbar-actions">
          <button
            className="secondary btn-sm"
            type="button"
            onClick={exportCsv}
          >
            Export CSV
          </button>
        </div>
      </div>

      {error && <p className="error">{error}</p>}
      {notice && <p className="success">{notice}</p>}

      {selectedCount > 0 && (
        <div className="bulk-bar">
          <span className="bulk-count">{selectedCount} selected</span>
          <button
            className="secondary btn-sm"
            type="button"
            disabled={bulkBusy}
            onClick={() => runBulk('Activated', (t) => setActiveOne(t, true))}
          >
            Activate
          </button>
          <button
            className="secondary btn-sm"
            type="button"
            disabled={bulkBusy}
            onClick={() =>
              runBulk('Deactivated', (t) => setActiveOne(t, false))
            }
          >
            Deactivate
          </button>
          <button
            className="btn-danger btn-sm"
            type="button"
            disabled={bulkBusy}
            onClick={() =>
              runBulk(
                'Deleted',
                deleteOne,
                `Delete ${selectedCount} selected tunnel(s)? This frees their subdomains.`,
              )
            }
          >
            Delete
          </button>
          <button
            className="secondary btn-sm"
            type="button"
            disabled={bulkBusy}
            onClick={() => setSelected(new Set())}
          >
            Clear
          </button>
          {bulkProgress && <span className="muted">{bulkProgress}</span>}
        </div>
      )}

      {loading ? (
        <p className="muted">Loading…</p>
      ) : tunnels.length === 0 ? (
        <p className="muted">No tunnels found.</p>
      ) : (
        <div className="admin-table-wrap">
          <table className="admin-table">
            <thead>
              <tr>
                <th className="col-check">
                  <input
                    type="checkbox"
                    aria-label="Select all on page"
                    checked={allSelected}
                    onChange={toggleAll}
                  />
                </th>
                <SortHeader
                  label="Subdomain"
                  col="subdomain"
                  sort={sort}
                  order={order}
                  onSort={onSort}
                />
                <th>Owner</th>
                <SortHeader
                  label="Status"
                  col="is_active"
                  sort={sort}
                  order={order}
                  onSort={onSort}
                />
                <SortHeader
                  label="Usage"
                  col="usage_pct"
                  sort={sort}
                  order={order}
                  onSort={onSort}
                />
                <SortHeader
                  label="Last seen"
                  col="last_seen_at"
                  sort={sort}
                  order={order}
                  onSort={onSort}
                />
                <th>Actions</th>
              </tr>
            </thead>
            <tbody>
              {tunnels.map((t) => (
                <tr
                  key={t.user_id}
                  className={selected.has(t.user_id) ? 'row-selected' : undefined}
                >
                  <td className="col-check">
                    <input
                      type="checkbox"
                      aria-label={`Select ${t.subdomain}`}
                      checked={selected.has(t.user_id)}
                      onChange={() => toggleRow(t.user_id)}
                    />
                  </td>
                  <td>{t.subdomain}</td>
                  <td style={{ wordBreak: 'break-all' }}>
                    {t.owner_email ?? '—'}
                  </td>
                  <td>
                    {t.is_active ? (
                      <span className="badge badge-ok">active</span>
                    ) : (
                      <span className="badge">inactive</span>
                    )}
                  </td>
                  <td style={{ minWidth: 140 }}>
                    <div>
                      {formatBytes(t.bytes_used)} / {formatBytes(t.quota_bytes)}
                    </div>
                    <div className="progress" style={{ marginTop: 4 }}>
                      <div
                        style={{
                          width: `${Math.min(100, t.usage_pct).toFixed(1)}%`,
                          background:
                            t.usage_pct >= 100
                              ? 'var(--danger)'
                              : 'var(--accent)',
                        }}
                      />
                    </div>
                  </td>
                  <td>{formatDate(t.last_seen_at)}</td>
                  <td>
                    <div className="row" style={{ flexWrap: 'wrap', gap: 4 }}>
                      <button
                        type="button"
                        className="secondary btn-sm"
                        disabled={busyId === t.user_id}
                        onClick={() => onToggleActive(t)}
                      >
                        {t.is_active ? 'Deactivate' : 'Activate'}
                      </button>
                      <button
                        type="button"
                        className="secondary btn-sm"
                        disabled={busyId === t.user_id}
                        onClick={() => onSetQuota(t)}
                      >
                        Quota
                      </button>
                      <button
                        type="button"
                        className="secondary btn-sm"
                        disabled={busyId === t.user_id}
                        onClick={() => onResetUsage(t)}
                      >
                        Reset
                      </button>
                      <button
                        type="button"
                        className="secondary btn-sm"
                        disabled={busyId === t.user_id}
                        onClick={() => onReassign(t)}
                      >
                        Reassign
                      </button>
                      <button
                        type="button"
                        className="secondary btn-sm"
                        disabled={busyId === t.user_id}
                        onClick={() => onRegenerate(t)}
                      >
                        Token
                      </button>
                      <button
                        type="button"
                        className="btn-danger btn-sm"
                        disabled={busyId === t.user_id}
                        onClick={() => onDelete(t)}
                      >
                        Delete
                      </button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      <div className="row" style={{ marginTop: '1rem' }}>
        <button
          className="secondary btn-sm"
          type="button"
          disabled={page <= 1}
          onClick={() => setPage((p) => Math.max(1, p - 1))}
        >
          Prev
        </button>
        <span className="muted">
          Page {page} of {totalPages} ({total} total)
        </span>
        <button
          className="secondary btn-sm"
          type="button"
          disabled={page >= totalPages}
          onClick={() => setPage((p) => p + 1)}
        >
          Next
        </button>
      </div>
    </div>
  );
}
