"use client";

import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
import { ArrowLeft, Send } from "lucide-react";
import { Badge, Button, ConfirmDialog, Dialog, Field, 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 { api, ApiError, qs } from "@/lib/api";
import { formatDateTime } from "@/lib/format";
import { useApi } from "@/lib/use-api";
import { CopyField, DevTabs } from "./common";
import { EndpointForm, type Endpoint, type EventType } from "./endpoint-form";

interface Delivery {
  id: number;
  event_id: string;
  event_type: string;
  status: "pending" | "delivering" | "succeeded" | "failed";
  attempts: number;
  last_status_code: number | null;
  last_error: string | null;
  next_attempt_at: string | null;
  delivered_at: string | null;
  created_at: string;
}
interface DeliveryDetail {
  id: number;
  status: Delivery["status"];
  next_attempt_at: string | null;
  event: { id: string; type: string; payload: unknown };
  attempts: { id: number; attempt: number; status_code: number | null; error: string | null; response_excerpt: string | null; duration_ms: number; created_at: string }[];
}
const STATUS: Record<Delivery["status"], { label: string; tone: "teal" | "amber" | "brick" | "neutral" }> = {
  succeeded: { label: "Delivered", tone: "teal" },
  pending: { label: "Retrying", tone: "amber" },
  delivering: { label: "Sending", tone: "neutral" },
  failed: { label: "Failed", tone: "brick" },
};

function DeliveryDialog({ endpointId, id, canRetry, onClose, onRetried }: { endpointId: number; id: number; canRetry: boolean; onClose: () => void; onRetried: () => void }) {
  const toast = useToast();
  const { data, reload } = useApi<{ data: DeliveryDetail }>(`/developers/webhooks/${endpointId}/deliveries/${id}`);
  const [busy, setBusy] = useState(false);
  async function retry() {
    setBusy(true);
    try {
      const r = await api.post<{ data: { ok: boolean; status: number | null; error: string | null } }>(`/developers/webhooks/${endpointId}/deliveries/${id}/retry`);
      toast(r.data.ok ? "success" : "error", r.data.ok ? `Delivered (HTTP ${r.data.status})` : `Still failing: ${r.data.error}`);
      reload();
      onRetried();
    } catch (e) {
      toast("error", (e as Error).message);
    } finally {
      setBusy(false);
    }
  }
  return (
    <Dialog
      open
      onClose={onClose}
      title={data ? data.data.event.type : "Delivery"}
      description={data ? `Event ${data.data.event.id}` : undefined}
      size="lg"
      footer={
        <>
          <Button onClick={onClose}>Close</Button>
          {canRetry && data && data.data.status !== "delivering" && (
            <Button variant="primary" icon={<Send className="size-4" />} loading={busy} onClick={retry}>
              Send again now
            </Button>
          )}
        </>
      }
    >
      {!data ? (
        <Skeleton className="h-48" />
      ) : (
        <div className="space-y-4">
          <div>
            <h3 className="mb-1.5 text-sm font-semibold">Attempts</h3>
            {data.data.attempts.length === 0 ? (
              <p className="text-sm text-muted">Not sent yet{data.data.next_attempt_at ? `; due ${formatDateTime(data.data.next_attempt_at)}` : ""}.</p>
            ) : (
              <ul className="divide-y divide-line rounded-lg border border-line">
                {data.data.attempts.map((a) => (
                  <li key={a.id} className="px-3 py-2 text-sm">
                    <p className="flex flex-wrap items-center gap-2">
                      <span className="font-medium">#{a.attempt}</span>
                      <Badge tone={a.status_code && a.status_code < 300 ? "teal" : "brick"}>{a.status_code ? `HTTP ${a.status_code}` : "No response"}</Badge>
                      <span className="text-[13px] text-muted">
                        {a.duration_ms} ms, {formatDateTime(a.created_at)}
                      </span>
                    </p>
                    {a.error && <p className="mt-1 text-[13px] text-brick">{a.error}</p>}
                    {a.response_excerpt && <pre className="mt-1 max-h-24 overflow-auto rounded bg-paper p-2 font-mono text-[12px]">{a.response_excerpt}</pre>}
                  </li>
                ))}
              </ul>
            )}
            {data.data.status === "pending" && data.data.next_attempt_at && <p className="mt-1.5 text-[13px] text-muted">Next automatic try {formatDateTime(data.data.next_attempt_at)}.</p>}
          </div>
          <div>
            <h3 className="mb-1.5 text-sm font-semibold">Payload</h3>
            <pre className="max-h-72 overflow-auto rounded-lg bg-ink p-3 font-mono text-[12px] leading-relaxed text-white">{JSON.stringify(data.data.event.payload, null, 2)}</pre>
          </div>
        </div>
      )}
    </Dialog>
  );
}

export function EndpointDetail() {
  const { id } = useParams<{ id: string }>();
  const router = useRouter();
  const toast = useToast();
  const { can } = useSession();
  const manage = can("developers.manage");
  const list = useApi<{ data: Endpoint[] }>("/developers/webhooks");
  const types = useApi<{ data: EventType[] }>("/developers/webhooks/event-types");
  const [status, setStatus] = useState<"all" | "pending" | "succeeded" | "failed">("all");
  const [page, setPage] = useState(1);
  const deliveries = useApi<{ data: Delivery[]; meta: { total: number; last_page: number } }>(`/developers/webhooks/${id}/deliveries${qs({ status, page })}`);
  const [dialog, setDialog] = useState<"edit" | "delete" | "rotate" | "secret" | null>(null);
  const [open, setOpen] = useState<number | null>(null);
  const [busy, setBusy] = useState(false);
  const [errors, setErrors] = useState<Record<string, string>>({});
  const [secret, setSecret] = useState<string | null>(null);
  const [grace, setGrace] = useState("24");
  const e = list.data?.data.find((x) => String(x.id) === id);

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

  if (list.data && !e) return <p className="rounded-md bg-brick-soft px-4 py-3 text-sm text-brick">Webhook endpoint not found.</p>;
  if (!e) return <Skeleton className="h-96" />;
  return (
    <>
      <Link href="/developers/webhooks" className="mb-3 inline-flex items-center gap-1.5 text-sm text-muted hover:text-teal">
        <ArrowLeft className="size-4" /> Webhooks
      </Link>
      <PageHeader
        title="Webhook endpoint"
        description={<span className="break-all font-mono text-[13px]">{e.url}</span>}
        actions={
          manage && (
            <div className="flex flex-wrap gap-2">
              <Button icon={<Send className="size-4" />} loading={busy && dialog === null} onClick={() => run(() => api.post<{ data: { ok: boolean; status: number | null; error: string | null } }>(`/developers/webhooks/${e.id}/test`), (r) => toast(r.data.ok ? "success" : "error", r.data.ok ? `Test event delivered (HTTP ${r.data.status})` : `Test failed: ${r.data.error}`))}>
                Send test event
              </Button>
              <Button onClick={() => run(() => api.patch(`/developers/webhooks/${e.id}`, { is_active: !e.is_active }), () => toast("success", e.is_active ? "Endpoint switched off" : "Endpoint switched on"))}>{e.is_active ? "Switch off" : "Switch on"}</Button>
              <Button onClick={() => (setErrors({}), setDialog("edit"))}>Edit</Button>
            </div>
          )
        }
      />
      <DevTabs />
      <div className="mb-6 grid gap-6 lg:grid-cols-3">
        <Panel className="p-5 lg:col-span-2">
          <p className="flex flex-wrap items-center gap-2 text-sm">
            <Badge tone={e.is_active ? (e.consecutive_failures ? "amber" : "teal") : "brick"}>{e.is_active ? (e.consecutive_failures ? `Failing (${e.consecutive_failures} in a row)` : "Active") : "Switched off"}</Badge>
            {e.description}
          </p>
          {!e.is_active && e.disabled_reason && <p className="mt-2 text-sm text-brick">{e.disabled_reason}</p>}
          <p className="mt-3 text-sm text-muted">Events: {e.events.includes("*") ? "all" : e.events.join(", ")}</p>
          <p className="mt-1 text-sm text-muted">
            Last success: {formatDateTime(e.last_success_at)}; last failure: {formatDateTime(e.last_failure_at)}
          </p>
        </Panel>
        <Panel className="p-5">
          <h2 className="mb-2 font-semibold">Signing secret</h2>
          {e.secret_rotating_until && <p className="mb-2 text-[13px] text-amber">Requests carry signatures for the old and new secret until {formatDateTime(e.secret_rotating_until)}.</p>}
          {manage && (
            <div className="flex flex-wrap gap-2">
              <Button size="sm" onClick={() => run(() => api.get<{ data: { secret: string } }>(`/developers/webhooks/${e.id}/secret`), (r) => (setSecret(r.data.secret), setDialog("secret")))}>
                Reveal
              </Button>
              <Button size="sm" onClick={() => (setGrace("24"), setDialog("rotate"))}>
                Rotate
              </Button>
              <Button size="sm" variant="ghost" className="hover:bg-brick-soft hover:text-brick" onClick={() => setDialog("delete")}>
                Delete endpoint
              </Button>
            </div>
          )}
        </Panel>
      </div>
      <Panel>
        <div className="flex flex-wrap items-center justify-between gap-2 border-b border-line px-5 py-3">
          <h2 className="font-semibold">Deliveries</h2>
          <FilterTabs label="Delivery status" value={status} onChange={(v) => (setStatus(v), setPage(1))} options={[["all", "All"], ["failed", "Failed"], ["pending", "Retrying"], ["succeeded", "Delivered"]]} />
        </div>
        {!deliveries.data ? (
          <Skeleton className="m-5 h-32" />
        ) : deliveries.data.data.length === 0 ? (
          <p className="px-5 py-10 text-center text-sm text-muted">No deliveries {status === "all" ? "yet. Send a test event to check your endpoint." : "with this status."}</p>
        ) : (
          <>
            <ul className="divide-y divide-line">
              {deliveries.data.data.map((d) => (
                <li key={d.id}>
                  <button type="button" onClick={() => setOpen(d.id)} className="flex w-full flex-wrap items-center gap-x-3 gap-y-1 px-5 py-3 text-left text-sm hover:bg-paper">
                    <Badge tone={STATUS[d.status].tone}>{STATUS[d.status].label}</Badge>
                    <code className="font-mono text-[13px]">{d.event_type}</code>
                    <span className="min-w-0 flex-1 truncate text-[13px] text-muted">
                      {d.attempts} attempt{d.attempts === 1 ? "" : "s"}
                      {d.last_status_code ? `, HTTP ${d.last_status_code}` : ""}
                      {d.last_error && d.status !== "succeeded" ? `, ${d.last_error}` : ""}
                    </span>
                    <span className="text-[13px] text-muted">{formatDateTime(d.created_at)}</span>
                  </button>
                </li>
              ))}
            </ul>
            <Pager page={page} last={deliveries.data.meta.last_page} total={deliveries.data.meta.total} onPage={setPage} />
          </>
        )}
      </Panel>

      {open !== null && <DeliveryDialog endpointId={e.id} id={open} canRetry={manage} onClose={() => setOpen(null)} onRetried={() => (deliveries.reload(), list.reload())} />}
      {dialog === "edit" && types.data && (
        <Dialog open onClose={() => setDialog(null)} title="Edit endpoint" size="md" footer={<><Button onClick={() => setDialog(null)}>Cancel</Button><Button variant="primary" type="submit" form="ep-edit" loading={busy}>Save</Button></>}>
          <EndpointForm id="ep-edit" initial={e} types={types.data.data} errors={errors} busy={busy} onSubmit={(body) => run(() => api.patch(`/developers/webhooks/${e.id}`, body), () => (setDialog(null), toast("success", "Endpoint saved")))} />
        </Dialog>
      )}
      {dialog === "rotate" && (
        <Dialog
          open
          onClose={() => setDialog(null)}
          title="Rotate signing secret?"
          description="Requests get signatures for both the old and new secret during the grace period, so you can update your server without dropping events."
          footer={<><Button onClick={() => setDialog(null)}>Cancel</Button><Button variant="primary" loading={busy} onClick={() => run(() => api.post<{ secret: string }>(`/developers/webhooks/${e.id}/rotate-secret`, { grace_hours: Number(grace) }), (r) => (setSecret(r.secret), setDialog("secret")))}>Rotate</Button></>}
        >
          <Field label="Also sign with the old secret for" htmlFor="ep-grace">
            <Select id="ep-grace" value={grace} onChange={(ev) => setGrace(ev.target.value)}>
              <option value="0">No time</option>
              <option value="24">24 hours</option>
              <option value="168">7 days</option>
            </Select>
          </Field>
        </Dialog>
      )}
      {dialog === "secret" && secret && (
        <Dialog open onClose={() => (setDialog(null), setSecret(null))} title="Signing secret" description="Keep it on your server only." footer={<Button variant="primary" onClick={() => (setDialog(null), setSecret(null))}>Done</Button>}>
          <CopyField value={secret} label="Signing secret" />
        </Dialog>
      )}
      <ConfirmDialog open={dialog === "delete"} title="Delete this endpoint?" message="Its delivery history is deleted too. Events stop being sent to this URL immediately." confirmLabel="Delete endpoint" loading={busy} onConfirm={() => run(() => api.delete(`/developers/webhooks/${e.id}`), () => (toast("success", "Endpoint deleted"), router.push("/developers/webhooks")))} onClose={() => setDialog(null)} />
    </>
  );
}
