import { useEffect, useMemo, useState } from 'react';
import {
  Alert,
  Badge,
  Button,
  Checkbox,
  Group,
  Loader,
  Modal,
  Stack,
  Text,
  Title,
} from '@mantine/core';
import { IconAlertTriangle, IconBuildingWarehouse } from '@tabler/icons-react';
import { useTranslation } from 'react-i18next';
import {
  extractErrorMessage,
  useLocalized,
  useGetLicenseTypesQuery,
  useGetMyOperatorTypesQuery,
  useUpdateMyOperatorTypesMutation,
} from '@ema-platform/api';
import { useUpdateMyAccountTypeMutation } from '@ema-platform/auth';
import { notify, ModalFooter } from '@ema-platform/ui';
import { useDateDisplayer } from '@ema-platform/shared';

/**
 * Registrations and seafarer-only services an applicant declares for
 * themselves rather than for a company.
 *
 * Named explicitly rather than inferred from `requiresOperatorMode: false`,
 * because that flag is also false for things nobody declares up front — a
 * waiver is requested per shipment, not adopted as an identity.
 * ENDORSEMENT_SEAFARER belongs here for the same reason as the two
 * registrations: a seafarer requesting one is not declaring a logistics mode
 * of operation, and without this key `accountTypeFor` below would fall the
 * account through to the company-representative type instead of `SEAFARER`.
 */
const PERSONAL_REGISTRATION_KEYS = [
  'SEAFARER_REGISTRATION',
  'VESSEL_REGISTRATION',
  'ENDORSEMENT_SEAFARER',
];

/**
 * Declared modes -> portal account type.
 *
 * The account type is what the server computes portal permissions from, and
 * therefore what decides which services appear in the sidebar at all. Modes
 * of operation grant nothing on their own: an applicant who declared
 * "freight forwarder" here was still left on the seafarer default and never
 * saw the logistics or vessel entries.
 *
 * One type per account against a multi-select, so the widest capability wins:
 * a company representative who also sails still needs the company services,
 * and the seafarer side stays reachable through their own registration.
 */
function accountTypeFor(keys: string[]): string | null {
  if (keys.some((key) => !PERSONAL_REGISTRATION_KEYS.includes(key))) {
    // Historical enum value for the logistics company representative.
    return 'FRIGHTER';
  }
  if (keys.includes('VESSEL_REGISTRATION')) return 'VESSEL_OWNER';
  if (keys.includes('SEAFARER_REGISTRATION')) return 'SEAFARER';
  if (keys.includes('ENDORSEMENT_SEAFARER')) return 'SEAFARER';
  return null;
}

/**
 * The applicant's modes of operation — what they do, and therefore which
 * licences the portal offers them.
 *
 * The options come from the licence-type catalogue rather than a list in the
 * code, so a sixth licence type configured by EMA appears here without a
 * release. Removing a mode is confirmed separately from adding one: adding
 * only widens what is on offer, while removing changes what the applicant can
 * still file.
 */
export function OperationsFormContent({
  onSaved,
}: {
  /**
   * Called once the set is stored with the keys of the selected licence
   * types — the onboarding step uses them to pick where to go next.
   */
  onSaved?: (selectedKeys: string[]) => void;
} = {}) {
  const { t } = useTranslation();
  const { data: catalogue, isLoading: loadingTypes } = useGetLicenseTypesQuery();
  const { data: mine, isLoading: loadingMine } = useGetMyOperatorTypesQuery();
  const [save, { isLoading: saving }] = useUpdateMyOperatorTypesMutation();
  const [setAccountType] = useUpdateMyAccountTypeMutation();
  const localized = useLocalized();

  const declaredIds = useMemo(
    () => (mine?.items ?? []).map((o) => o.licenseTypeId),
    [mine],
  );
  const [selected, setSelected] = useState<string[]>([]);
  const [confirmingRemoval, setConfirmingRemoval] = useState(false);

  // Re-sync whenever the server's answer changes — including after a save, so
  // the form reflects what was actually stored rather than what was typed.
  useEffect(() => setSelected(declaredIds), [declaredIds]);

  /**
   * The operator licences, plus the two registrations an applicant declares
   * for themselves.
   *
   * Seafarer and vessel registration are not company modes of operation, so
   * they carry `requiresOperatorMode: false` and were filtered out here. But
   * this screen is also where a new applicant says what they are, and someone
   * registering as a seafarer or a vessel owner had no way to say so — they
   * landed on an onboarding step that did not describe them.
   *
   * Listed separately below rather than mixed in, because declaring "I am a
   * seafarer" is a different kind of statement from "my company forwards
   * freight".
   */
  const { operatorOptions, personalOptions } = useMemo(() => {
    const active = (catalogue?.items ?? []).filter((t) => t.isActive);
    return {
      operatorOptions: active.filter((t) => t.requiresOperatorMode !== false),
      personalOptions: active.filter(
        (t) =>
          t.requiresOperatorMode === false &&
          PERSONAL_REGISTRATION_KEYS.includes(t.key),
      ),
    };
  }, [catalogue]);

  const options = useMemo(
    () => [...operatorOptions, ...personalOptions],
    [operatorOptions, personalOptions],
  );

  const showDate = useDateDisplayer();
  const removed = declaredIds.filter((id) => !selected.includes(id));
  const dirty =
    removed.length > 0 || selected.some((id) => !declaredIds.includes(id));

  const lastChanged = useMemo(() => {
    const dates = (mine?.items ?? [])
      .map((o) => o.declaredAt)
      .filter((d): d is string => Boolean(d))
      .sort();
    return dates.length > 0 ? dates[dates.length - 1] : null;
  }, [mine]);

  async function persist() {
    try {
      await save({ licenseTypeIds: selected }).unwrap();
      const selectedKeys = options
        .filter((t) => selected.includes(t.id))
        .map((t) => t.key);

      const type = accountTypeFor(selectedKeys);
      if (type) {
        // Refused for a registered seafarer, whose type the authority owns
        // (`registered_seafarer_type_is_fixed`), and for staff. The modes are
        // already stored either way, so a refusal here is not the applicant's
        // problem to see.
        await setAccountType({ type }).unwrap().catch(() => undefined);
      }

      setConfirmingRemoval(false);
      notify.success(
        t('profileOperations.updateSuccessBody'),
        t('profileOperations.updateSuccessTitle'),
      );
      onSaved?.(selectedKeys);
    } catch (err) {
      notify.error(extractErrorMessage(err), t('profileOperations.updateErrorTitle'));
    }
  }

  if (loadingTypes || loadingMine) {
    return <Loader size="sm" type="oval" />;
  }

  const removedNames = options
    .filter((t) => removed.includes(t.id))
    .map((t) => localized(t.name));

  return (
    <Stack gap="xl">
      <div>
        <Title order={5}>{t('profileOperations.title')}</Title>
        <Text size="sm" c="dimmed" mb="md">
          {t('profileOperations.description')}
        </Text>

        <Checkbox.Group value={selected} onChange={setSelected}>
          <Stack gap="sm">
            {operatorOptions.map((type) => (
              <Checkbox
                key={type.id}
                value={type.id}
                label={
                  <Group gap="xs" wrap="nowrap">
                    <Text size="sm">{localized(type.name)}</Text>
                    {declaredIds.includes(type.id) && (
                      <Badge size="xs" variant="light" color="teal">
                        {t('profileOperations.current')}
                      </Badge>
                    )}
                  </Group>
                }
                description={
                  type.description ? localized(type.description) : undefined
                }
              />
            ))}

            {/* Kept apart from the company modes above: declaring "I am a
                seafarer" is a different kind of statement from "my company
                forwards freight", and running them together reads as though
                one person could be both at once. */}
            {personalOptions.length > 0 && (
              <>
                <Text size="xs" fw={600} c="dimmed" mt="sm" tt="uppercase">
                  Registering or applying as an individual or vessel owner
                </Text>
                {personalOptions.map((type) => (
                  <Checkbox
                    key={type.id}
                    value={type.id}
                    label={
                      <Group gap="xs" wrap="nowrap">
                        <Text size="sm">{localized(type.name)}</Text>
                        {declaredIds.includes(type.id) && (
                          <Badge size="xs" variant="light" color="teal">
                            Current
                          </Badge>
                        )}
                      </Group>
                    }
                    description={
                      type.description ? localized(type.description) : undefined
                    }
                  />
                ))}
              </>
            )}
          </Stack>
        </Checkbox.Group>

        {options.length === 0 && (
          <Text size="sm" c="dimmed">
            {t('profileOperations.emptyState')}
          </Text>
        )}
      </div>

      {selected.length === 0 && (
        <Alert
          variant="light"
          color="orange"
          icon={<IconAlertTriangle size={18} />}
          title={t('profileOperations.noneSelectedTitle')}
        >
          {t('profileOperations.noneSelectedBody')}
        </Alert>
      )}

      <Group justify="space-between">
        <Text size="xs" c="dimmed">
          {lastChanged
            ? t('profileOperations.lastChanged', { date: showDate(lastChanged) })
            : t('profileOperations.notSetYet')}
        </Text>
        <Group gap="sm">
          {dirty && (
            <Button
              variant="subtle"
              size="sm"
              onClick={() => setSelected(declaredIds)}
            >
              {t('profileOperations.discardChanges')}
            </Button>
          )}
          <Button
            size="sm"
            loading={saving}
            disabled={!dirty}
            leftSection={<IconBuildingWarehouse size={16} />}
            onClick={() =>
              removed.length > 0 ? setConfirmingRemoval(true) : persist()
            }
          >
            {t('profileOperations.saveOperations')}
          </Button>
        </Group>
      </Group>

      {/* Removal is the one direction that takes something away, so it is
          spelled out rather than saved on a single click. */}
      <Modal
        opened={confirmingRemoval}
        onClose={() => setConfirmingRemoval(false)}
        title={t('profileOperations.removeModalTitle')}
        centered
      >
        <Stack gap="md">
          <Text size="sm">
            {t('profileOperations.removingPrefix')}{' '}
            <Text span fw={600}>
              {removedNames.join(', ')}
            </Text>
            .
          </Text>
          <Text size="sm" c="dimmed">
            {t('profileOperations.removeConsequence')}
          </Text>
          <ModalFooter gap="sm">
            <Button
              variant="default"
              onClick={() => setConfirmingRemoval(false)}
            >
              {t('common.cancel')}
            </Button>
            <Button color="orange" loading={saving} onClick={persist}>
              {t('profileOperations.removeAndSave')}
            </Button>
          </ModalFooter>
        </Stack>
      </Modal>
    </Stack>
  );
}
