"use client";

import { use, useMemo, useState } from "react";
import Link from "next/link";
import { useQuery } from "@tanstack/react-query";
import { AlertTriangle, ArrowLeft, ReceiptText, SlidersHorizontal } from "lucide-react";
import { PageHeader } from "@/components/shared/page-header";
import { Pagination } from "@/components/shared/pagination";
import { EmptyState, ErrorState, TableSkeleton } from "@/components/shared/states";
import { BalanceStateBadge, LedgerEntryTypeBadge } from "@/components/shared/ledger-badges";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { AdjustDialog } from "@/features/ledger/adjust-dialog";
import { SettleDialog } from "@/features/ledger/settle-dialog";
import { useCan } from "@/features/auth/use-can";
import { useI18n, useT } from "@/i18n";
import { formatCurrency, formatDateTime, formatPhone } from "@/lib/format";
import { friendlyErrorMessage } from "@/lib/errors";
import { queryKeys } from "@/lib/query-keys";
import { useUrlState } from "@/lib/use-url-state";
import { getCaptainStatement, keyedMap, toCents } from "@/services/ledger";
import type { CaptainBalanceState, LedgerEntry, SettleDirection } from "@/types/ledger";

/** Fallback currency for a captain with no entries yet — the server's own default. */
const DEFAULT_CURRENCY = "SAR";

/**
 * Derive the state the server would report from a signed balance.
 *
 * The statement endpoint returns balances as bare numbers, without the state and settling
 * movement the desk overview carries, so the same rule is applied here. It has to match the
 * server's exactly — a card that offered "pay out" where the server expected "hand in" would
 * build a request guaranteed to be refused.
 *
 * So it is a literal copy of `CaptainBalanceState::fromBalance()`, thresholds included, rather
 * than a rounding of its own. Rounding to cents looks equivalent but is not: it calls 0.005 owed
 * where the server calls it clear. Balances arrive rounded to two places so that edge cannot
 * occur today — but "matches the server" should be true, not true-for-now.
 */
function stateOf(balance: number): CaptainBalanceState {
  if (balance >= 0.01) return "owed";
  if (balance <= -0.01) return "owes";
  return "clear";
}

function directionFor(state: CaptainBalanceState): SettleDirection | null {
  if (state === "owed") return "cash_paid_out";
  if (state === "owes") return "cash_handed_in";
  return null;
}

/**
 * One captain's account: their balance in every currency, and every line behind it.
 *
 * The statement is newest-first with a running balance on each line, so a disputed figure can be
 * traced back to the exact delivery or desk movement that produced it — which is the question a
 * captain asks when told they owe more than they think.
 */
export default function CaptainStatementPage({ params }: { params: Promise<{ ulid: string }> }) {
  const { ulid } = use(params);
  const t = useT();
  const { locale } = useI18n();
  const { can } = useCan();
  const canSettle = can("captain_ledger.settle");

  const [page, setPage] = useUrlState("page", 1, (value) => {
    const parsed = Number(value);
    return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
  });
  const [currency, setCurrency] = useUrlState("currency", "", (value) => value.toUpperCase());

  const filters = useMemo(
    () => ({ page, rows: 15, currency: currency || undefined }),
    [page, currency],
  );

  const { data, isLoading, isError, error, refetch } = useQuery({
    queryKey: queryKeys.ledger.statement(ulid, filters),
    queryFn: () => getCaptainStatement(ulid, filters),
  });

  const captain = data?.Captain ?? null;
  const balances = keyedMap(data?.Balances);
  const currencies = Object.keys(balances).sort();
  const entries = data?.Model ?? [];

  const [settleCurrency, setSettleCurrency] = useState<string | null>(null);
  const [adjustOpen, setAdjustOpen] = useState(false);

  const settleBalance = settleCurrency !== null ? balances[settleCurrency] : undefined;
  const settleState = settleBalance !== undefined ? stateOf(settleBalance) : null;
  const settleDirection = settleState ? directionFor(settleState) : null;

  return (
    <div>
      <Link
        href="/payments"
        className="mb-3 inline-flex items-center gap-1 text-xs font-medium text-muted-foreground hover:text-foreground"
      >
        <ArrowLeft className="h-3 w-3" aria-hidden />
        {t("payments.title")}
      </Link>

      <PageHeader
        title={captain?.name ?? t("ledger.statement")}
        subtitle={captain?.phone ? formatPhone(captain.phone) : undefined}
        actions={
          canSettle && captain ? (
            <Button variant="outline" size="sm" onClick={() => setAdjustOpen(true)}>
              <SlidersHorizontal className="h-4 w-4" aria-hidden />
              {t("ledger.adjust.open")}
            </Button>
          ) : null
        }
      />

      {isError ? (
        <ErrorState message={friendlyErrorMessage(error, t)} onRetry={() => refetch()} />
      ) : (
        <>
          {/* Balances — one card per currency the captain has ever moved money in. */}
          <div className="mb-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
            {isLoading ? (
              <Skeleton className="h-28 w-full rounded-lg" />
            ) : currencies.length === 0 ? (
              <Card className="p-4 sm:col-span-2 lg:col-span-3">
                <p className="text-sm text-muted-foreground">{t("ledger.noBalance")}</p>
              </Card>
            ) : (
              currencies.map((code) => {
                const balance = balances[code];
                const state = stateOf(balance);
                const outstanding = Math.abs(toCents(balance)) / 100;

                return (
                  <Card key={code} className="p-4">
                    <div className="flex items-start justify-between gap-2">
                      <p className="text-xs font-medium text-muted-foreground">{code}</p>
                      <BalanceStateBadge state={state} label={t(`ledger.state.${state}`)} />
                    </div>
                    <p
                      className={
                        state === "owes"
                          ? "mt-2 text-2xl font-semibold text-warning"
                          : state === "owed"
                            ? "mt-2 text-2xl font-semibold text-primary"
                            : "mt-2 text-2xl font-semibold text-muted-foreground"
                      }
                    >
                      {formatCurrency(outstanding, code, locale)}
                    </p>
                    <p className="mt-0.5 text-xs text-muted-foreground">
                      {t(`ledger.stateHint.${state}`)}
                    </p>
                    {canSettle && state !== "clear" ? (
                      <Button
                        className="mt-3 w-full"
                        size="sm"
                        onClick={() => setSettleCurrency(code)}
                      >
                        {state === "owed"
                          ? t("ledger.settle.payoutConfirm")
                          : t("ledger.settle.handInConfirm")}
                      </Button>
                    ) : null}
                  </Card>
                );
              })
            )}
          </div>

          <Card>
            <div className="flex flex-wrap items-center justify-between gap-3 border-b p-4">
              <h2 className="text-sm font-semibold">{t("ledger.statement")}</h2>
              {currencies.length > 1 ? (
                <Select
                  value={currency || "all"}
                  onValueChange={(value) => {
                    setCurrency(value === "all" ? "" : value);
                    setPage(1);
                  }}
                >
                  <SelectTrigger className="w-[8rem]">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="all">{t("ledger.filter.allCurrencies")}</SelectItem>
                    {currencies.map((code) => (
                      <SelectItem key={code} value={code}>
                        {code}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              ) : null}
            </div>

            {isLoading ? (
              <TableSkeleton rows={8} cols={6} />
            ) : entries.length === 0 ? (
              <EmptyState
                title={t("ledger.noEntries")}
                description={t("ledger.noEntriesHint")}
                icon={ReceiptText}
              />
            ) : (
              <>
                <div className="overflow-x-auto">
                  <Table>
                    <TableHeader>
                      <TableRow>
                        <TableHead>{t("ledger.when")}</TableHead>
                        <TableHead>{t("ledger.type")}</TableHead>
                        <TableHead className="text-end">{t("ledger.amount")}</TableHead>
                        <TableHead className="text-end">{t("ledger.balanceAfter")}</TableHead>
                        <TableHead>{t("ledger.order")}</TableHead>
                        <TableHead>{t("ledger.detail")}</TableHead>
                      </TableRow>
                    </TableHeader>
                    <TableBody>
                      {entries.map((entry) => (
                        <EntryRow key={entry.uuid} entry={entry} locale={locale} />
                      ))}
                    </TableBody>
                  </Table>
                </div>
                <Pagination
                  page={data!.Page}
                  totalPages={data!.Total}
                  records={data!.Records}
                  onPageChange={(next) => setPage(next)}
                />
              </>
            )}
          </Card>
        </>
      )}

      {captain && settleCurrency && settleState && settleDirection && settleBalance !== undefined ? (
        <SettleDialog
          open
          onOpenChange={(open) => {
            if (!open) setSettleCurrency(null);
          }}
          captainUlid={ulid}
          captainName={captain.name}
          currency={settleCurrency}
          outstanding={Math.abs(toCents(settleBalance)) / 100}
          direction={settleDirection}
        />
      ) : null}

      {captain ? (
        <AdjustDialog
          open={adjustOpen}
          onOpenChange={setAdjustOpen}
          captainUlid={ulid}
          captainName={captain.name}
          defaultCurrency={currencies[0] ?? DEFAULT_CURRENCY}
        />
      ) : null}
    </div>
  );
}

function EntryRow({ entry, locale }: { entry: LedgerEntry; locale: "en" | "ar" }) {
  const t = useT();
  const positive = toCents(entry.amount) > 0;

  return (
    <TableRow>
      <TableCell className="whitespace-nowrap text-xs text-muted-foreground">
        {entry.created_at ? formatDateTime(entry.created_at, locale) : "—"}
      </TableCell>

      <TableCell>
        <LedgerEntryTypeBadge type={entry.type} label={entry.type_label} />
      </TableCell>

      {/*
        Signed here, unlike the overview, because on a statement the sign *is* the information:
        each line either raised what we owe the captain (+) or what they owe us (−), and the
        running balance beside it only makes sense read against that.
      */}
      <TableCell
        className={
          positive
            ? "numeric text-end font-medium text-primary"
            : "numeric text-end font-medium text-warning"
        }
        dir="ltr"
      >
        {positive ? "+" : "−"}
        {formatCurrency(Math.abs(entry.amount), entry.currency, locale)}
      </TableCell>

      <TableCell className="numeric text-end text-muted-foreground" dir="ltr">
        {formatCurrency(entry.balance_after, entry.currency, locale)}
      </TableCell>

      <TableCell>
        {entry.order ? (
          <Link
            href={`/orders/${entry.order.uuid}`}
            className="text-xs font-medium text-primary hover:underline"
          >
            {entry.order.order_number}
          </Link>
        ) : (
          <span className="text-xs text-muted-foreground">—</span>
        )}
      </TableCell>

      <TableCell className="max-w-[18rem] text-xs">
        {/*
          The variance is the reason this screen exists. A delivery where the captain collected
          less than the order said is the conversation the cashier needs to have, so it is drawn
          as a warning rather than left for somebody to spot in two adjacent columns.
        */}
        {entry.has_variance && entry.expected_amount !== null && entry.variance !== null ? (
          <p className="mb-0.5 flex items-center gap-1 font-medium text-destructive">
            <AlertTriangle className="h-3 w-3 shrink-0" aria-hidden />
            {t("ledger.variance", {
              expected: formatCurrency(entry.expected_amount, entry.currency, locale),
              variance: formatCurrency(entry.variance, entry.currency, locale),
            })}
          </p>
        ) : null}
        {entry.note ? <p className="text-foreground">{entry.note}</p> : null}
        <p className="text-muted-foreground">
          {entry.recorded_by?.name
            ? t(`ledger.recordedBy.${entry.recorded_by.kind}`, { name: entry.recorded_by.name })
            : null}
          {entry.reference ? (
            <span className="ms-1" dir="ltr">
              · {entry.reference}
            </span>
          ) : null}
        </p>
      </TableCell>
    </TableRow>
  );
}
