import { useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
  Alert,
  Anchor,
  Badge,
  Box,
  Button,
  Card,
  Center,
  Group,
  SimpleGrid,
  Stack,
  Text,
  ThemeIcon,
  Tooltip,
} from '@mantine/core';
import {
  IconAnchor,
  IconArrowRight,
  IconBuildingWarehouse,
  IconChevronRight,
  IconFileText,
  IconLock,
  IconShieldOff,
  IconShip,
  IconTrendingUp,
} from '@tabler/icons-react';
import {
  useLocalized,
  useGetLicenseCategoriesQuery,
  useGetLicenseTypesQuery,
  useGetMyOperatorTypesQuery,
} from '@ema-platform/api';
import type { LicenseCategory, LicenseType } from '@ema-platform/api';
import { LICENSE_PERMISSIONS, RequirePermission } from '@ema-platform/auth';
import { useTranslation } from 'react-i18next';

/**
 * The licence catalogue an applicant chooses from, grouped by category.
 *
 * Shared by the dashboard and the applications page so the two never drift —
 * they previously listed the same licence types in two different shapes.
 */

const CATEGORY_ICONS: Record<LicenseCategory, typeof IconShip> = {
  CARGO_FREIGHT: IconBuildingWarehouse,
  SHIPPING_AGENCY: IconShip,
  INVESTMENT: IconTrendingUp,
  // The three below appear only when the applicant has declared a licence
  // type in them (see the family filter below); listed here so the record
  // stays total either way.
  MARITIME_PERSONNEL: IconShip,
  VESSEL_SERVICES: IconAnchor,
  WAIVER_SERVICES: IconShieldOff,
};

function formatFee(amount: string | number | null, currency: string, noFeeLabel: string): string {
  if (amount === null || amount === '') return noFeeLabel;
  const value = Number(amount);
  if (!Number.isFinite(value)) return noFeeLabel;
  return `${value.toLocaleString('en-US')} ${currency}`;
}

export function LicenseCatalogue() {
  const navigate = useNavigate();
  const localized = useLocalized();
  const { t } = useTranslation();
  const { data: types } = useGetLicenseTypesQuery();
  const { data: categories } = useGetLicenseCategoriesQuery();
  const { data: operatorTypes, isLoading: loadingOperatorTypes } =
    useGetMyOperatorTypesQuery();
  // An escape hatch, not a preference: someone who wants to see what else
  // exists can, without first editing their profile to find out.
  const [showAll, setShowAll] = useState(false);

  const declared = useMemo(
    () => new Set((operatorTypes?.items ?? []).map((o) => o.licenseTypeId)),
    [operatorTypes],
  );
  const hasDeclared = declared.size > 0;

  const { groups, orphans } = useMemo(() => {
    const active = (types?.items ?? [])
      .filter((t) => t.isActive)
      // The logistics family, plus whatever this applicant actually declared.
      //
      // `familyKind` is the real data-model classification and is what keeps
      // browse-all to the operator catalogue rather than every certificate and
      // document type in the system. But it is not what decides eligibility:
      // the Operations tab also offers the personal registrations (seafarer,
      // vessel) and the seafarer endorsement, which are DOCUMENT/CERTIFICATE
      // family, so an applicant who declared one of those was shown an empty
      // catalogue — allowed to file, and offered nothing to file. Each of
      // those keys already has an entry point at `/licensing/<key>/apply`
      // (a router redirect for SEAFARER_REGISTRATION and SEAMAN_BOOK, the
      // generic wizard for the rest), so the card leads somewhere real.
      .filter((t) => t.familyKind === 'LOGISTICS_LICENSE' || declared.has(t.id))
      // Only what the applicant operates as. The server enforces the same rule
      // on create; this is what stops them starting an application they will
      // be refused at the end of.
      .filter((t) => showAll || !hasDeclared || declared.has(t.id));
    const catalogue = (categories?.items ?? [])
      .slice()
      .sort((a, b) => a.sortOrder - b.sortOrder);

    const known = new Set(catalogue.map((c) => c.key));
    return {
      groups: catalogue
        .map((category) => ({
          category,
          licenseTypes: active.filter((t) => t.category === category.key),
        }))
        .filter((g) => g.licenseTypes.length > 0),
      // A licence type whose category has no catalogue entry would otherwise
      // vanish from the page entirely.
      orphans: active.filter((t) => !known.has(t.category)),
    };
  }, [types, categories, declared, hasDeclared, showAll]);

  // A profile created before modes existed, or one whose modes were all
  // removed. Showing an empty catalogue would read as "there is nothing for
  // you here" when the truth is "tell us what you do".
  if (!loadingOperatorTypes && !hasDeclared && !showAll) {
    return (
      <Card withBorder radius="md" padding="xl">
        <Stack gap="xs" align="center">
          <ThemeIcon variant="light" color="emaPrimary" size="lg" radius="xl">
            <IconBuildingWarehouse size={18} />
          </ThemeIcon>
          <Text size="sm" fw={600}>
            {t('licensing.catalogue.emptyTitle')}
          </Text>
          <Text size="sm" c="dimmed" ta="center" maw={520}>
            {t('licensing.catalogue.emptyBody')}
          </Text>
          <Group gap="sm" mt="xs">
            <Button size="xs" onClick={() => navigate('/profile#operations')}>
              {t('licensing.catalogue.setOperations')}
            </Button>
            <Button size="xs" variant="subtle" onClick={() => setShowAll(true)}>
              {t('licensing.catalogue.browseAll')}
            </Button>
          </Group>
        </Stack>
      </Card>
    );
  }

  if (groups.length === 0 && orphans.length === 0) {
    return (
      <Card withBorder radius="md" padding="xl">
        <Center>
          <Stack gap={6} align="center">
            <ThemeIcon variant="light" color="gray" size="lg" radius="xl">
              <IconFileText size={18} />
            </ThemeIcon>
            <Text size="sm" c="dimmed" ta="center">
              {t('licensing.catalogue.noneAvailable')}
            </Text>
          </Stack>
        </Center>
      </Card>
    );
  }

  // In browse-all, a card the applicant cannot yet apply for sends them to the
  // Operations tab rather than into a form the server would refuse to accept.
  const canApply = (type: LicenseType) => !hasDeclared || declared.has(type.id);
  const select = (type: LicenseType) =>
    canApply(type)
      ? navigate(`/licensing/${type.key}/apply`)
      : navigate('/profile#operations');

  return (
    <Stack gap="lg">
      {showAll && hasDeclared && (
        <Group gap="xs">
          <Text size="xs" c="dimmed">
            {t('licensing.catalogue.showingAll')}
          </Text>
          <Anchor size="xs" onClick={() => setShowAll(false)}>
            {t('licensing.catalogue.showOnlyMine')}
          </Anchor>
        </Group>
      )}
      {groups.map(({ category, licenseTypes }) => (
        <CategoryGroup
          key={category.key}
          icon={CATEGORY_ICONS[category.key] ?? IconFileText}
          title={localized(category.name)}
          description={localized(category.description)}
          licenseTypes={licenseTypes}
          canApply={canApply}
          onSelect={select}
        />
      ))}
      {orphans.length > 0 && (
        <CategoryGroup
          icon={IconFileText}
          title={t('licensing.catalogue.otherLicences')}
          description={t('licensing.catalogue.otherLicencesDescription')}
          licenseTypes={orphans}
          canApply={canApply}
          onSelect={select}
        />
      )}
      {hasDeclared && !showAll && (
        <Group gap="xs">
          <Text size="xs" c="dimmed">
            {t('licensing.catalogue.showingMine')}
          </Text>
          <Anchor size="xs" onClick={() => setShowAll(true)}>
            {t('licensing.catalogue.browseAll')}
          </Anchor>
        </Group>
      )}
    </Stack>
  );
}

function CategoryGroup({
  icon: Icon,
  title,
  description,
  licenseTypes,
  canApply,
  onSelect,
}: {
  icon: typeof IconShip;
  title: string;
  description: string;
  licenseTypes: LicenseType[];
  canApply: (type: LicenseType) => boolean;
  onSelect: (type: LicenseType) => void;
}) {
  return (
    <Box>
      <Group gap="xs" mb="xs">
        <ThemeIcon variant="light" color="emaTeal" radius="md" size="md">
          <Icon size={16} />
        </ThemeIcon>
        <Text fw={600} size="sm">
          {title}
        </Text>
        <Text size="xs" c="dimmed">
          {description}
        </Text>
      </Group>
      <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
        {licenseTypes.map((type) => (
          <LicenseTypeCard
            key={type.id}
            type={type}
            canApply={canApply(type)}
            onSelect={onSelect}
          />
        ))}
      </SimpleGrid>
    </Box>
  );
}

function LicenseTypeCard({
  type,
  canApply,
  onSelect,
}: {
  type: LicenseType;
  canApply: boolean;
  onSelect: (type: LicenseType) => void;
}) {
  const localized = useLocalized();
  const { t } = useTranslation();
  const capital = type.capitalThreshold ? Number(type.capitalThreshold) : null;

  return (
    <Card
      withBorder
      radius="md"
      padding="md"
      style={{ cursor: canApply ? 'pointer' : 'default', height: '100%' }}
      onClick={canApply ? () => onSelect(type) : undefined}
    >
      <Stack gap="xs" justify="space-between" h="100%">
        <Box>
          <Group justify="space-between" align="flex-start" wrap="nowrap">
            <Text fw={600} size="sm" lh={1.35}>
              {localized(type.name)}
            </Text>
            {canApply ? (
              <IconChevronRight
                size={16}
                color="var(--mantine-color-dimmed)"
                style={{ flexShrink: 0, marginTop: 2 }}
              />
            ) : (
              <IconLock
                size={16}
                color="var(--mantine-color-dimmed)"
                style={{ flexShrink: 0, marginTop: 2 }}
              />
            )}
          </Group>
          {type.description && (
            <Text size="xs" c="dimmed" mt={6} lineClamp={3}>
              {localized(type.description)}
            </Text>
          )}
        </Box>

        <Box>
          <Group gap={6} mt="sm">
            <Badge size="sm" variant="light" color="emaPrimary">
              {formatFee(type.feeNewApplication, type.feeCurrency, t('licensing.catalogue.noFee'))}
            </Badge>
            {capital && (
              <Tooltip label={t('licensing.catalogue.capitalTooltip')}>
                <Badge size="sm" variant="light" color="gray">
                  {t('licensing.catalogue.capitalBadge', { amount: capital.toLocaleString('en-US') })}
                </Badge>
              </Tooltip>
            )}
            {type.issuesCertificate ? (
              <Badge size="sm" variant="light" color="teal">
                {t('licensing.catalogue.validityBadge', { months: type.validityMonths })}
              </Badge>
            ) : (
              <Tooltip label={t('licensing.catalogue.evaluationTooltip')}>
                <Badge size="sm" variant="light" color="gray">
                  {t('licensing.catalogue.evaluationOnly')}
                </Badge>
              </Tooltip>
            )}
          </Group>
          <RequirePermission anyOf={[LICENSE_PERMISSIONS.CREATE_APPLICATION]} hideOnly>
            <Button
              fullWidth
              mt="sm"
              size="xs"
              variant="light"
              disabled={!canApply}
              rightSection={<IconArrowRight size={14} />}
            >
              {t('licensing.catalogue.startApplication')}
            </Button>
            {/* A disabled button on its own only says "no". This says why, and
                where to go about it — the licence is offered against a declared
                mode of operation, and the server refuses a create for one the
                applicant has not declared. Called out rather than set in dimmed
                small print: it is the only thing on a locked card the applicant
                can act on. */}
            {!canApply && (
              <Alert
                variant="light"
                color="orange"
                radius="md"
                mt="sm"
                p="xs"
              >
                <Text size="xs" lh={1.4}>
                  {t('licensing.catalogue.lockedHint')}
                </Text>
                <Anchor
                  size="xs"
                  fw={600}
                  component="button"
                  type="button"
                  mt={4}
                  onClick={(event) => {
                    // The card is inert while locked, but the anchor inside it
                    // must not re-trigger anything if that ever changes.
                    event.stopPropagation();
                    onSelect(type);
                  }}
                >
                  {t('licensing.catalogue.addToOperations')}
                </Anchor>
              </Alert>
            )}
          </RequirePermission>
        </Box>
      </Stack>
    </Card>
  );
}

export default LicenseCatalogue;
