"use client";

import { useMemo, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { AlertTriangle, RefreshCw, Webhook } from "lucide-react";
import Link from "next/link";
import { Skeleton } from "@/components/ui/skeleton";
import { toast } from "sonner";
import { PageHeader } from "@/components/shared/page-header";
import { Pagination } from "@/components/shared/pagination";
import { EmptyState, ErrorState, TableSkeleton } from "@/components/shared/states";
import { WebhookStatusBadge } from "@/components/shared/store-badges";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import {
  getWebhookDelivery,
  getWebhookDeliveries,
  replayWebhookDelivery,
  WEBHOOK_EVENTS,
  WEBHOOK_STATUSES,
  type WebhookFilters,
} from "@/services/integration";
import { queryKeys } from "@/lib/query-keys";
import { useUrlState } from "@/lib/use-url-state";
import { useI18n, useT } from "@/i18n";
import { useCan } from "@/features/auth/use-can";
import { formatDateTime } from "@/lib/format";
import { friendlyErrorMessage } from "@/lib/errors";
import type { Locale } from "@/i18n/types";
import type { WebhookDelivery, WebhookDeliveryStatus } from "@/types/store";

/**
 * What we have tried to tell each store.
 *
 * This screen exists for the one failure in the integration with **no symptom on our side**: if
 * the queue worker stops, orders keep arriving, dispatchers keep assigning and captains keep
 * delivering — every other screen looks perfectly healthy — while no store hears anything at
 * all. The first sign would otherwise be a phone call, a day late.
 *
 * That is why the two summary counts are promoted to banners rather than left as numbers in a
 * row: a tile nobody reads is not a warning.
 */
export default function IntegrationPage() {
  const t = useT();
  const { locale } = useI18n();
  const { can } = useCan();
  const queryClient = useQueryClient();

  const [expanded, setExpanded] = useState<string | null>(null);

  const [page, setPage] = useUrlState("page", 1, (value) => {
    const n = Number(value);
    return Number.isFinite(n) && n >= 1 ? n : null;
  });
  const [status, setStatus] = useUrlState<WebhookDeliveryStatus | "">("status", "", (value) =>
    (WEBHOOK_STATUSES as string[]).includes(value) ? (value as WebhookDeliveryStatus) : null,
  );
  const [event, setEvent] = useUrlState("event", "", (value) =>
    (WEBHOOK_EVENTS as readonly string[]).includes(value) ? value : null,
  );

  const filters = useMemo<WebhookFilters>(
    () => ({ page, rows: 10, status: status || undefined, event: event || undefined }),
    [page, status, event],
  );

  const { data, isLoading, isError, error, refetch } = useQuery({
    queryKey: queryKeys.integration.webhooks(filters),
    queryFn: () => getWebhookDeliveries(filters),
  });

  const replay = useMutation({
    mutationFn: (uuid: string) => replayWebhookDelivery(uuid),
    onSuccess: () => {
      toast.success(t("integration.replayed"));
      void queryClient.invalidateQueries({ queryKey: ["integration", "webhooks"] });
    },
    onError: (err) => toast.error(friendlyErrorMessage(err, t)),
  });

  const deliveries = data?.Model ?? [];
  const summary = data?.Summary;

  return (
    <div>
      <PageHeader title={t("integration.title")} subtitle={t("integration.subtitle")} />

      {summary ? (
        <div className="mb-4 grid grid-cols-2 gap-3 sm:grid-cols-4">
          <SummaryTile label={t("integration.summarySent")} value={summary.sent} />
          <SummaryTile label={t("integration.summaryPending")} value={summary.pending} warn={summary.pending > 0} />
          <SummaryTile label={t("integration.summaryFailed")} value={summary.failed} />
          <SummaryTile label={t("integration.summaryDropped")} value={summary.dropped} warn={summary.dropped > 0} />
        </div>
      ) : null}

      {summary && summary.pending > 0 ? (
        <Banner text={t("integration.workerWarning")} />
      ) : null}
      {summary && summary.dropped > 0 ? (
        <Banner text={t("integration.droppedWarning")} />
      ) : null}

      <Card>
        <div className="flex flex-col gap-3 border-b p-4 sm:flex-row sm:items-center">
          <div className="flex items-center gap-2">
            <span className="text-xs text-muted-foreground">{t("common.status")}</span>
            <Select
              value={status || "all"}
              onValueChange={(value) => {
                setPage(1);
                setStatus(value === "all" ? "" : (value as WebhookDeliveryStatus));
              }}
            >
              <SelectTrigger className="w-[9.5rem]">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">{t("common.all")}</SelectItem>
                {WEBHOOK_STATUSES.map((option) => (
                  <SelectItem key={option} value={option}>
                    {t(`integration.status.${option}`)}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>

          <div className="flex items-center gap-2">
            <span className="text-xs text-muted-foreground">{t("integration.filterEvent")}</span>
            <Select
              value={event || "all"}
              onValueChange={(value) => {
                setPage(1);
                setEvent(value === "all" ? "" : value);
              }}
            >
              <SelectTrigger className="w-[13rem]">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">{t("common.all")}</SelectItem>
                {WEBHOOK_EVENTS.map((option) => (
                  <SelectItem key={option} value={option}>
                    {option}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
        </div>

        {isLoading ? (
          <TableSkeleton rows={8} cols={6} />
        ) : isError ? (
          <ErrorState message={friendlyErrorMessage(error, t)} onRetry={() => refetch()} />
        ) : deliveries.length === 0 ? (
          <EmptyState
            title={t("integration.empty")}
            description={t("integration.emptyHint")}
            icon={Webhook}
          />
        ) : (
          <>
            <div className="overflow-x-auto">
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>{t("integration.event")}</TableHead>
                    <TableHead>{t("orders.store")}</TableHead>
                    <TableHead>{t("orders.orderNumber")}</TableHead>
                    <TableHead>{t("common.status")}</TableHead>
                    <TableHead>{t("integration.attempts")}</TableHead>
                    <TableHead>{t("integration.responseStatus")}</TableHead>
                    <TableHead>{t("common.createdAt")}</TableHead>
                    <TableHead aria-label={t("common.actions")} />
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {deliveries.map((delivery) => (
                    <DeliveryRow
                      key={delivery.uuid}
                      delivery={delivery}
                      locale={locale}
                      expanded={expanded === delivery.uuid}
                      onToggle={() =>
                        setExpanded(expanded === delivery.uuid ? null : delivery.uuid)
                      }
                      canReplay={can("integration.replay")}
                      onReplay={() => replay.mutate(delivery.uuid)}
                      replaying={replay.isPending && replay.variables === delivery.uuid}
                      t={t}
                    />
                  ))}
                </TableBody>
              </Table>
            </div>
            <Pagination
              page={data!.Page}
              totalPages={data!.Total}
              records={data!.Records}
              onPageChange={(next) => setPage(next)}
            />
          </>
        )}
      </Card>
    </div>
  );
}

function SummaryTile({ label, value, warn }: { label: string; value: number; warn?: boolean }) {
  return (
    <Card className="p-4">
      <p className="text-xs text-muted-foreground">{label}</p>
      <p
        className={
          warn
            ? "mt-1 text-2xl font-semibold text-destructive"
            : "mt-1 text-2xl font-semibold text-foreground"
        }
      >
        {value}
      </p>
    </Card>
  );
}

function Banner({ text }: { text: string }) {
  return (
    <div className="mb-4 flex items-start gap-2 rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm text-destructive">
      <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" aria-hidden />
      <span>{text}</span>
    </div>
  );
}

function DeliveryRow({
  delivery,
  locale,
  expanded,
  onToggle,
  canReplay,
  onReplay,
  replaying,
  t,
}: {
  delivery: WebhookDelivery;
  locale: Locale;
  expanded: boolean;
  onToggle: () => void;
  canReplay: boolean;
  onReplay: () => void;
  replaying: boolean;
  t: (key: never) => string;
}) {
  const translate = t as unknown as (key: string) => string;

  return (
    <>
      <TableRow>
        <TableCell>
          <button
            type="button"
            onClick={onToggle}
            className="text-start text-sm font-medium text-primary hover:underline"
          >
            {delivery.event_label || delivery.event}
          </button>
          <p className="text-[0.7rem] text-muted-foreground">{delivery.event}</p>
        </TableCell>
        <TableCell className="text-sm">{delivery.store?.name ?? "—"}</TableCell>
        <TableCell className="text-sm">
          {delivery.order?.order_number ?? "—"}
          {delivery.order?.external_order_id ? (
            <p className="text-[0.7rem] text-muted-foreground">
              {delivery.order.external_order_id}
            </p>
          ) : null}
        </TableCell>
        <TableCell>
          <WebhookStatusBadge status={delivery.status} label={delivery.status_label} />
        </TableCell>
        <TableCell className="numeric text-sm">{delivery.attempts}</TableCell>
        <TableCell className="numeric text-sm">{delivery.response_status ?? "—"}</TableCell>
        <TableCell className="text-xs text-muted-foreground">
          {formatDateTime(delivery.created_at, locale)}
        </TableCell>
        <TableCell>
          {canReplay ? (
            <Button
              size="sm"
              variant="ghost"
              onClick={onReplay}
              disabled={replaying}
              title={translate("integration.replayHint")}
            >
              <RefreshCw className="me-1 h-3 w-3" aria-hidden />
              {translate("integration.replay")}
            </Button>
          ) : null}
        </TableCell>
      </TableRow>

      {expanded ? (
        <TableRow>
          <TableCell colSpan={8} className="bg-muted/30">
            <WebhookDetail delivery={delivery} locale={locale} t={translate} />
          </TableCell>
        </TableRow>
      ) : null}
    </>
  );
}

/**
 * Everything about one delivery, re-read when the row is opened.
 *
 * The row already carries all of it, so the panel draws immediately and the fetch only replaces a
 * snapshot: a list is whatever was true when it was fetched, and a delivery being retried in the
 * background changes its status, its attempt count and what came back. Opening a row is exactly the
 * moment somebody wants the current answer rather than the one from a minute ago.
 */
function WebhookDetail({
  delivery,
  locale,
  t,
}: {
  delivery: WebhookDelivery;
  locale: Locale;
  t: (key: string) => string;
}) {
  const query = useQuery({
    queryKey: queryKeys.integration.webhook(delivery.uuid),
    queryFn: async () => (await getWebhookDelivery(delivery.uuid)).Model ?? null,
  });

  // The list row already carries the payload and the response, so the panel is useful while the
  // detail is still in flight rather than being a skeleton over information we already have.
  const full = query.data ?? delivery;

  return (
    <div className="space-y-3">
      {full.error ? (
        <div>
          <p className="text-xs font-medium text-destructive">{t("integration.errorDetail")}</p>
          <p className="text-xs text-muted-foreground">{full.error}</p>
        </div>
      ) : null}

      <div className="grid gap-3 text-xs sm:grid-cols-2 lg:grid-cols-4">
        <Detail label={t("stores.title")}>
          {query.isPending ? (
            <Skeleton className="h-4 w-24" />
          ) : full.store ? (
            <Link href={`/stores/${full.store.uuid}`} className="text-primary hover:underline">
              {full.store.name}
            </Link>
          ) : (
            "—"
          )}
        </Detail>

        <Detail label={t("nav.orders")}>
          {query.isPending ? (
            <Skeleton className="h-4 w-24" />
          ) : full.order ? (
            <Link href={`/orders/${full.order.uuid}`} className="text-primary hover:underline">
              {full.order.order_number}
              {full.order.external_order_id ? (
                <span className="ms-1 text-muted-foreground">
                  ({full.order.external_order_id})
                </span>
              ) : null}
            </Link>
          ) : (
            "—"
          )}
        </Detail>

        <Detail label={t("integration.url")}>
          {/* Their endpoint, not ours: the commonest cause of a failing delivery is that this is
              not the address they expected. */}
          <span className="break-all font-mono text-[0.7rem]" dir="ltr">
            {full.url}
          </span>
        </Detail>

        <Detail label={t("integration.nextAttempt")}>
          {full.next_attempt_at ? formatDateTime(full.next_attempt_at, locale) : "—"}
        </Detail>
      </div>

      <div className="grid gap-3 lg:grid-cols-2">
        <div>
          <p className="mb-1 text-xs font-medium text-foreground">{t("integration.payload")}</p>
          {/* The raw bytes. This is what settles "we sent it" with an external partner. */}
          <pre className="max-h-72 overflow-auto rounded-md bg-background p-3 text-[0.7rem] leading-relaxed">
            {JSON.stringify(full.payload, null, 2)}
          </pre>
        </div>
        <div>
          <p className="mb-1 text-xs font-medium text-foreground">
            {t("integration.responseBody")}
          </p>
          <pre className="max-h-72 overflow-auto rounded-md bg-background p-3 text-[0.7rem] leading-relaxed">
            {full.response_body ?? t("integration.noResponseBody")}
          </pre>
        </div>
      </div>
    </div>
  );
}

function Detail({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div className="min-w-0">
      <p className="text-[0.7rem] uppercase tracking-wide text-muted-foreground">{label}</p>
      <p className="mt-0.5 truncate text-foreground">{children}</p>
    </div>
  );
}
