"use client";

import * as React from "react";
import { Fingerprint, Plus, Trash2 } from "lucide-react";
import { toast } from "sonner";

import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { customerCsrfHeader } from "@/lib/customer-csrf.client";
import { dashboardCsrfHeader } from "@/lib/csrf.client";
import {
  registerPasskeyWithOptions,
  supportsPasskeys,
  type PasskeyApiPasskey,
} from "@/lib/passkeys/browser-client";
import { errorMessageOr } from "@/lib/toast-api-error";
import type { PublicKeyCredentialCreationOptionsJSON } from "@simplewebauthn/browser";

type PasskeysManagerProps = {
  listUrl: string;
  registerOptionsUrl: string;
  registerVerifyUrl: string;
  revokeUrl: string;
  csrf: "customer" | "dashboard";
  tenant?: string;
  disabled?: boolean;
  disabledMessage?: string;
};

type ListResponse = {
  status?: string;
  message?: string;
  passkeys?: PasskeyApiPasskey[];
};

function csrfHeaders(csrf: "customer" | "dashboard", tenant?: string): Record<string, string> {
  return csrf === "customer" ? customerCsrfHeader(tenant) : dashboardCsrfHeader();
}

function formatWhen(value: string | null | undefined): string {
  if (!value) return "—";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  return date.toLocaleString();
}

export function PasskeysManager({
  listUrl,
  registerOptionsUrl,
  registerVerifyUrl,
  revokeUrl,
  csrf,
  tenant,
  disabled = false,
  disabledMessage = "Passkey management is unavailable while impersonating.",
}: PasskeysManagerProps) {
  const [supported, setSupported] = React.useState(false);
  const [loading, setLoading] = React.useState(true);
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState<string | null>(null);
  const [passkeys, setPasskeys] = React.useState<PasskeyApiPasskey[]>([]);
  const [label, setLabel] = React.useState("");

  const load = React.useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const response = await fetch(listUrl, {
        method: "GET",
        credentials: "same-origin",
        headers: { Accept: "application/json" },
        cache: "no-store",
      });
      const data = (await response.json().catch(() => null)) as ListResponse | null;
      if (!response.ok || data?.status !== "success") {
        const detail =
          (typeof data?.message === "string" && data.message.trim()) ||
          `Could not load passkeys. (HTTP ${response.status})`;
        throw new Error(detail);
      }
      setPasskeys(Array.isArray(data.passkeys) ? data.passkeys : []);
    } catch (err) {
      const message = errorMessageOr(err, "Could not load passkeys.");
      setError(message);
      setPasskeys([]);
    } finally {
      setLoading(false);
    }
  }, [listUrl]);

  React.useEffect(() => {
    setSupported(supportsPasskeys());
  }, []);

  React.useEffect(() => {
    if (disabled) {
      setLoading(false);
      return;
    }
    void load();
  }, [disabled, load]);

  async function handleRegister() {
    if (disabled || busy) return;
    setBusy(true);
    setError(null);
    try {
      const optionsResponse = await fetch(registerOptionsUrl, {
        method: "POST",
        credentials: "same-origin",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
          ...csrfHeaders(csrf, tenant),
        },
        body: "{}",
      });
      const optionsData = (await optionsResponse.json().catch(() => null)) as {
        status?: string;
        message?: string;
        options?: PublicKeyCredentialCreationOptionsJSON;
      } | null;
      if (!optionsResponse.ok || optionsData?.status !== "success" || !optionsData.options) {
        throw new Error(optionsData?.message ?? "Could not start passkey registration.");
      }

      const credential = await registerPasskeyWithOptions(optionsData.options);
      const verifyResponse = await fetch(registerVerifyUrl, {
        method: "POST",
        credentials: "same-origin",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
          ...csrfHeaders(csrf, tenant),
        },
        body: JSON.stringify({
          credential,
          label: label.trim() || null,
        }),
      });
      const verifyData = (await verifyResponse.json().catch(() => null)) as {
        status?: string;
        message?: string;
      } | null;
      if (!verifyResponse.ok || verifyData?.status !== "success") {
        throw new Error(verifyData?.message ?? "Could not save passkey.");
      }

      toast.success(verifyData.message ?? "Passkey added.");
      setLabel("");
      await load();
    } catch (err) {
      const message = errorMessageOr(err, "Could not register passkey.");
      setError(message);
      toast.error(message);
    } finally {
      setBusy(false);
    }
  }

  async function handleRevoke(id: number) {
    if (disabled || busy) return;
    if (!window.confirm("Remove this passkey? You will no longer be able to sign in with it.")) {
      return;
    }
    setBusy(true);
    setError(null);
    try {
      const response = await fetch(revokeUrl, {
        method: "POST",
        credentials: "same-origin",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
          ...csrfHeaders(csrf, tenant),
        },
        body: JSON.stringify({ id }),
      });
      const data = (await response.json().catch(() => null)) as {
        status?: string;
        message?: string;
      } | null;
      if (!response.ok || data?.status !== "success") {
        throw new Error(data?.message ?? "Could not remove passkey.");
      }
      toast.success(data.message ?? "Passkey removed.");
      await load();
    } catch (err) {
      const message = errorMessageOr(err, "Could not remove passkey.");
      setError(message);
      toast.error(message);
    } finally {
      setBusy(false);
    }
  }

  if (disabled) {
    return <p className="text-muted-foreground text-sm">{disabledMessage}</p>;
  }

  if (!supported) {
    return (
      <p className="text-muted-foreground text-sm">
        This browser does not support passkeys. Try a current Chrome, Safari, Edge, or Firefox build
        on a device with a platform authenticator.
      </p>
    );
  }

  return (
    <div className="max-w-xl space-y-5">
      {loading ? <p className="text-muted-foreground text-sm">Loading passkeys…</p> : null}
      {error ? <p className="text-destructive text-sm">{error}</p> : null}

      <ul className="space-y-2">
        {passkeys.length === 0 && !loading ? (
          <li className="text-muted-foreground text-sm">No passkeys registered yet.</li>
        ) : null}
        {passkeys.map((item) => (
          <li
            key={item.id}
            className="flex items-start justify-between gap-3 rounded-lg border px-3 py-2.5"
          >
            <div className="min-w-0 space-y-0.5">
              <p className="flex items-center gap-2 font-medium text-sm">
                <Fingerprint className="size-3.5 shrink-0 text-primary" />
                <span className="truncate">{item.label?.trim() || `Passkey #${item.id}`}</span>
              </p>
              <p className="text-muted-foreground text-xs">
                Added {formatWhen(item.created_at)}
                {item.last_used_at ? ` · Last used ${formatWhen(item.last_used_at)}` : ""}
              </p>
            </div>
            <Button
              type="button"
              variant="ghost"
              size="icon"
              className="size-8 shrink-0 text-destructive hover:text-destructive"
              disabled={busy}
              aria-label={`Remove ${item.label?.trim() || `passkey ${item.id}`}`}
              onClick={() => void handleRevoke(item.id)}
            >
              <Trash2 className="size-4" />
            </Button>
          </li>
        ))}
      </ul>

      <div className="space-y-3 rounded-lg border p-3">
        <div className="space-y-2">
          <Label htmlFor="passkey-label">Label (optional)</Label>
          <Input
            id="passkey-label"
            value={label}
            onChange={(event) => setLabel(event.target.value)}
            placeholder="e.g. MacBook Touch ID"
            disabled={busy}
            maxLength={120}
          />
        </div>
        <Button type="button" className="gap-2" disabled={busy || loading} onClick={() => void handleRegister()}>
          <Plus className="size-4" />
          {busy ? "Waiting for authenticator…" : "Add passkey"}
        </Button>
      </div>
    </div>
  );
}
