"use client";

import { useMemo } from "react";
import Link from "next/link";
import { useQuery } from "@tanstack/react-query";
import { ArrowDownLeft, ArrowUpRight, Receipt, TriangleAlert } from "lucide-react";
import { Pagination } from "@/components/shared/pagination";
import { EmptyState, ErrorState, TableSkeleton } from "@/components/shared/states";
import { LedgerEntryTypeBadge } from "@/components/shared/ledger-badges";
import { Badge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { useI18n, useT } from "@/i18n";
import { friendlyErrorMessage } from "@/lib/errors";
import { formatCurrency, formatDateTime } from "@/lib/format";
import { queryKeys } from "@/lib/query-keys";
import { useUrlState } from "@/lib/use-url-state";
import { getLedgerFeed, keyedMap, LEDGER_ENTRY_TYPES } from "@/services/ledger";
import type { LedgerEntry, LedgerEntryType, LedgerFeedTotals } from "@/types/ledger";

/**
 * Every movement of money, newest first — the audit trail behind the balances.
 *
 * **A balance is not a record of a day.** A captain who took 5,000 from customers and handed 5,000
 * back before closing has a balance of zero, and the balances list has nothing to say about the
 * 10,000 that passed through their hands. This is the screen that does.
 *
 * The totals follow the filters and report money in and out separately for the same reason: the day
 * above nets to nothing, and a cashier reconciling it needs both figures rather than their
 * difference.
 */
export function LedgerFeed() {
  const t = useT();
  const { locale } = useI18n();

  const [page, setPage] = useUrlState("fpage", 1, (value) => {
    const parsed = Number(value);
    return Number.isFinite(parsed) && parsed > 0 ? parsed : 1;
  });
  const [type, setType] = useUrlState<LedgerEntryType | "">("type", "", (value) =>
    LEDGER_ENTRY_TYPES.includes(value as LedgerEntryType) ? (value as LedgerEntryType) : "",
  );
  const [from, setFrom] = useUrlState("from", "", (value) => value);
  const [to, setTo] = useUrlState("to", "", (value) => value);

  // A window whose end precedes its start is refused by the server; there is no point spending the
  // round trip to be told, and the message belongs beside the fields.
  const backwards = from !== "" && to !== "" && from > to;

  const filters = useMemo(
    () => ({
      page,
      rows: 25,
      type: type || undefined,
      from: from || undefined,
      to: to || undefined,
    }),
    [page, type, from, to],
  );

  const { data, isLoading, isError, error, refetch } = useQuery({
    queryKey: queryKeys.ledger.feed(filters),
    queryFn: () => getLedgerFeed(filters),
    enabled: !backwards,
  });

  const entries = data?.Model ?? [];
  const totals = keyedMap(data?.Totals);
  const currencies = Object.keys(totals).sort();
  const filtered = type !== "" || from !== "" || to !== "";

  return (
    <div className="space-y-4">
      {currencies.length > 0 ? (
        <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
          {currencies.map((code) => (
            <MovedCard key={code} currency={code} totals={totals[code]} locale={locale} />
          ))}
        </div>
      ) : null}

      <Card>
        <div className="flex flex-wrap items-end justify-between gap-3 border-b p-4">
          <p className="basis-full text-sm text-muted-foreground sm:basis-auto">
            {t("ledger.feed.hint")}
          </p>

          <div className="flex flex-wrap items-end gap-2">
            <div className="space-y-1.5">
              <Label htmlFor="feed-type">{t("ledger.feed.type")}</Label>
              <Select
                value={type || "all"}
                onValueChange={(value) => {
                  setType(value === "all" ? "" : (value as LedgerEntryType));
                  setPage(1);
                }}
              >
                <SelectTrigger id="feed-type" className="w-[13rem]">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="all">{t("ledger.feed.allTypes")}</SelectItem>
                  {LEDGER_ENTRY_TYPES.map((option) => (
                    <SelectItem key={option} value={option}>
                      {t(`ledger.type.${option}`)}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>

            <div className="space-y-1.5">
              <Label htmlFor="feed-from">{t("ledger.feed.from")}</Label>
              <Input
                id="feed-from"
                type="date"
                value={from}
                className="w-[10rem]"
                onChange={(event) => {
                  setFrom(event.target.value);
                  setPage(1);
                }}
              />
            </div>

            <div className="space-y-1.5">
              <Label htmlFor="feed-to">{t("ledger.feed.to")}</Label>
              <Input
                id="feed-to"
                type="date"
                value={to}
                className="w-[10rem]"
                onChange={(event) => {
                  setTo(event.target.value);
                  setPage(1);
                }}
              />
            </div>
          </div>

          {backwards ? (
            <p className="basis-full text-xs text-destructive">{t("ledger.feed.backwards")}</p>
          ) : null}
        </div>

        {isLoading ? (
          <TableSkeleton rows={10} cols={6} />
        ) : isError ? (
          <ErrorState message={friendlyErrorMessage(error, t)} onRetry={() => refetch()} />
        ) : entries.length === 0 ? (
          <EmptyState
            title={filtered ? t("ledger.feed.emptyFiltered") : t("ledger.feed.empty")}
            description={t("ledger.feed.emptyHint")}
            icon={Receipt}
          />
        ) : (
          <>
            <div className="overflow-x-auto">
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>{t("ledger.feed.when")}</TableHead>
                    <TableHead>{t("ledger.captain")}</TableHead>
                    <TableHead>{t("ledger.feed.type")}</TableHead>
                    <TableHead className="text-end">{t("ledger.amount")}</TableHead>
                    <TableHead>{t("orders.title")}</TableHead>
                    <TableHead>{t("ledger.detail")}</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {entries.map((entry) => (
                    <FeedRow 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>
    </div>
  );
}

/**
 * What one currency moved over the filtered window.
 *
 * In and out, never netted — see the file comment. `out` arrives negative and is shown as it is,
 * because a cashier reading a column of movements should not have to work out which way a positive
 * number pointed.
 */
function MovedCard({
  currency,
  totals,
  locale,
}: {
  currency: string;
  totals: LedgerFeedTotals;
  locale: "en" | "ar";
}) {
  const t = useT();

  return (
    <Card className="p-4">
      <div className="flex items-center justify-between gap-2">
        <p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
          {currency}
        </p>
        <p className="numeric text-xs text-muted-foreground">
          {t("ledger.feed.movementsCount", { count: totals.entries })}
        </p>
      </div>

      <div className="mt-3 grid grid-cols-2 gap-3">
        <div>
          <p className="flex items-center gap-1 text-[0.7rem] text-muted-foreground">
            <ArrowUpRight className="h-3 w-3 text-success" aria-hidden />
            {t("ledger.feed.in")}
          </p>
          <p className="numeric mt-0.5 text-lg font-semibold text-success">
            {formatCurrency(totals.in, currency, locale)}
          </p>
        </div>
        <div>
          <p className="flex items-center gap-1 text-[0.7rem] text-muted-foreground">
            <ArrowDownLeft className="h-3 w-3 text-warning" aria-hidden />
            {t("ledger.feed.out")}
          </p>
          <p className="numeric mt-0.5 text-lg font-semibold text-warning">
            {formatCurrency(totals.out, currency, locale)}
          </p>
        </div>
      </div>

      <p className="mt-2 text-xs text-muted-foreground">
        {t("ledger.feed.net", { amount: formatCurrency(totals.net, currency, locale) })}
      </p>
    </Card>
  );
}

function FeedRow({ entry, locale }: { entry: LedgerEntry; locale: "en" | "ar" }) {
  const t = useT();
  const captain = entry.captain ?? null;

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

      <TableCell className="text-sm">
        {captain ? (
          <Link
            href={`/payments/captains/${captain.uuid}`}
            className="font-medium text-primary hover:underline"
          >
            {captain.name}
          </Link>
        ) : (
          "—"
        )}
      </TableCell>

      <TableCell className="text-sm">
        <LedgerEntryTypeBadge type={entry.type} label={entry.type_label} />
        {entry.note ? (
          <p className="mt-1 text-[0.7rem] text-muted-foreground">{entry.note}</p>
        ) : null}
      </TableCell>

      <TableCell className="numeric text-end text-sm">
        {/* Signed from the captain's side: positive means the company owes them. Coloured rather
            than prefixed, because a minus sign in a right-to-left column is easy to miss. */}
        <span className={entry.amount < 0 ? "font-medium text-warning" : "font-medium text-success"}>
          {formatCurrency(entry.amount, entry.currency, locale)}
        </span>
        {entry.has_variance && entry.variance !== null ? (
          <Badge variant="warning" className="ms-2">
            <TriangleAlert className="h-3 w-3" aria-hidden />
            {formatCurrency(entry.variance, entry.currency, locale)}
          </Badge>
        ) : null}
      </TableCell>

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

      <TableCell className="text-xs text-muted-foreground">
        {/* The same sentence the statement uses, so one vocabulary covers both screens. */}
        {entry.recorded_by
          ? t(`ledger.recordedBy.${entry.recorded_by.kind}`, {
              name: entry.recorded_by.name ?? "—",
            })
          : "—"}
        {entry.reference ? (
          <p className="font-mono text-[0.7rem]" dir="ltr">
            {entry.reference}
          </p>
        ) : null}
      </TableCell>
    </TableRow>
  );
}
