import {
  Badge,
  Card,
  Checkbox,
  Divider,
  Grid,
  Group,
  Text,
  TextInput,
  Tooltip,
} from '@mantine/core';
import { IconAlertTriangle, IconMapPin } from '@tabler/icons-react';
import { useTranslation } from 'react-i18next';
import {
  conditionHolds,
  displayFieldValue,
  useLocalized,
  type FormFieldConfig,
  type FormSectionConfig,
} from '@ema-platform/api';
import { useDateDisplayer } from '@ema-platform/shared';

/** A section as it will be rendered: config where there is some, key otherwise. */
interface ResolvedSection {
  key: string;
  title: string;
  description?: string;
  fields: { field: FormFieldConfig; value: unknown }[];
}

interface FormDetailsTabProps {
  /** The application's answers, keyed by section. */
  formData: Record<string, Record<string, unknown>>;
  /** The licence type's form schema — the order and labels to render by. */
  configSections: FormSectionConfig[];
  currency?: string;
  /** sectionKey -> remark. Owned by the review page. */
  flags: Record<string, { remark: string }>;
  onToggleFlag: (sectionKey: string) => void;
  onFlagRemark: (sectionKey: string, remark: string) => void;
  /** Resolves a location id to a readable path, when the tree is loaded. */
  resolveLocation?: (locationId: string) => string | undefined;
}

/**
 * What the applicant actually filled in, as the reviewing officer reads it.
 *
 * Replaces a set of bordered key/value tables built by walking `formData`.
 * Three things were wrong with that, all of them worse on a person-centric
 * registration than on a company licence:
 *
 *  - Values were printed with `String(v)`, so a reviewer deciding on a seafarer
 *    read `O_POSITIVE`, `DECK` and `true` — database codes, not the answers
 *    anybody chose. Now resolved through the same field config that rendered
 *    the input, shared with the applicant's own summary (`displayFieldValue`).
 *  - Order came from jsonb key order, which is arbitrary: the declaration could
 *    appear above the emergency contact. Now the schema's `sortOrder` decides,
 *    which is the order the applicant filled them in.
 *  - A location answer is a uuid. Shown raw it told the reviewer nothing;
 *    resolved, it reads "Addis Ababa → Bole → Woreda 03".
 */
export function FormDetailsTab({
  formData,
  configSections,
  currency,
  flags,
  onToggleFlag,
  onFlagRemark,
  resolveLocation,
}: FormDetailsTabProps) {
  const { t, i18n } = useTranslation();
  const localized = useLocalized();
  const showDate = useDateDisplayer();

  const sections = resolveSections();

  /**
   * Sections in schema order, each with its fields in schema order.
   *
   * Anything present in `formData` but absent from the schema is still shown,
   * appended after the configured sections — a stale answer from a since-edited
   * form is exactly the kind of thing a reviewer needs to see, not something to
   * hide because the config moved on.
   */
  function resolveSections(): ResolvedSection[] {
    const configured = [...configSections]
      .sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
      .map((section) => {
        const values = formData[section.key] ?? {};
        const fields = [...(section.fields ?? [])]
          .filter((f) => conditionHolds(f.showWhen, formData))
          .sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0))
          .map((field) => ({ field, value: values[field.key] }));
        return {
          key: section.key,
          title: localized(section.title) || section.key,
          description: localized(section.description) || undefined,
          fields,
        };
      })
      // A section the applicant never reached is noise on a review screen.
      .filter((s) => s.fields.some((f) => hasValue(f.value)));

    const configuredKeys = new Set(configSections.map((s) => s.key));
    const orphans: ResolvedSection[] = Object.entries(formData)
      .filter(([key, values]) => !configuredKeys.has(key) && values)
      .map(([key, values]) => ({
        key,
        title: humanise(key),
        fields: Object.entries(values).map(([fieldKey, value]) => ({
          // No config to render by, so it is treated as free text under a
          // humanised key rather than dropped.
          field: { key: fieldKey, label: { en: humanise(fieldKey) }, type: 'TEXT' } as FormFieldConfig,
          value,
        })),
      }));

    return [...configured, ...orphans];
  }

  function display(field: FormFieldConfig, value: unknown): string {
    // A location is stored as a tree id; the reviewer needs the place.
    if (isLocationField(field) && typeof value === 'string' && value) {
      return resolveLocation?.(value) ?? value;
    }
    return displayFieldValue(field, value, {
      language: i18n.language,
      showDate,
      currency,
    });
  }

  return (
    <Grid>
      {sections.map((section) => {
        const flagged = Boolean(flags[section.key]);
        const missing = section.fields.filter((f) => !hasValue(f.value)).length;

        return (
          <Grid.Col span={12} key={section.key}>
            <Card withBorder padding="md" radius="md">
              <Group justify="space-between" wrap="nowrap" align="flex-start">
                <div style={{ minWidth: 0 }}>
                  <Group gap="xs">
                    <Text fw={600} size="sm">
                      {section.title}
                    </Text>
                    {missing > 0 && (
                      <Tooltip
                        label={t(
                          'review.missingAnswers',
                          'Left blank by the applicant',
                        )}
                      >
                        <Badge
                          size="xs"
                          color="gray"
                          variant="light"
                          leftSection={<IconAlertTriangle size={10} />}
                        >
                          {missing}
                        </Badge>
                      </Tooltip>
                    )}
                  </Group>
                  {section.description && (
                    <Text size="xs" c="dimmed" mt={2}>
                      {section.description}
                    </Text>
                  )}
                </div>
                <Checkbox
                  size="xs"
                  label={t('review.needsCorrection', 'Needs correction')}
                  checked={flagged}
                  onChange={() => onToggleFlag(section.key)}
                  style={{ flexShrink: 0 }}
                />
              </Group>

              <Divider my="sm" />

              {/* Label above value, two per row — a reviewer scans a definition
                  list far faster than a bordered table of the same answers. */}
              <Grid gutter="sm">
                {section.fields.map(({ field, value }) => {
                  const text = display(field, value);
                  const answered = hasValue(value) && text !== '';
                  return (
                    <Grid.Col
                      span={{ base: 12, sm: field.type === 'TEXTAREA' ? 12 : 6 }}
                      key={field.key}
                    >
                      <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
                        {localized(field.label) || field.key}
                      </Text>
                      <Group gap={4} wrap="nowrap" align="center" mt={2}>
                        {answered && isLocationField(field) && (
                          <IconMapPin size={13} style={{ flexShrink: 0, opacity: 0.6 }} />
                        )}
                        <Text
                          size="sm"
                          c={answered ? undefined : 'dimmed'}
                          fs={answered ? undefined : 'italic'}
                          style={{ wordBreak: 'break-word' }}
                        >
                          {answered
                            ? text
                            : t('review.notProvided', 'Not provided')}
                        </Text>
                      </Group>
                    </Grid.Col>
                  );
                })}
              </Grid>

              {flagged && (
                <TextInput
                  mt="sm"
                  size="xs"
                  withAsterisk
                  placeholder={t(
                    'review.correctionPlaceholder',
                    'What must the applicant correct?',
                  )}
                  // Flagging without saying why is what the applicant would
                  // receive: "fix this section", and nothing else.
                  error={
                    flags[section.key].remark.trim()
                      ? null
                      : t('review.correctionRequired', 'Say what must be corrected')
                  }
                  value={flags[section.key].remark}
                  onChange={(e) => {
                    // Read here, not inside the updater: React nulls
                    // `currentTarget` when the handler returns, and the updater
                    // runs afterwards during the re-render.
                    onFlagRemark(section.key, e.currentTarget.value);
                  }}
                />
              )}
            </Card>
          </Grid.Col>
        );
      })}
    </Grid>
  );
}

function hasValue(value: unknown): boolean {
  return value !== null && value !== undefined && value !== '';
}

/** English-pinned, like the portal's own location override. */
function isLocationField(field: Pick<FormFieldConfig, 'key' | 'label'>): boolean {
  return (
    field.key === 'locationId' ||
    (field.label?.en ?? '').trim().toLowerCase() === 'location'
  );
}

function humanise(key: string): string {
  const spaced = key.replace(/([A-Z])/g, ' $1').replace(/[_-]+/g, ' ');
  return spaced.charAt(0).toUpperCase() + spaced.slice(1).trim();
}
