"use client";

import { useEffect, useState } from "react";
import { Button, Field, Input } from "@/components/ui/form";

type AdminBetaRequestDto = {
  id: number;
  uuid: string;
  email: string;
  displayName?: string | null;
  invited: boolean;
  verified: boolean;
  active: boolean;
  createdAt?: string;
  updatedAt?: string;
};

type PageResponse<T> = {
  content: T[];
  number: number;
  size: number;
  totalElements: number;
  totalPages: number;
};

// Expected invite response shape (adjust fields if your DTO differs)
type AdminInviteResponse = {
  ok?: boolean;
  email?: string;

  // backend returns this
  inviteUrl?: string;

  // keep these optional if you ever add them later
  magicUrl?: string;
  token?: string;
};

async function fetchJson(path: string, init: RequestInit = {}) {
  const res = await fetch(path, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...(init.headers ?? {}),
    },
    cache: "no-store",
  });

  const text = await res.text().catch(() => "");
  const data = text
    ? (() => {
        try {
          return JSON.parse(text);
        } catch {
          return text;
        }
      })()
    : null;

  if (!res.ok) {
    const msg =
      typeof data === "string"
        ? data
        : data?.message || data?.error || `Request failed (${res.status})`;
    throw new Error(msg);
  }

  return data;
}

function Badge({
  ok,
  yes = "Yes",
  no = "No",
}: {
  ok: boolean;
  yes?: string;
  no?: string;
}) {
  return (
    <span
      className={[
        "inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium",
        ok
          ? "border border-emerald-500/30 bg-emerald-500/10 text-emerald-200"
          : "border border-zinc-700 bg-zinc-950/40 text-zinc-400",
      ].join(" ")}
    >
      {ok ? yes : no}
    </span>
  );
}

export default function AdminBetaInvitesPage() {
  // ------------------------------
  // Batch invites
  // ------------------------------
  const [dryRun, setDryRun] = useState(true);
  const [limit, setLimit] = useState("25");
  const [tokenMinutes, setTokenMinutes] = useState("30");
  const [loadingBatch, setLoadingBatch] = useState(false);
  const [batchResult, setBatchResult] = useState<any>(null);
  const [batchError, setBatchError] = useState<string | null>(null);

  async function runInvites() {
    setLoadingBatch(true);
    setBatchError(null);
    setBatchResult(null);

    try {
      const qs = new URLSearchParams({
        dryRun: String(dryRun),
        limit: String(limit || "0"),
        tokenMinutes: String(tokenMinutes || "30"),
      });

      const data = await fetchJson(`/api/admin/beta-invites?${qs.toString()}`, {
        method: "POST",
      });

      setBatchResult(data);
      await loadRequests();
    } catch (e: any) {
      setBatchError(e?.message ?? "Failed");
    } finally {
      setLoadingBatch(false);
    }
  }

  // ------------------------------
  // Requests table
  // ------------------------------
  const [page, setPage] = useState(0);
  const size = 25;

  const [loadingList, setLoadingList] = useState(false);
  const [listError, setListError] = useState<string | null>(null);
  const [requests, setRequests] =
    useState<PageResponse<AdminBetaRequestDto> | null>(null);

  // per-row loading state
  const [rowBusy, setRowBusy] = useState<Record<number, boolean>>({});

  // last invite response (for visibility / debug)
  const [lastInvite, setLastInvite] = useState<AdminInviteResponse | null>(
    null
  );

  async function loadRequests() {
    setLoadingList(true);
    setListError(null);

    try {
      const data = (await fetchJson(
        `/api/admin/beta/requests?page=${page}&size=${size}`,
        { method: "GET" }
      )) as PageResponse<AdminBetaRequestDto>;

      setRequests(data);
    } catch (e: any) {
      setListError(e?.message ?? "Failed to load beta requests");
    } finally {
      setLoadingList(false);
    }
  }

  async function inviteSingle(userId: number): Promise<AdminInviteResponse> {
    // calls backend: POST /api/v1/admin/beta/requests/{userId}/invite
    return (await fetchJson(`/api/admin/beta/requests/${userId}/invite`, {
      method: "POST",
    })) as AdminInviteResponse;
  }

  async function onSendInviteEmail(userId: number) {
    setListError(null);
    setLastInvite(null);
    setRowBusy((m) => ({ ...m, [userId]: true }));

    try {
      const resp = await inviteSingle(userId);
      setLastInvite(resp);
      await loadRequests();
    } catch (e: any) {
      setListError(e?.message ?? "Failed to send invite");
    } finally {
      setRowBusy((m) => ({ ...m, [userId]: false }));
    }
  }

  async function onCopyInviteLink(userId: number) {
    setListError(null);
    setLastInvite(null);
    setRowBusy((m) => ({ ...m, [userId]: true }));

    try {
      const resp = await inviteSingle(userId);
      setLastInvite(resp);
      await loadRequests();

      // build URL
      const url =
        resp.inviteUrl || //  what your backend returns
        resp.magicUrl ||
        (resp.token
          ? `${window.location.origin}/beta/magic?token=${resp.token}`
          : null);

      if (!url) {
        throw new Error(
          `Invite response missing inviteUrl. Backend should return {"inviteUrl": "..."}`
        );
      }

      await navigator.clipboard.writeText(url);
    } catch (e: any) {
      setListError(e?.message ?? "Failed to copy invite link");
    } finally {
      setRowBusy((m) => ({ ...m, [userId]: false }));
    }
  }

  useEffect(() => {
    loadRequests();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [page]);

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-xl font-semibold">Beta Access</h1>
        <p className="mt-1 text-sm text-zinc-400">
          Review pending beta requests and send invite links (batch or
          per-user).
        </p>
      </div>

      <div className="grid gap-6 lg:grid-cols-[280px_minmax(0,1fr)] xl:grid-cols-[320px_minmax(0,1fr)]">
        {/* Left: Batch Invites */}
        <div className="rounded-lg border border-zinc-900 bg-zinc-950 p-4 space-y-4">
          <div className="flex items-center justify-between gap-3">
            <div>
              <h2 className="text-sm font-semibold uppercase tracking-[0.18em] text-zinc-500">
                Batch Invites
              </h2>
              <p className="mt-1 text-sm text-zinc-400">
                Runs the backend batch invite sender.
              </p>
            </div>

            <span className="shrink-0 rounded-full border border-amber-500/30 bg-amber-500/10 px-3 py-1 text-xs text-amber-200">
              {dryRun ? "Dry run" : "Live send"}
            </span>
          </div>

          <label className="flex items-center gap-2 text-sm text-zinc-200">
            <input
              type="checkbox"
              checked={dryRun}
              onChange={(e) => setDryRun(e.target.checked)}
            />
            Dry run (do everything except actually send)
          </label>

          <div className="grid gap-4 md:grid-cols-2">
            <Field label="Limit" htmlFor="limit">
              <Input
                id="limit"
                value={limit}
                onChange={(e) => setLimit(e.target.value)}
              />
            </Field>

            <Field label="Token minutes" htmlFor="tokenMinutes">
              <Input
                id="tokenMinutes"
                value={tokenMinutes}
                onChange={(e) => setTokenMinutes(e.target.value)}
              />
            </Field>
          </div>

          <Button onClick={runInvites} disabled={loadingBatch}>
            {loadingBatch ? "Running…" : "Send Beta Invites"}
          </Button>

          {batchError && (
            <div className="rounded-md border border-red-500/40 bg-red-500/10 px-3 py-2 text-sm text-red-200">
              {batchError}
            </div>
          )}

          {batchResult && (
            <pre className="overflow-auto rounded-md border border-zinc-800 bg-black/40 p-3 text-xs text-zinc-200">
              {JSON.stringify(batchResult, null, 2)}
            </pre>
          )}
        </div>

        {/* Right: Requests List */}
        <div className="rounded-lg border border-zinc-900 bg-zinc-950 p-4 space-y-4">
          <div className="flex items-start justify-between gap-3">
            <div>
              <h2 className="text-sm font-semibold uppercase tracking-[0.18em] text-zinc-500">
                Beta Requests
              </h2>
              <p className="mt-1 text-sm text-zinc-400">
                Users with role <span className="text-zinc-200">BETA</span> and{" "}
                <span className="text-zinc-200">is_active=false</span>.
              </p>
            </div>

            <Button
              onClick={loadRequests}
              disabled={loadingList}
              className="w-auto px-6"
            >
              {loadingList ? "Refreshing…" : "Refresh"}
            </Button>
          </div>

          {listError && (
            <div className="rounded-md border border-red-500/40 bg-red-500/10 px-3 py-2 text-sm text-red-200">
              {listError}
            </div>
          )}

          <div className="overflow-x-auto rounded-md border border-zinc-800">
            {" "}
            <table className="min-w-[680px] w-full text-left text-sm">
              <thead className="bg-black/30 text-xs text-zinc-500">
                <tr className="border-b border-zinc-800">
                  <th className="px-3 py-2">Email</th>
                  <th className="px-3 py-2">Invited</th>
                  <th className="px-3 py-2">Verified</th>
                  <th className="px-3 py-2">Active</th>
                  <th className="px-3 py-2 text-right w-[190px] whitespace-nowrap">
                    Actions
                  </th>
                </tr>
              </thead>

              <tbody className="text-zinc-200">
                {requests?.content?.length ? (
                  requests.content.map((u) => {
                    const busy = !!rowBusy[u.id];

                    return (
                      <tr key={u.id} className="border-b border-zinc-900">
                        <td className="px-3 py-2">
                          <div className="font-medium">{u.email}</div>
                          <div className="text-xs text-zinc-500">
                            {u.displayName ?? "—"} • #{u.id}
                          </div>
                        </td>

                        <td className="px-3 py-2">
                          <Badge ok={u.invited} yes="Invited" no="—" />
                        </td>
                        <td className="px-3 py-2">
                          <Badge ok={u.verified} yes="Verified" no="—" />
                        </td>
                        <td className="px-3 py-2">
                          <Badge ok={u.active} yes="Active" no="Inactive" />
                        </td>

                        <td className="px-3 py-2 w-[190px]">
                          <div className="flex justify-end gap-2">
                            <button
                              onClick={() => onCopyInviteLink(u.id)}
                              disabled={busy}
                              className="rounded-md border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs text-zinc-200 hover:bg-zinc-900 disabled:opacity-60"
                              title="Dev helper: generate invite and copy URL (also triggers invite generation server-side)"
                            >
                              {busy ? "Working…" : "Copy link (dev)"}
                            </button>

                            <button
                              onClick={() => onSendInviteEmail(u.id)}
                              disabled={busy}
                              className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-1.5 text-xs text-amber-200 hover:bg-amber-500/15 disabled:opacity-60"
                              title="Send invite email to this user"
                            >
                              {busy ? "Working…" : "Send invite email"}
                            </button>
                          </div>
                        </td>
                      </tr>
                    );
                  })
                ) : (
                  <tr>
                    <td className="px-3 py-6 text-sm text-zinc-400" colSpan={5}>
                      {loadingList ? "Loading…" : "No pending beta requests."}
                    </td>
                  </tr>
                )}
              </tbody>
            </table>
          </div>

          {/* pager */}
          <div className="flex items-center justify-between text-xs text-zinc-500">
            <button
              className="rounded-md border border-zinc-800 bg-black/30 px-2 py-1 disabled:opacity-50"
              disabled={!requests || requests.number <= 0 || loadingList}
              onClick={() => setPage((p) => Math.max(0, p - 1))}
            >
              Prev
            </button>

            <span>
              Page {requests ? requests.number + 1 : 1} of{" "}
              {requests ? requests.totalPages : 1} •{" "}
              {requests ? requests.totalElements : 0} total
            </span>

            <button
              className="rounded-md border border-zinc-800 bg-black/30 px-2 py-1 disabled:opacity-50"
              disabled={
                !requests ||
                loadingList ||
                requests.number >= requests.totalPages - 1
              }
              onClick={() => setPage((p) => p + 1)}
            >
              Next
            </button>
          </div>

          {/* optional debug panel */}
          {lastInvite && (
            <pre className="overflow-auto rounded-md border border-zinc-800 bg-black/40 p-3 text-xs text-zinc-200">
              {JSON.stringify(lastInvite, null, 2)}
            </pre>
          )}
        </div>
      </div>
    </div>
  );
}
