import { useRef, useState } from 'react';
import {
  Alert,
  Badge,
  Button,
  Card,
  FileButton,
  Group,
  Loader,
  Stack,
  Text,
} from '@mantine/core';
import {
  IconAlertTriangle,
  IconCheck,
  IconFileUpload,
} from '@tabler/icons-react';
import {
  conditionHolds,
  useLocalized,
  uploadDocument,
  type Attachment,
  type DocumentRequirement,
} from '@ema-platform/api';
import { useTranslation } from 'react-i18next';
import { FilePreviewModal } from '@ema-platform/ui';

const MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;

interface Props {
  requirements: DocumentRequirement[];
  attachments: Attachment[];
  formData: Record<string, Record<string, unknown>>;
  ownerType: 'APPLICATION' | 'APPLICATION_STAFF';
  ownerId: string;
  /** Document keys the officer flagged; these render as "needs fixing". */
  flagged?: Record<string, string>;
  /** When set, only flagged slots accept a new upload. */
  restrictToFlagged?: boolean;
  /**
   * Requirement keys opened because a flagged section drives their condition —
   * a category correction can make documents newly required, and those have to
   * be uploadable even though the officer flagged no document.
   */
  alsoUnlocked?: string[];
  onUploaded: () => void;
  readOnly?: boolean;
}

/**
 * The upload slots for an application, driven by the configured document
 * requirements. Conditional slots appear only when their rule matches the
 * answers given — an owned vehicle asks for a libre, a rented one for the
 * rental agreement.
 */
export function DocumentSlots({
  requirements,
  attachments,
  formData,
  ownerType,
  ownerId,
  flagged = {},
  restrictToFlagged = false,
  alsoUnlocked = [],
  onUploaded,
  readOnly,
}: Props) {
  const localized = useLocalized();
  const { t } = useTranslation();
  const [busy, setBusy] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [preview, setPreview] = useState<{
    url: string;
    title: string;
    mimeType?: string | null;
  } | null>(
    null,
  );
  const resetRefs = useRef<Record<string, () => void>>({});

  const required = requirements.filter(
    (r) =>
      r.mode === 'ALWAYS' ||
      r.mode === 'OPTIONAL' ||
      (r.mode === 'CONDITIONAL' && conditionHolds(r.conditionExpression, formData)),
  );

  async function handle(documentKey: string, file: File | null) {
    if (!file) return;
    if (file.size > MAX_FILE_SIZE_BYTES) {
      setError(
        t('licensing.msg.fileTooLarge', {
          size: `${(file.size / 1024 / 1024).toFixed(1)}MB`,
        }),
      );
      resetRefs.current[documentKey]?.();
      return;
    }
    setBusy(documentKey);
    setError(null);
    const result = await uploadDocument({ ownerType, ownerId, documentKey, file });
    setBusy(null);
    resetRefs.current[documentKey]?.();
    if (result.ok) onUploaded();
    else setError(result.error);
  }

  return (
    <Stack gap="sm">
      {error && (
        <Alert color="red" icon={<IconAlertTriangle size={16} />}>
          {error}
        </Alert>
      )}
      {required.map((requirement) => {
        const existing = attachments.find((a) => a.documentKey === requirement.key);
        const uploaded = Boolean(existing?.files?.length);
        const files = existing?.files ?? [];
        const fromVault = Boolean(existing?.copiedFromAttachmentId);
        const flagRemark = flagged[requirement.key];
        const locked =
          readOnly ||
          (restrictToFlagged &&
            !flagRemark &&
            !alsoUnlocked.includes(requirement.key));

        return (
          <Card
            key={requirement.id}
            withBorder
            padding="md"
            style={{
              borderColor: flagRemark
                ? 'var(--mantine-color-orange-5)'
                : uploaded
                  ? 'var(--mantine-color-teal-4)'
                  : undefined,
              borderStyle: uploaded ? 'solid' : 'dashed',
            }}
          >
            <Group justify="space-between" wrap="nowrap" align="flex-start">
              <div style={{ minWidth: 0 }}>
                <Group gap="xs">
                  <Text fw={600} size="sm">
                    {localized(requirement.name)}
                  </Text>
                  {requirement.mode === 'CONDITIONAL' && (
                    <Badge size="xs" variant="light" color="grape">
                      {t('licensing.documents.conditional')}
                    </Badge>
                  )}
                  {requirement.mode === 'OPTIONAL' && (
                    <Badge size="xs" variant="light" color="gray">
                      {t('common.optional')}
                    </Badge>
                  )}
                  {uploaded && !flagRemark && (
                    <Badge size="xs" color="teal" leftSection={<IconCheck size={10} />}>
                      {t('licensing.documents.uploaded')}
                    </Badge>
                  )}
                  {/* Filled from the applicant's own vault rather than
                      uploaded here — without saying so, a file they never
                      attached to this application looks like a mistake. */}
                  {fromVault && !flagRemark && (
                    <Badge size="xs" variant="light" color="blue">
                      {t('licensing.documents.fromVault')}
                    </Badge>
                  )}
                </Group>
                {requirement.description && (
                  <Text size="xs" c="dimmed" mt={2}>
                    {localized(requirement.description)}
                  </Text>
                )}
                {files.map((file) => (
                  <Text key={file.id} size="xs" c="dimmed" truncate>
                    {file.originalName} · {(Number(file.sizeBytes) / 1024).toFixed(0)} KB
                  </Text>
                ))}
                {flagRemark && (
                  <Text size="xs" c="orange.7" mt={4}>
                    {t('licensing.documents.officerRemark', { name: flagRemark })}
                  </Text>
                )}
              </div>

              <Group gap="xs" wrap="nowrap">
                {files
                  .filter((file) => file.url)
                  .map((file, index) => (
                    <Button
                      key={file.id}
                      size="xs"
                      variant="subtle"
                      onClick={() =>
                        setPreview({
                          url: file.url as string,
                          title: file.originalName,
                          mimeType: file.mimeType,
                        })
                      }
                    >
                      {files.length > 1
                        ? `${t('licensing.documents.view')} ${index + 1}`
                        : t('licensing.documents.view')}
                    </Button>
                  ))}
                {!locked && (
                  <FileButton
                    resetRef={(r) => {
                      if (r) resetRefs.current[requirement.key] = r;
                    }}
                    onChange={(file) => handle(requirement.key, file)}
                    accept={requirement.allowedMimeTypes?.join(',')}
                  >
                    {(props) => (
                      <Button
                        {...props}
                        size="xs"
                        variant={uploaded ? 'light' : 'filled'}
                        leftSection={
                          busy === requirement.key ? (
                            <Loader size={12} type="oval" />
                          ) : (
                            <IconFileUpload size={14} />
                          )
                        }
                        disabled={busy === requirement.key}
                      >
                        {uploaded
                          ? t('licensing.documents.replace')
                          : t('licensing.documents.upload')}
                      </Button>
                    )}
                  </FileButton>
                )}
              </Group>
            </Group>
          </Card>
        );
      })}
      <FilePreviewModal
        opened={Boolean(preview)}
        onClose={() => setPreview(null)}
        url={preview?.url ?? ''}
        title={preview?.title}
        mimeType={preview?.mimeType}
      />
    </Stack>
  );
}
