import { directionLabel } from "@/lib/utils";
import { useMyTradeAccess } from "@/hooks/useMyTradeAccess";
import {
  ActionIcon,
  Box,
  Card,
  Stack,
  Text,
  ThemeIcon,
} from "@mantine/core";
import {
  AlertTriangle,
  ArrowRight,
  CalendarClock,
  CheckCircle2,
  Clock,
  FileText,
  Inbox,
  LayoutList,
  RefreshCw,
  Repeat,
  User,
} from "lucide-react";
import { useCallback, useMemo, type ReactNode } from "react";
import { useNavigate } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { api } from "@/services/api";

import { ContractStatusBadge } from "@/components/contracts/ContractStatusBadge";
import { bookingTable } from "@/components/bookings/booking-ui.styles";
import { KpiStrip, PageContainer, PageHeader } from "@/components/page";
import {
  CONTRACT_LIST_TABS,
  CONTRACT_STATUS_STYLES,
  contractCourt,
} from "@/features/contracts/contract-status.config";
import { formatContractApprovalProgress } from "@/features/contracts/contract-approval-progress";
import {
  getStaffRowAction,
  toContractListRow,
  type ContractListRow,
} from "@/features/contracts/mapContractListRow";
import { formatDate, humanize } from "@/lib/format";
import {
  useContractList,
  useContractListSummary,
} from "@/hooks/contracts/useContracts";
import type { ContractListFilter } from "@/services/contracts.service";
import {
  Badge,
  DataTable,
  DataTableFooter,
  type ColumnDef,
} from "@edr/ui-common";
import { FilterBar, dateRangeParams, routeParams, useFilters, type FilterDef } from "@/components/filters";
import { ExportButton } from "@/components/export/ExportButton";

/** Every filterable status — the pill tabs are gone, so the select carries them all. */
const STATUS_OPTIONS = CONTRACT_LIST_TABS.flatMap((t) => t.statuses ?? []).map(
  (s) => ({
    value: s,
    label: CONTRACT_STATUS_STYLES[s]?.label ?? s,
  }),
);

const TRADE_DIRECTION_OPTIONS = [
  { value: "IMPORT", label: "Import" },
  { value: "EXPORT", label: "Export" },
  { value: "DOMESTIC", label: "Domestic" },
];

const FREIGHT_TYPE_OPTIONS = [
  { value: "CONTAINER", label: "Container" },
  { value: "BULK", label: "Bulk" },
];

const CONTRACT_KIND_OPTIONS = [
  { value: "GENERAL", label: "General (recurring)" },
  { value: "ONE_TIME", label: "One-time" },
];

const CURRENCY_OPTIONS = [
  { value: "ETB", label: "ETB" },
  { value: "USD", label: "USD" },
  { value: "DJF", label: "DJF" },
];

/** value = `${sortBy}:${sortOrder}` for the sort Select. */
const SORT_OPTIONS = [
  { value: "createdAt:DESC", label: "Newest first" },
  { value: "createdAt:ASC", label: "Oldest first" },
  { value: "contractValidUntil:ASC", label: "Expiring soonest" },
  { value: "contractValidUntil:DESC", label: "Expiring latest" },
];

/**
 * Per-column widths — they must sum to the table's min-w (960px, set on the
 * containerClassName below) because table-fixed distributes any difference.
 */
const COLUMN_WIDTHS = { contract: 250, route: 270, status: 250, validity: 190 };
const COLUMN_META = {
  headerClassName: "whitespace-normal break-words",
  cellClassName: "whitespace-normal break-words align-top",
};

export default function ContractRequestsPage() {
  const navigate = useNavigate();
  const { filterOptions } = useMyTradeAccess();

  // Yard options for the route filter (shared routes reference list, same
  // query BookingRequestsPage uses).
  const { data: yardRefs } = useQuery(
    api.routes.yards.queryOptions({ staleTime: 5 * 60_000 }),
  );
  const yardOptions = useMemo(
    () => (yardRefs ?? []).map((y) => ({ value: y.id, label: y.label ?? y.code })),
    [yardRefs],
  );

  // Service-type options — same reference-data payload the booking form uses.
  const { data: refData } = useQuery(
    api.bookings.referenceData.queryOptions({ staleTime: 5 * 60_000 }),
  );
  const serviceTypeOptions = useMemo(
    () => (refData?.service ?? []).map((s) => ({ value: s.id, label: s.name })),
    [refData],
  );

  // Static shape only (no facet counts) — this is what useFilters needs to
  // parse the URL and build API params. Counts are attached separately below,
  // for rendering only, once the summary query (which itself depends on
  // these params) has resolved.
  const filterDefs: FilterDef[] = useMemo(
    () => [
      { key: "statuses", label: "Status", type: "enum", options: STATUS_OPTIONS },
      {
        key: "contractKind",
        label: "Kind",
        type: "enum",
        multiple: false,
        options: CONTRACT_KIND_OPTIONS,
      },
      {
        key: "tradeDirection",
        label: "Direction",
        type: "enum",
        multiple: false,
        options: filterOptions(TRADE_DIRECTION_OPTIONS),
      },
      {
        key: "freightType",
        label: "Freight",
        type: "enum",
        multiple: false,
        options: FREIGHT_TYPE_OPTIONS,
      },
      {
        key: "serviceTypeId",
        label: "Service",
        type: "enum",
        multiple: false,
        options: serviceTypeOptions,
      },
      {
        key: "paymentCurrency",
        label: "Currency",
        type: "enum",
        multiple: false,
        options: CURRENCY_OPTIONS,
        secondary: true,
      },
      {
        key: "created",
        label: "Created",
        type: "date",
        secondary: true,
        // Before/after are safe to expose: the repository applies
        // createdFrom/createdTo independently, so a single-sided bound
        // already works server-side.
        operators: ["between", "before", "after"],
        toParams: dateRangeParams("createdFrom", "createdTo"),
      },
      {
        key: "route",
        label: "Route",
        type: "route",
        options: yardOptions,
        toParams: routeParams("originYardId", "destinationYardId"),
      },
    ],
    [filterOptions, yardOptions, serviceTypeOptions],
  );

  const controls = useFilters(filterDefs, {
    defaultSort: "createdAt:DESC",
    pageSize: 10,
  });

  const filter: ContractListFilter = useMemo(
    () => ({
      ...(controls.params as unknown as ContractListFilter),
      // Kept as the React Query cache-key discriminator (tabs themselves are gone).
      tab: "all",
    }),
    [controls.params],
  );

  const { data, isLoading, isError, refetch, isFetching } =
    useContractList(filter);
  const {
    data: summary,
    isLoading: summaryLoading,
    refetch: refetchSummary,
  } = useContractListSummary(filter);

  const statusCounts = useMemo(
    () => Object.fromEntries((summary?.facets?.status ?? []).map((b) => [b.value, b.count])),
    [summary?.facets],
  );

  // filterDefs + counts, for the bar to render. Kept separate from filterDefs
  // itself so the URL-parsing hook above never has to wait on this query.
  const defs: FilterDef[] = useMemo(
    () =>
      filterDefs.map((d) =>
        d.key === "statuses" && d.type === "enum" ? { ...d, counts: statusCounts } : d,
      ),
    [filterDefs, statusCounts],
  );

  const rows = useMemo(
    () => (data?.items ?? []).map(toContractListRow),
    [data?.items],
  );

  const total = data?.total ?? 0;
  const showEmpty = !isLoading && !isError && rows.length === 0;

  const metrics = summary?.metrics;
  const tabCounts = summary?.tabs;

  const handleRefresh = useCallback(() => {
    void refetch();
    void refetchSummary();
  }, [refetch, refetchSummary]);

  const handleRowClick = useCallback(
    (row: ContractListRow) => {
      navigate(`/dashboard/contract-requests/${row.id}`);
    },
    [navigate],
  );

  const columns: ColumnDef<ContractListRow>[] = [
    {
      id: "contract",
      size: COLUMN_WIDTHS.contract,
      meta: COLUMN_META,
      header: () => <span className={bookingTable.headerCell}>Customer</span>,
      cell: ({ row }) => {
        const c = row.original;
        return (
          <div className="flex items-center gap-3 py-1.5">
            <div className={bookingTable.rowIcon}>
              <User className="size-4" strokeWidth={1.75} />
            </div>
            <div className="min-w-0">
              <p className="font-medium text-foreground">{c.customerLabel}</p>
              <p className="mt-0.5 flex items-center gap-1 text-xs text-muted-foreground">
                <FileText className="size-3 shrink-0 opacity-70" />
                {c.reference}
              </p>
            </div>
          </div>
        );
      },
    },
    {
      id: "route",
      size: COLUMN_WIDTHS.route,
      meta: COLUMN_META,
      header: () => <span className={bookingTable.headerCell}>Route</span>,
      cell: ({ row }) => {
        const c = row.original;
        return (
          <div className="space-y-1 py-1">
            <div className="flex items-center gap-1.5 text-sm font-medium text-foreground">
              <span>{c.originLabel}</span>
              <ArrowRight className="size-3.5 shrink-0 text-muted-foreground" />
              <span>{c.destinationLabel}</span>
            </div>
            <div className="flex gap-1.5">
              <Badge
                variant="outline"
                className="h-5 border-border/50 bg-background/50 px-1.5 text-[10px] font-medium backdrop-blur-sm"
              >
                {directionLabel(c.tradeDirection)}
              </Badge>
              <Badge
                variant="secondary"
                className="h-5 bg-muted/40 px-1.5 text-[10px] font-medium"
              >
                {humanize(c.freightType)}
              </Badge>
            </div>
          </div>
        );
      },
    },
    {
      id: "status",
      size: COLUMN_WIDTHS.status,
      meta: COLUMN_META,
      header: () => <span className={bookingTable.headerCell}>Status</span>,
      cell: ({ row }) => {
        const c = row.original;
        const court = contractCourt(c.status);
        const progress = formatContractApprovalProgress(
          c.status,
          c.approvalSteps,
        );
        const action = getStaffRowAction(c);
        // One dimmed line: who it's waiting on, the staff verb, and the
        // approval chain when one exists. "Open" adds nothing — row click
        // already opens the detail page.
        const pieces: ReactNode[] = [];
        if (court) {
          pieces.push(court === "customer" ? "With customer" : "With EDR");
        }
        if (action && action.variant === "filled") {
          pieces.push(
            // "View & sign" pointed at the contract-view page, not the
            // detail page — keep that deep link as an inline link.
            action.to(c.id).endsWith("/view") ? (
              <button
                type="button"
                className="underline underline-offset-2 hover:text-primary"
                onClick={(e) => {
                  e.stopPropagation();
                  navigate(action.to(c.id));
                }}
              >
                {action.label}
              </button>
            ) : (
              action.label
            ),
          );
        }
        if ((c.approvalSteps ?? []).length > 0) {
          pieces.push(progress.label);
          if (!progress.complete && progress.detail.startsWith("Next:")) {
            pieces.push(progress.detail);
          }
        }
        return (
          <div className="space-y-1 py-1">
            <ContractStatusBadge status={c.status} isRenewal={c.isRenewal} />
            {pieces.length ? (
              <div className="text-xs text-muted-foreground">
                {pieces.map((piece, i) => (
                  <span key={i}>
                    {i > 0 ? " · " : null}
                    {piece}
                  </span>
                ))}
              </div>
            ) : null}
          </div>
        );
      },
    },
    {
      id: "validity",
      size: COLUMN_WIDTHS.validity,
      meta: COLUMN_META,
      header: () => <span className={bookingTable.headerCell}>Validity</span>,
      cell: ({ row }) => {
        const c = row.original;
        const isGeneral = c.contractKind === "GENERAL";
        return (
          <Stack gap={2} align="flex-start">
            <span className="inline-flex items-center gap-1.5 text-sm text-muted-foreground">
              <CalendarClock className="size-3.5" />
              {c.validUntil
                ? `Until ${formatDate(c.validUntil)}`
                : c.validityDays
                  ? `${c.validityDays} days`
                  : "—"}
            </span>
            {c.validFrom ? (
              <Text size="xs" c="dimmed">
                From {formatDate(c.validFrom)}
              </Text>
            ) : null}
            <Badge
              variant="outline"
              className="h-5 border-border/50 bg-background/50 px-1.5 text-[10px] font-medium uppercase"
            >
              {isGeneral ? (
                <span className="inline-flex items-center gap-1">
                  <Repeat className="size-3" /> General
                </span>
              ) : (
                "One-time"
              )}
            </Badge>
          </Stack>
        );
      },
    },
  ];

  return (
    <PageContainer>
      <Stack gap="lg">
        <PageHeader
          title="Contract requests"
          subtitle="Review, approve, and execute freight contract requests."
          action={
            <ActionIcon
              variant="default"
              size="lg"
              radius="md"
              loading={isFetching}
              onClick={handleRefresh}
              aria-label="Refresh"
            >
              <RefreshCw size={16} />
            </ActionIcon>
          }
        />

        <KpiStrip
          loading={summaryLoading}
          items={[
            {
              label: "In queue",
              value: metrics?.inQueue ?? 0,
              icon: LayoutList,
              color: "edr-green",
            },
            {
              label: "Needs action",
              value: metrics?.needsAction ?? 0,
              icon: Clock,
              color: "yellow",
            },
            {
              label: "Urgent",
              value: metrics?.urgent ?? 0,
              icon: AlertTriangle,
              color: "red",
            },
            {
              label: "Closed",
              value: tabCounts?.closed ?? 0,
              icon: CheckCircle2,
              color: "edr-green",
            },
          ]}
        />

        <Card p={0}>
          <Stack gap={0}>
            <Box px="md" pt="sm" pb="xs" w="100%">
              <FilterBar
                defs={defs}
                controls={controls}
                searchPlaceholder="Search reference or customer…"
                sortOptions={SORT_OPTIONS}
                viewId="contract-requests"
              >
                <ExportButton datasetKey="contracts" params={controls.params} />
              </FilterBar>
            </Box>

            {showEmpty ? (
              <Stack align="center" gap={8} py={48}>
                <ThemeIcon variant="light" color="gray" radius="xl" size={48}>
                  <Inbox size={22} />
                </ThemeIcon>
                <Text c="dimmed">No contracts match this view.</Text>
              </Stack>
            ) : (
              <Box style={{ overflowX: "auto" }} w="100%">
                <DataTable
                  columns={columns}
                  data={rows}
                  status={
                    isLoading ? "loading" : isError ? "error" : "success"
                  }
                  onRowClick={handleRowClick}
                  {...controls.tableProps(total)}
                  // table-fixed makes the per-column widths stick; without
                  // it auto-layout re-widens columns once cells wrap.
                  containerClassName="border-0 shadow-none bg-transparent [&_table]:table-fixed [&_table]:min-w-[960px]"
                  footer={DataTableFooter}
                />
              </Box>
            )}
          </Stack>
        </Card>
      </Stack>
    </PageContainer>
  );
}
