import { useEffect, useState } from 'react';
import {
  Alert,
  Button,
  Checkbox,
  Modal,
  Select,
  Stack,
  Text,
  Textarea,
  TextInput,
} from '@mantine/core';
import { IconAlertTriangle } from '@tabler/icons-react';
import { useTranslation } from 'react-i18next';
import { ModalFooter } from '@ema-platform/ui';
import type { ResolvedAction } from '../config/actions';

/** Reason codes offered per action. Free text is always available too. */
const REASON_CODES: Record<string, string[]> = {
  reject: [
    'review.reasons.incompleteDocuments',
    'review.reasons.belowCapital',
    'review.reasons.failedInspection',
    'review.reasons.ineligibleApplicant',
    'review.reasons.duplicateApplication',
  ],
  'request-adjustment': [
    'review.reasons.illegibleDocument',
    'review.reasons.expiredDocument',
    'review.reasons.missingDocument',
    'review.reasons.inconsistentDetails',
  ],
  hold: [
    'review.reasons.awaitingThirdParty',
    'review.reasons.legalProceedings',
    'review.reasons.applicantRequest',
  ],
  escalate: [
    'review.reasons.aboveAuthority',
    'review.reasons.policyUnclear',
    'review.reasons.conflictOfInterest',
  ],
};

export interface DecisionSubmission {
  reasonCode?: string;
  reason: string;
  /** Chosen officer, for Assign and Escalate. */
  officerId?: string;
  /** Documents the applicant must fix. Adjustments only. */
  deficiencies: string[];
  /** The message that will be sent, after any officer edit. */
  notificationBody: string;
}

interface DecisionConfirmModalProps {
  action: ResolvedAction | null;
  applicantName: string;
  applicationNumber: string;
  /**
   * What the officer flagged, for the deficiency checklist.
   *
   * Carries a label as well as the key because the key is only human-readable
   * for documents: a form section is camelCase and a flagged staff member is a
   * uuid, which is not something to put in front of an officer.
   */
  flaggedItems?: Array<{ key: string; label: string }>;
  /** Populated for Assign and Escalate, which must name a person. */
  officers?: Array<{ id: string; name: string | null }>;
  submitting?: boolean;
  onClose: () => void;
  onConfirm: (submission: DecisionSubmission) => void;
}

/**
 * One confirmation anatomy for every decision.
 *
 * Each decision used to have its own ad-hoc modal — some with a reason, some
 * without, none showing what the applicant would actually receive. This gives
 * all of them the same five parts: the consequence in plain language naming
 * the applicant and application, a reason (required where it matters), the
 * document deficiency checklist for adjustments, an editable preview of the
 * message that will be sent, and an explicit warning where the step cannot be
 * undone.
 */
export function DecisionConfirmModal({
  action,
  applicantName,
  applicationNumber,
  flaggedItems = [],
  officers = [],
  submitting,
  onClose,
  onConfirm,
}: DecisionConfirmModalProps) {
  const { t } = useTranslation();
  const [reasonCode, setReasonCode] = useState<string | null>(null);
  const [reason, setReason] = useState('');
  const [deficiencies, setDeficiencies] = useState<string[]>([]);
  const [notification, setNotification] = useState('');
  const [acknowledged, setAcknowledged] = useState(false);
  const [officerId, setOfficerId] = useState<string | null>(null);
  const [confirmText, setConfirmText] = useState('');

  const codes = action ? (REASON_CODES[action.id] ?? []) : [];
  // `flaggedItems` is a fresh array on every parent render, so keying the
  // reset effect on its identity would wipe the officer's edits continuously.
  const flaggedKey = flaggedItems.map((item) => item.key).join('|');

  // Reset per opening, and seed the message the applicant will receive so the
  // officer edits real copy rather than composing from nothing.
  useEffect(() => {
    if (!action) return;
    setReasonCode(null);
    setReason('');
    setDeficiencies(flaggedItems.map((item) => item.key));
    setAcknowledged(false);
    setOfficerId(null);
    setConfirmText('');
    setNotification(
      t(`review.notifications.${action.id}`, {
        applicant: applicantName,
        number: applicationNumber,
        defaultValue: t('review.notifications.fallback', {
          applicant: applicantName,
          number: applicationNumber,
          action: t(action.labelKey),
          defaultValue:
            'Dear {{applicant}}, there is an update on application {{number}}: {{action}}.',
        }),
      }),
    );
  }, [action, applicantName, applicationNumber, flaggedKey, t]);

  if (!action) return null;

  // The push-model assignments name a person the same way Assign does, so they
  // reuse this picker rather than each carrying a dialog of their own.
  const needsOfficer =
    action.id === 'assign' ||
    action.id === 'escalate' ||
    action.id === 'assign-reviewer' ||
    action.id === 'assign-inspector';
  const reasonMissing = action.requiresReason && !reason.trim() && !reasonCode;
  const blocked =
    reasonMissing ||
    (needsOfficer && !officerId) ||
    (action.irreversible && !acknowledged) ||
    // A destructive action must have the consequence typed out, not just
    // acknowledged with a tick — it is the last stop before something
    // irreversible happens to a real operator.
    (action.tier === 'destructive' && confirmText.trim() !== applicationNumber);

  return (
    <Modal
      opened
      onClose={onClose}
      title={t(action.labelKey)}
      size="lg"
      // Focus returns to the trigger on close, and Esc dismisses.
      trapFocus
      returnFocus
      closeOnEscape
    >
      <Stack gap="md">
        {/* 1. What is about to happen, in plain language. */}
        <Text size="sm">
          {t(`review.consequences.${action.id}`, {
            applicant: applicantName,
            number: applicationNumber,
            defaultValue: t('review.consequences.fallback', {
              applicant: applicantName,
              number: applicationNumber,
              defaultValue:
                'This updates application {{number}} for {{applicant}}.',
            }),
          })}
        </Text>

        {/* 1b. Who picks it up. */}
        {needsOfficer && (
          <Select
            label={
              action.id === 'escalate'
                ? t('review.supervisor', 'Supervisor')
                : action.id === 'assign-inspector'
                  ? t('review.inspector', 'Inspector')
                  : action.id === 'assign-reviewer'
                    ? t('review.reviewer', 'Reviewer')
                    : t('review.officer', 'Officer')
            }
            placeholder={t('review.officerPlaceholder', 'Select who takes this on')}
            data={officers.map((officer) => ({
              value: officer.id,
              label: officer.name ?? officer.id,
            }))}
            value={officerId}
            onChange={setOfficerId}
            searchable
            withAsterisk
            nothingFoundMessage={t('review.noOfficers', 'No officers found')}
          />
        )}

        {/* 2. Reason — coded plus free text. */}
        {action.requiresReason && (
          <>
            {codes.length > 0 && (
              <Select
                label={t('review.reasonCode', 'Reason')}
                placeholder={t('review.reasonCodePlaceholder', 'Select a reason')}
                data={codes.map((code) => ({ value: code, label: t(code) }))}
                value={reasonCode}
                onChange={setReasonCode}
                clearable
                withAsterisk
              />
            )}
            <Textarea
              label={t('review.reasonDetail', 'Details for the applicant')}
              description={t(
                'review.reasonDetailHint',
                'This text is sent to the applicant verbatim.',
              )}
              value={reason}
              onChange={(event) => setReason(event.currentTarget.value)}
              autosize
              minRows={3}
              withAsterisk={!reasonCode}
            />
          </>
        )}

        {/* 3. Deficiency checklist — the applicant sees exactly this list. */}
        {action.id === 'request-adjustment' && flaggedItems.length > 0 && (
          <Checkbox.Group
            label={t('review.deficiencies', 'Items the applicant must correct')}
            description={t(
              'review.deficienciesHint',
              'Only the ticked items become editable for the applicant.',
            )}
            value={deficiencies}
            onChange={setDeficiencies}
          >
            <Stack gap={4} mt="xs">
              {flaggedItems.map((item) => (
                <Checkbox key={item.key} value={item.key} label={item.label} />
              ))}
            </Stack>
          </Checkbox.Group>
        )}

        {/* 4. Editable preview of the outbound message. */}
        <Textarea
          label={t('review.notificationPreview', 'Message to the applicant')}
          description={t(
            'review.notificationPreviewHint',
            'Sent by SMS and email. Edit before confirming if needed.',
          )}
          value={notification}
          onChange={(event) => setNotification(event.currentTarget.value)}
          autosize
          minRows={3}
        />

        {/* 5. Irreversibility, acknowledged explicitly. */}
        {action.irreversible && (
          <Alert color="red" icon={<IconAlertTriangle size={18} />}>
            <Stack gap="xs">
              <Text size="sm">
                {t(
                  'review.irreversibleWarning',
                  'This decision is final and cannot be undone from the backoffice.',
                )}
              </Text>
              <Checkbox
                checked={acknowledged}
                onChange={(event) => setAcknowledged(event.currentTarget.checked)}
                label={t('review.irreversibleAck', 'I understand this is final')}
              />
            </Stack>
          </Alert>
        )}

        {/* 5b. Destructive actions require the application number typed out. */}
        {action.tier === 'destructive' && (
          <TextInput
            label={t('review.typeToConfirm', {
              number: applicationNumber,
              defaultValue: 'Type {{number}} to confirm',
            })}
            value={confirmText}
            onChange={(event) => setConfirmText(event.currentTarget.value)}
            placeholder={applicationNumber}
            error={
              confirmText && confirmText.trim() !== applicationNumber
                ? t('review.confirmMismatch', 'Does not match')
                : undefined
            }
          />
        )}

        <ModalFooter>
          <Button variant="default" onClick={onClose}>
            {t('common.cancel', 'Cancel')}
          </Button>
          <Button
            color={action.color}
            loading={submitting}
            disabled={blocked}
            onClick={() =>
              onConfirm({
                reasonCode: reasonCode ?? undefined,
                officerId: officerId ?? undefined,
                reason: reason.trim() || (reasonCode ? t(reasonCode) : ''),
                deficiencies,
                notificationBody: notification,
              })
            }
          >
            {t(action.labelKey)}
          </Button>
        </ModalFooter>
      </Stack>
    </Modal>
  );
}
