"use client";

import { useEffect, useRef, useState, useTransition } from "react";
import {
  beginRegistration,
  finishRegistration,
  deletePasskey,
} from "@/app/auth-actions";
import { startRegistration } from "@simplewebauthn/browser";
import ConfirmDialog from "@/components/confirm-dialog";
import FormDialogShell, { Field, inputClass } from "@/components/form-dialog-shell";
import Toast, { type ToastMessage } from "@/components/toast";

type Passkey = {
  id: string;
  counter: number;
  transports: string[];
  name: string;
  createdAt: string;
};

type Props = {
  initial: Passkey[];
  username: string;
};

function relativeTime(iso: string): string {
  const then = new Date(iso).getTime();
  if (!Number.isFinite(then)) return iso;
  const now = Date.now();
  const seconds = Math.max(1, Math.round((now - then) / 1000));
  if (seconds < 60) return `${seconds}s ago`;
  const minutes = Math.round(seconds / 60);
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.round(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.round(hours / 24);
  if (days < 30) return `${days}d ago`;
  return new Date(iso).toLocaleDateString();
}

export default function PasskeyList({ initial, username: _username }: Props) {
  const [list, setList] = useState<Passkey[]>(initial);
  const [addOpen, setAddOpen] = useState(false);
  const [deviceName, setDeviceName] = useState("");
  const [addError, setAddError] = useState<string | null>(null);
  const [addPending, startAdd] = useTransition();
  const [removeTarget, setRemoveTarget] = useState<Passkey | null>(null);
  const [removePending, startRemove] = useTransition();
  const [removeError, setRemoveError] = useState<string | null>(null);
  const [toast, setToast] = useState<ToastMessage | null>(null);
  const nameInputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    setList(initial);
  }, [initial]);

  useEffect(() => {
    if (!addOpen) return;
    setDeviceName("");
    setAddError(null);
    if (typeof window !== "undefined") {
      const isPointerDevice = window.matchMedia(
        "(hover: hover) and (pointer: fine)",
      ).matches;
      if (isPointerDevice) {
        requestAnimationFrame(() => nameInputRef.current?.focus());
      }
    }
  }, [addOpen]);

  function submitAdd() {
    const name = deviceName.trim();
    if (!name) {
      setAddError("Device name is required");
      return;
    }
    setAddError(null);
    startAdd(async () => {
      try {
        const options = await beginRegistration();
        const response = await startRegistration({ optionsJSON: options });
        const result = await finishRegistration(response, name);
        if (result.ok) {
          setAddOpen(false);
          setToast({ type: "success", message: "Passkey added" });
        } else {
          setAddError(result.error);
        }
      } catch (err) {
        const message =
          err instanceof Error ? err.message : "Enrollment failed";
        if (
          message.toLowerCase().includes("notallowed") ||
          message.toLowerCase().includes("cancel")
        ) {
          setAddError("Enrollment was cancelled");
        } else {
          setAddError(message);
        }
      }
    });
  }

  function submitRemove() {
    const target = removeTarget;
    if (!target) return;
    setRemoveError(null);
    startRemove(async () => {
      const result = await deletePasskey(target.id);
      if (result.ok) {
        setRemoveTarget(null);
        setToast({ type: "success", message: "Passkey removed" });
      } else {
        setRemoveError(result.error);
      }
    });
  }

  return (
    <div className="space-y-8">
      <div className="flex flex-wrap items-end justify-between gap-4">
        <div>
          <p className="text-[11px] font-medium uppercase tracking-wider text-zinc-500">
            Settings
          </p>
          <h1 className="mt-1 text-2xl font-semibold tracking-tight text-zinc-900 sm:text-3xl">
            Passkeys
            <span className="ml-2 align-middle text-base font-medium text-zinc-400">
              {list.length}
            </span>
          </h1>
        </div>
        <button
          type="button"
          onClick={() => setAddOpen(true)}
          className="inline-flex items-center gap-1.5 rounded-full bg-zinc-900 px-4 py-2 text-xs font-medium text-white shadow-sm transition-colors hover:bg-zinc-700"
        >
          <span aria-hidden="true">+</span>
          Add passkey
        </button>
      </div>

      {list.length === 0 ? (
        <div className="rounded-2xl bg-white p-10 text-center ring-1 ring-zinc-200/60">
          <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-emerald-50 ring-1 ring-emerald-100">
            <svg
              aria-hidden="true"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.6"
              strokeLinecap="round"
              strokeLinejoin="round"
              className="h-5 w-5 text-emerald-700"
            >
              <path d="M7 11V8a5 5 0 0 1 10 0v3" />
              <rect x="5" y="11" width="14" height="10" rx="2" />
            </svg>
          </div>
          <p className="mt-4 text-sm font-medium text-zinc-900">
            No passkeys enrolled yet
          </p>
          <p className="mx-auto mt-1 max-w-sm text-[13px] text-zinc-500">
            Add one to sign in with Face ID, Touch ID, or fingerprint on this
            device.
          </p>
        </div>
      ) : (
        <ul className="space-y-2">
          {list.map((p) => (
            <li
              key={p.id}
              className="flex items-center gap-3 rounded-xl bg-white px-4 py-3 ring-1 ring-zinc-200/60"
            >
              <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-zinc-100 text-zinc-600">
                <svg
                  aria-hidden="true"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="1.8"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  className="h-4 w-4"
                >
                  <path d="M7 11V8a5 5 0 0 1 10 0v3" />
                  <rect x="5" y="11" width="14" height="10" rx="2" />
                </svg>
              </span>
              <div className="min-w-0 flex-1">
                <p className="truncate text-sm font-semibold text-zinc-900">
                  {p.name}
                </p>
                <p className="text-[11px] text-zinc-500">
                  Added {relativeTime(p.createdAt)}
                </p>
              </div>
              <button
                type="button"
                onClick={() => {
                  setRemoveError(null);
                  setRemoveTarget(p);
                }}
                aria-label={`Remove passkey ${p.name}`}
                className="inline-flex h-7 w-7 items-center justify-center rounded-md text-zinc-400 transition-colors hover:bg-red-50 hover:text-red-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-400"
              >
                <span aria-hidden="true" className="text-base leading-none">
                  ×
                </span>
              </button>
            </li>
          ))}
        </ul>
      )}

      <FormDialogShell
        open={addOpen}
        onCancel={() => {
          if (!addPending) setAddOpen(false);
        }}
        onSubmit={submitAdd}
        title="Add passkey"
        submitLabel="Continue"
        pending={addPending}
        error={addError}
      >
        <Field label="Device name" required hint="e.g. iPhone 15, MacBook Pro">
          <input
            ref={nameInputRef}
            value={deviceName}
            onChange={(e) => setDeviceName(e.target.value)}
            disabled={addPending}
            autoComplete="off"
            spellCheck={false}
            placeholder="iPhone 15"
            className={inputClass}
          />
        </Field>
        <p className="text-[11px] text-zinc-500">
          Your browser will prompt for Face ID, Touch ID, or fingerprint.
        </p>
      </FormDialogShell>

      <ConfirmDialog
        open={removeTarget !== null}
        onCancel={() => {
          if (!removePending) setRemoveTarget(null);
        }}
        onConfirm={submitRemove}
        title="Remove passkey?"
        message={
          <>
            Remove the passkey{" "}
            <span className="font-mono text-zinc-900">
              {removeTarget?.name ?? ""}
            </span>
            ? You&rsquo;ll lose the ability to sign in from this device until you
            enroll it again.
            {removeError && (
              <span className="mt-2 block font-mono text-[11px] text-red-600">
                {removeError}
              </span>
            )}
          </>
        }
        confirmLabel="Remove"
        destructive
        pending={removePending}
      />

      <Toast toast={toast} onDismiss={() => setToast(null)} />
    </div>
  );
}
