import type { Dispatch, ReactNode, SetStateAction } from "react";
import { Badge, Checkbox, Group, Text, Tooltip } from "@mantine/core";
import type { TFunction } from "i18next";
import {
  STATUS_COLORS,
  STATUS_LABELS,
  applicantOrCompanyName,
  localized,
  type ApplicationKind,
  type LicenseApplication,
  type QueueFilter,
} from "@ema-platform/api";

const KIND_COLOR: Record<ApplicationKind, string> = {
  NEW: "blue",
  RENEWAL: "teal",
  REISSUE: "orange",
};
import type { AdvancedColumn } from "@ema-platform/ui";
import { dateDisplayer } from "@ema-platform/shared";
import { computeSla } from "../../sla";

/**
 * Label for the Company/Applicant column, derived from the rows actually on
 * screen rather than the route — a type-pinned queue (`/type/:typeCode`)
 * happens to be one family, but nothing stops the mixed "All Applications"
 * grid from holding both, and a static header can't be correct for both at
 * once. Falls back to the combined label until the page has data to look at.
 */
function companyColumnHeader(
  t: TFunction,
  items: LicenseApplication[],
  isLogistics: boolean | undefined,
): string {
  // A type-pinned non-logistics queue (Seafarer Registration, Seaman Book,
  // BTC, ...) is always "Applicant" — no need to guess from loaded rows.
  if (isLogistics === false) return t("queue.applicant", "Applicant");
  if (isLogistics === true) return t("queue.company", "Company");
  if (items.length === 0) {
    return t("queue.companyOrApplicant", "Applicant / Company");
  }
  const allLogistics = items.every((a) => a.familyKind === "LOGISTICS_LICENSE");
  const allNonLogistics = items.every((a) => a.familyKind !== "LOGISTICS_LICENSE");
  if (allLogistics) return t("queue.company", "Company");
  if (allNonLogistics) return t("queue.applicant", "Applicant");
  return t("queue.companyOrApplicant", "Applicant / Company");
}

export function licenseQueueColumns(
  t: TFunction,
  locale: string,
  opts: {
    items: LicenseApplication[];
    selected: string[];
    setSelected: Dispatch<SetStateAction<string[]>>;
    allSelected: boolean;
    sortableHeader: (
      label: string,
      field: NonNullable<QueueFilter["sortBy"]>,
    ) => ReactNode;
    /** Set for a type-pinned queue; undefined for the mixed All/Mine grids. */
    isLogistics?: boolean;
  },
): AdvancedColumn<LicenseApplication>[] {
  const { items, selected, setSelected, allSelected, sortableHeader, isLogistics } = opts;
  const columns: AdvancedColumn<LicenseApplication>[] = [
    {
      header: (
        <Checkbox
          aria-label={t("queue.selectAll", "Select all")}
          checked={allSelected}
          indeterminate={selected.length > 0 && !allSelected}
          onChange={() =>
            setSelected(allSelected ? [] : items.map((a) => a.id))
          }
        />
      ),
      size: 40,
      cell: ({ row }) => (
        <Checkbox
          aria-label={t("queue.selectRow", {
            number: row.original.applicationNumber,
            defaultValue: "Select {{number}}",
          })}
          checked={selected.includes(row.original.id)}
          onChange={(e) => {
            const checked = e.currentTarget.checked;
            setSelected((prev) =>
              checked
                ? [...prev, row.original.id]
                : prev.filter((id) => id !== row.original.id),
            );
          }}
        />
      ),
    },
    {
      header: sortableHeader(t("queue.number", "App #"), "applicationNumber"),
      label: t("queue.number", "App #"),
      cell: ({ row }) => (
        <Text size="sm" fw={500}>
          {row.original.applicationNumber}
        </Text>
      ),
    },
    {
      // Header reflects what's actually on screen, not the route: a
      // type-pinned queue (`typeCode` set) is always one family, but the
      // mixed "All Applications" grid can hold logistics rows and
      // certificate/document rows side by side, so no single static label is
      // right for the whole column there — "Applicant / Company" covers
      // both without claiming a row is one or the other.
      header: sortableHeader(companyColumnHeader(t, items, isLogistics), "companyName"),
      label: t("queue.company", "Company"),
      cell: ({ row }) => (
        <Text size="sm">{applicantOrCompanyName(row.original) ?? "—"}</Text>
      ),
    },
    {
      header: t("queue.typeCol", "Type"),
      cell: ({ row }) => (
        <Group gap={6} wrap="nowrap">
          <Text size="sm">
            {localized(row.original.licenseType?.name, locale) || "—"}
          </Text>
          {row.original.kind !== "NEW" && (
            <Badge size="xs" variant="light" color={KIND_COLOR[row.original.kind]}>
              {t(
                `queue.kindValues.${row.original.kind}`,
                row.original.kind === "RENEWAL" ? "Renewal" : "Replacement",
              )}
            </Badge>
          )}
        </Group>
      ),
    },
    {
      header: sortableHeader(t("queue.statusCol", "Status"), "status"),
      label: t("queue.statusCol", "Status"),
      cell: ({ row }) => {
        const label = t(
          `queue.statusValues.${row.original.status}`,
          STATUS_LABELS[row.original.status],
        );
        return (
          <Tooltip label={label} withArrow>
            <Badge color={STATUS_COLORS[row.original.status]} variant="light">
              {label}
            </Badge>
          </Tooltip>
        );
      },
    },
    {
      header: sortableHeader(
        t("queue.submitted", "Submitted"),
        "submittedAt",
      ),
      label: t("queue.submitted", "Submitted"),
      cell: ({ row }) => (
        <Text size="sm" c="dimmed">
          {dateDisplayer(row.original.submittedAt, locale)}
        </Text>
      ),
    },
    {
      header: t("queue.sla", "Age / SLA"),
      cell: ({ row }) => {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any -- i18next's overloaded TFunction type doesn't structurally match a plain callback signature
        const sla = computeSla(row.original, undefined, locale, (key, options) => t(key, options as any) as string);
        return (
          // Colour is never the only signal — the label says the same thing.
          <Tooltip label={sla.tooltip} withArrow>
            <Badge color={sla.color} variant="light" size="sm">
              {sla.label}
            </Badge>
          </Tooltip>
        );
      },
    },
  ];

  // A type-pinned non-logistics queue never has a TIN to show — a business
  // registration number doesn't apply to a certificate/document filed by a
  // person — so the column itself is dropped rather than left showing blanks.
  if (isLogistics !== false) {
    columns.splice(2, 0, {
      header: t("queue.tin", "TIN"),
      cell: ({ row }) =>
        row.original.familyKind === "LOGISTICS_LICENSE" ? (
          <Text size="sm" c="dimmed">
            {row.original.tinNumber ?? "—"}
          </Text>
        ) : null,
    });
  }

  return columns;
}
