"use client";

import { useState } from "react";
import { KeyRound, Plus } from "lucide-react";
import { Badge, Button, ConfirmDialog, Dialog, EmptyState, Field, Input, PageHeader, Panel, Select, Skeleton } from "@/components/ui/kit";
import { useToast } from "@/components/ui/toast";
import { useSession } from "@/components/session";
import { FilterTabs, Pager } from "@/components/admin/common";
import { CopyField, DevTabs } from "@/components/developers/common";
import { api, ApiError, qs } from "@/lib/api";
import { formatDateTime } from "@/lib/format";
import { useApi } from "@/lib/use-api";

interface ApiKey {
  id: number;
  name: string;
  preview: string;
  scopes: string[];
  rate_limit_per_minute: number;
  expires_at: string | null;
  previous_expires_at: string | null;
  last_used_at: string | null;
  last_used_ip: string | null;
  status: "active" | "revoked" | "expired";
  created_by_name: string | null;
  created_at: string;
}
type ScopeInfo = { scope: string; description: string };

function KeyForm({ initial, scopes, onSubmit, busy, errors, id }: { initial?: ApiKey; scopes: ScopeInfo[]; onSubmit: (body: Record<string, unknown>) => void; busy: boolean; errors: Record<string, string>; id: string }) {
  const [chosen, setChosen] = useState<string[]>(initial?.scopes ?? ["contacts:read"]);
  return (
    <form
      id={id}
      className="space-y-4"
      noValidate
      onSubmit={(e) => {
        e.preventDefault();
        const f = new FormData(e.currentTarget);
        onSubmit({
          name: f.get("name"),
          scopes: chosen,
          rate_limit_per_minute: Number(f.get("rate")),
          ...(initial ? {} : { expires_in_days: f.get("expires") ? Number(f.get("expires")) : null }),
        });
      }}
    >
      <Field label="Name" htmlFor="k-name" error={errors.name} hint="Something that tells you where it's used, like “Shopify sync”.">
        <Input id="k-name" name="name" defaultValue={initial?.name} invalid={!!errors.name} disabled={busy} />
      </Field>
      <fieldset>
        <legend className="mb-1.5 text-sm font-medium">Permissions</legend>
        {errors.scopes && <p className="mb-1 text-[13px] text-brick">{errors.scopes}</p>}
        <div className="space-y-1.5">
          {scopes.map((s) => (
            <label key={s.scope} className="flex items-start gap-2.5 rounded-md px-2 py-1.5 text-sm hover:bg-paper">
              <input type="checkbox" className="mt-0.5 size-4 accent-teal" checked={chosen.includes(s.scope)} onChange={(e) => setChosen((c) => (e.target.checked ? [...c, s.scope] : c.filter((x) => x !== s.scope)))} />
              <span>
                <code className="font-mono text-[13px]">{s.scope}</code>
                <span className="block text-[13px] text-muted">{s.description}</span>
              </span>
            </label>
          ))}
        </div>
      </fieldset>
      <div className="grid gap-4 sm:grid-cols-2">
        <Field label="Requests per minute" htmlFor="k-rate" error={errors.rate_limit_per_minute}>
          <Input id="k-rate" name="rate" type="number" min={1} max={600} defaultValue={initial?.rate_limit_per_minute ?? 60} />
        </Field>
        {!initial && (
          <Field label="Expires" htmlFor="k-exp">
            <Select id="k-exp" name="expires" defaultValue="">
              <option value="">Never</option>
              <option value="30">In 30 days</option>
              <option value="90">In 90 days</option>
              <option value="365">In a year</option>
            </Select>
          </Field>
        )}
      </div>
    </form>
  );
}

function RequestLog({ k, onClose }: { k: ApiKey; onClose: () => void }) {
  const [status, setStatus] = useState<"all" | "errors">("all");
  const [page, setPage] = useState(1);
  const { data } = useApi<{ data: { id: number; method: string; path: string; status: number; duration_ms: number; ip: string | null; error_code: string | null; created_at: string }[]; meta: { total: number; last_page: number } }>(`/developers/api-keys/${k.id}/requests${qs({ status, page })}`);
  return (
    <Dialog open onClose={onClose} title={`Requests: ${k.name}`} description="The last 30 days of calls made with this key." size="lg" footer={<Button onClick={onClose}>Close</Button>}>
      <div className="mb-3">
        <FilterTabs label="Requests" value={status} onChange={(v) => (setStatus(v), setPage(1))} options={[["all", "All"], ["errors", "Errors"]]} />
      </div>
      {!data ? (
        <Skeleton className="h-40" />
      ) : data.data.length === 0 ? (
        <p className="py-8 text-center text-sm text-muted">No requests yet.</p>
      ) : (
        <>
          <ul className="divide-y divide-line rounded-lg border border-line">
            {data.data.map((r) => (
              <li key={r.id} className="flex flex-wrap items-center gap-x-3 gap-y-1 px-3 py-2 text-sm">
                <Badge tone={r.status >= 500 || r.status === 429 ? "brick" : r.status >= 400 ? "amber" : "teal"}>{r.status}</Badge>
                <code className="min-w-0 flex-1 truncate font-mono text-[13px]">
                  {r.method} {r.path}
                </code>
                <span className="text-[13px] text-muted">
                  {r.error_code ? `${r.error_code}, ` : ""}
                  {r.duration_ms} ms, {formatDateTime(r.created_at)}
                </span>
              </li>
            ))}
          </ul>
          <Pager page={page} last={data.meta.last_page} total={data.meta.total} onPage={setPage} />
        </>
      )}
    </Dialog>
  );
}

export default function ApiKeysPage() {
  const { can } = useSession();
  const toast = useToast();
  const manage = can("developers.manage");
  const { data, error, reload } = useApi<{ data: ApiKey[] }>("/developers/api-keys");
  const scopes = useApi<{ data: ScopeInfo[] }>("/developers/api-keys/scopes");
  const [dialog, setDialog] = useState<{ kind: "create" } | { kind: "edit" | "rotate" | "revoke" | "log"; key: ApiKey } | null>(null);
  const [revealed, setRevealed] = useState<{ name: string; key: string } | null>(null);
  const [busy, setBusy] = useState(false);
  const [errors, setErrors] = useState<Record<string, string>>({});
  const [grace, setGrace] = useState("24");

  async function run<T>(fn: () => Promise<T>, after: (r: T) => void) {
    setBusy(true);
    setErrors({});
    try {
      after(await fn());
      reload();
    } catch (e) {
      if (e instanceof ApiError && Object.keys(e.fields).length) setErrors(e.fields);
      else toast("error", (e as Error).message);
    } finally {
      setBusy(false);
    }
  }

  return (
    <>
      <PageHeader
        title="Developers"
        description="Connect your own systems to Devwebs with the REST API and signed webhooks."
        actions={
          manage && (
            <Button variant="primary" icon={<Plus className="size-4" />} onClick={() => (setErrors({}), setDialog({ kind: "create" }))}>
              New API key
            </Button>
          )
        }
      />
      <DevTabs />
      {error && <p className="mb-4 rounded-md bg-brick-soft px-4 py-3 text-sm text-brick">{error}</p>}
      <Panel>
        {!data ? (
          <Skeleton className="m-5 h-32" />
        ) : data.data.length === 0 ? (
          <EmptyState icon={<KeyRound />} title="No API keys yet" body="Create a key for each system that calls the API, with only the permissions it needs." action={manage ? <Button variant="primary" onClick={() => setDialog({ kind: "create" })}>New API key</Button> : undefined} />
        ) : (
          <ul className="divide-y divide-line">
            {data.data.map((k) => (
              <li key={k.id} className="flex flex-wrap items-center gap-x-4 gap-y-2 px-5 py-4">
                <div className="min-w-0 flex-1 basis-64">
                  <p className="flex flex-wrap items-center gap-2 font-medium">
                    {k.name}
                    <Badge tone={k.status === "active" ? "teal" : "brick"}>{k.status === "active" ? "Active" : k.status === "revoked" ? "Revoked" : "Expired"}</Badge>
                    {k.previous_expires_at && <Badge tone="amber">Old secret works until {formatDateTime(k.previous_expires_at)}</Badge>}
                  </p>
                  <p className="mt-0.5 font-mono text-[13px] text-muted">{k.preview}</p>
                  <p className="mt-1 flex flex-wrap gap-1">
                    {k.scopes.map((s) => (
                      <code key={s} className="rounded bg-paper px-1.5 py-0.5 font-mono text-[12px]">
                        {s}
                      </code>
                    ))}
                  </p>
                  <p className="mt-1 text-[13px] text-muted">
                    {k.rate_limit_per_minute}/min, {k.last_used_at ? `last used ${formatDateTime(k.last_used_at)}` : "never used"}
                    {k.expires_at ? `, expires ${formatDateTime(k.expires_at)}` : ""}, created by {k.created_by_name ?? "a former teammate"}
                  </p>
                </div>
                <div className="flex flex-wrap gap-2">
                  <Button size="sm" onClick={() => setDialog({ kind: "log", key: k })}>
                    Requests
                  </Button>
                  {manage && k.status === "active" && (
                    <>
                      <Button size="sm" onClick={() => (setErrors({}), setDialog({ kind: "edit", key: k }))}>
                        Edit
                      </Button>
                      <Button size="sm" onClick={() => (setGrace("24"), setDialog({ kind: "rotate", key: k }))}>
                        Rotate
                      </Button>
                      <Button size="sm" variant="ghost" className="hover:bg-brick-soft hover:text-brick" onClick={() => setDialog({ kind: "revoke", key: k })}>
                        Revoke
                      </Button>
                    </>
                  )}
                </div>
              </li>
            ))}
          </ul>
        )}
      </Panel>

      {dialog?.kind === "create" && scopes.data && (
        <Dialog open onClose={() => setDialog(null)} title="New API key" size="md" footer={<><Button onClick={() => setDialog(null)}>Cancel</Button><Button variant="primary" type="submit" form="key-form" loading={busy}>Create key</Button></>}>
          <KeyForm id="key-form" scopes={scopes.data.data} busy={busy} errors={errors} onSubmit={(body) => run(() => api.post<{ data: ApiKey; key: string }>("/developers/api-keys", body), (r) => (setDialog(null), setRevealed({ name: r.data.name, key: r.key })))} />
        </Dialog>
      )}
      {dialog?.kind === "edit" && scopes.data && (
        <Dialog open onClose={() => setDialog(null)} title={`Edit ${dialog.key.name}`} size="md" footer={<><Button onClick={() => setDialog(null)}>Cancel</Button><Button variant="primary" type="submit" form="key-edit" loading={busy}>Save</Button></>}>
          <KeyForm id="key-edit" initial={dialog.key} scopes={scopes.data.data} busy={busy} errors={errors} onSubmit={(body) => run(() => api.patch(`/developers/api-keys/${dialog.key.id}`, body), () => (setDialog(null), toast("success", "Key updated")))} />
        </Dialog>
      )}
      {dialog?.kind === "rotate" && (
        <Dialog
          open
          onClose={() => setDialog(null)}
          title={`Rotate ${dialog.key.name}?`}
          description="You get a new secret for the same key. Update your integration, then the old secret stops working."
          footer={<><Button onClick={() => setDialog(null)}>Cancel</Button><Button variant="primary" loading={busy} onClick={() => run(() => api.post<{ data: ApiKey; key: string }>(`/developers/api-keys/${dialog.key.id}/rotate`, { grace_hours: Number(grace) }), (r) => (setDialog(null), setRevealed({ name: r.data.name, key: r.key })))}>Rotate</Button></>}
        >
          <Field label="Keep the old secret working for" htmlFor="k-grace">
            <Select id="k-grace" value={grace} onChange={(e) => setGrace(e.target.value)}>
              <option value="0">No time (stop it now)</option>
              <option value="1">1 hour</option>
              <option value="24">24 hours</option>
              <option value="168">7 days</option>
            </Select>
          </Field>
        </Dialog>
      )}
      <ConfirmDialog
        open={dialog?.kind === "revoke"}
        title={`Revoke ${dialog && "key" in dialog ? dialog.key.name : ""}?`}
        message="Every request with this key fails from now on. This can't be undone; create a new key if you need one again."
        confirmLabel="Revoke key"
        loading={busy}
        onConfirm={() => dialog && "key" in dialog && run(() => api.post(`/developers/api-keys/${dialog.key.id}/revoke`), () => (setDialog(null), toast("success", "Key revoked")))}
        onClose={() => setDialog(null)}
      />
      {dialog?.kind === "log" && <RequestLog k={dialog.key} onClose={() => setDialog(null)} />}
      {revealed && (
        <Dialog open onClose={() => setRevealed(null)} title={`Your key: ${revealed.name}`} description="Copy it now and store it somewhere safe, like your server's secret manager. You won't see it again." footer={<Button variant="primary" onClick={() => setRevealed(null)}>I&apos;ve copied it</Button>}>
          <CopyField value={revealed.key} label="API key" />
          <p className="mt-3 text-[13px] text-muted">
            Send it as <code className="font-mono">Authorization: Bearer &lt;key&gt;</code>.
          </p>
        </Dialog>
      )}
    </>
  );
}
