import { ActionIcon, Autocomplete, Checkbox, Group, Paper, Select, Stack, Switch, Text, TextInput } from '@mantine/core';
import { IconPlus, IconTrash } from '@tabler/icons-react';
import { useTranslation } from 'react-i18next';
import type { FieldCondition, FormSchemaPalette } from '@ema-platform/api';
import { useLocalized } from '@ema-platform/api';
import type { ConditionTarget } from '../config/schema-paths';

/** `FieldCondition` plus the renewal-only extra `DocumentRequirement.conditionExpression` carries. */
export type ConditionValue = FieldCondition & { previousDocExpired?: string };

/** One editable `anyOf` arm — a single-field condition, same shape a plain condition holds. */
type ConditionArm = Omit<ConditionValue, 'anyOf' | 'previousDocExpired'>;

type Operator = 'equals' | 'notEquals' | 'in' | 'isSet';

function operatorOf(condition: ConditionValue | undefined): Operator | null {
  if (!condition) return null;
  if (condition.isSet !== undefined) return 'isSet';
  if (condition.equals !== undefined) return 'equals';
  if (condition.notEquals !== undefined) return 'notEquals';
  if (condition.in !== undefined) return 'in';
  return null;
}

/** Best-effort type for a raw stored value, so re-editing an existing
 * condition renders a number input for a numeric value rather than text. */
function coerce(raw: string, targetType: string | undefined): string | number | boolean {
  if (targetType === 'BOOLEAN') return raw === 'true';
  if (targetType === 'NUMBER' || targetType === 'MONEY') {
    const n = Number(raw);
    return Number.isFinite(n) && raw.trim() !== '' ? n : raw;
  }
  return raw;
}

/** One-line summary of a condition for read-only chips ("when X = Y", "when X = Y or W = Z"). */
export function describeCondition(
  condition: ConditionValue,
  t: (key: string, fallback: string) => string,
): string {
  if (condition.anyOf?.length) {
    return condition.anyOf.map((arm) => describeCondition(arm, t)).join(` ${t('certReq.condition.or', 'or')} `);
  }
  if (!condition.field) return '';
  const parts = [condition.field];
  if (condition.equals !== undefined) parts.push(`= ${condition.equals}`);
  if (condition.notEquals !== undefined) parts.push(`≠ ${condition.notEquals}`);
  if (condition.in !== undefined) parts.push(`∈ [${condition.in.join(', ')}]`);
  if (condition.isSet !== undefined) {
    parts.push(condition.isSet ? t('certReq.condition.isSet', 'is set') : t('certReq.condition.isNotSet', 'is not set'));
  }
  return parts.join(' ');
}

/**
 * The field/operator/value trio for one condition — a plain condition, or one
 * arm of an `anyOf`. No enable switch of its own; the caller owns whether
 * this row exists at all.
 */
function ConditionArmFields({
  value,
  onChange,
  targets,
  palette,
}: {
  value: ConditionArm;
  onChange: (value: ConditionArm) => void;
  targets: ConditionTarget[];
  palette: FormSchemaPalette | undefined;
}) {
  const { t } = useTranslation();
  const localized = useLocalized();

  const operator = operatorOf(value) ?? 'equals';
  const target = targets.find((c) => c.path === value.field);
  const operators = palette?.conditionOperators ?? ['equals', 'notEquals', 'in', 'isSet'];

  function setField(field: string) {
    onChange({ field, ...(operator === 'isSet' ? { isSet: true } : { equals: '' }) });
  }

  function setOperator(next: Operator) {
    if (!value.field) return;
    const base: ConditionArm = { field: value.field };
    if (next === 'isSet') base.isSet = true;
    else if (next === 'in') base.in = [];
    else if (next === 'notEquals') base.notEquals = '';
    else base.equals = '';
    onChange(base);
  }

  function setValueRaw(raw: string) {
    if (!value.field) return;
    const coerced = coerce(raw, target?.field.type);
    if (operator === 'equals') onChange({ field: value.field, equals: coerced });
    else if (operator === 'notEquals') onChange({ field: value.field, notEquals: coerced });
  }

  function setInValues(raws: string[]) {
    if (!value.field) return;
    onChange({
      field: value.field,
      in: raws.map((r) => coerce(r, target?.field.type)) as (string | number)[],
    });
  }

  return (
    <Stack gap="xs">
      <Autocomplete
        label={t('certReq.condition.field', 'Field path')}
        placeholder="certificate.rank"
        description={t(
          'certReq.condition.fieldHelp',
          'Dot path into the form, e.g. sectionKey.fieldKey',
        )}
        data={targets.map((c) => c.path)}
        value={value.field ?? ''}
        onChange={setField}
      />

      <Group grow align="flex-start">
        <Select
          label={t('certReq.condition.operator', 'Operator')}
          data={operators.map((op) => ({ value: op, label: op }))}
          value={operator}
          onChange={(v) => v && setOperator(v as Operator)}
          allowDeselect={false}
        />

        {operator !== 'isSet' && operator !== 'in' && target?.field.type === 'SELECT' && (
          <Select
            label={t('certReq.condition.value', 'Value')}
            data={(target.field.options ?? []).map((o) => ({
              value: o.value,
              label: localized(o.label) || o.value,
            }))}
            value={String(value.equals ?? value.notEquals ?? '')}
            onChange={(v) => v !== null && setValueRaw(v)}
          />
        )}

        {operator !== 'isSet' && operator !== 'in' && target?.field.type !== 'SELECT' && (
          target?.field.type === 'BOOLEAN' ? (
            <Checkbox
              mt="xl"
              label={t('certReq.condition.value', 'Value')}
              checked={Boolean(value.equals ?? value.notEquals ?? false)}
              onChange={(e) => setValueRaw(String(e.currentTarget.checked))}
            />
          ) : (
            <TextInput
              label={t('certReq.condition.value', 'Value')}
              type={target?.field.type === 'NUMBER' || target?.field.type === 'MONEY' ? 'number' : 'text'}
              value={String(value.equals ?? value.notEquals ?? '')}
              onChange={(e) => setValueRaw(e.currentTarget.value)}
            />
          )
        )}

        {operator === 'in' && target?.field.type === 'SELECT' && (
          <Select
            label={t('certReq.condition.values', 'Any of')}
            data={(target.field.options ?? []).map((o) => ({
              value: o.value,
              label: localized(o.label) || o.value,
            }))}
            multiple={undefined}
            value={null}
            onChange={(v) => {
              if (!v) return;
              const current = (value.in ?? []) as string[];
              if (!current.includes(v)) setInValues([...current, v]);
            }}
          />
        )}

        {operator === 'in' && target?.field.type !== 'SELECT' && (
          <TextInput
            label={t('certReq.condition.values', 'Any of (comma-separated)')}
            value={(value.in ?? []).join(', ')}
            onChange={(e) =>
              setInValues(
                e.currentTarget.value
                  .split(',')
                  .map((s) => s.trim())
                  .filter(Boolean),
              )
            }
          />
        )}
      </Group>

      {operator === 'in' && (value.in?.length ?? 0) > 0 && (
        <Group gap={4}>
          {(value.in ?? []).map((v, i) => (
            <Text
              key={`${v}-${i}`}
              fz="xs"
              px={6}
              py={2}
              bg="var(--mantine-color-default-hover)"
              style={{ borderRadius: 4, cursor: 'pointer' }}
              onClick={() => setInValues((value.in ?? []).filter((_, idx) => idx !== i).map(String))}
              title={t('certReq.condition.removeValue', 'Click to remove')}
            >
              {String(v)} ×
            </Text>
          ))}
        </Group>
      )}

      {!target && value.field && (
        <Text fz="xs" c="dimmed">
          {t(
            'certReq.condition.unknownField',
            'This path is not a field in the current schema yet — it will still be saved as typed.',
          )}
        </Text>
      )}
    </Stack>
  );
}

const EMPTY_ARM: ConditionArm = { field: '', equals: '' };

/**
 * Authors one `FieldCondition` (`showWhen` on a section/field, or
 * `conditionExpression` on a document requirement).
 *
 * The field-path box autocompletes from every field already defined in this
 * licence type's schema (`targets`); when the chosen path resolves to a
 * SELECT field, the value picker switches to that field's own options
 * instead of free text — the condition can only ever reference an answer
 * that could actually be chosen.
 *
 * "Any of these" switches to authoring several single-field conditions whose
 * OR is the real condition — needed when the same logical value can live on
 * one of several mutually-exclusive fields (e.g. a rank split by
 * department, see FieldCondition.anyOf).
 */
export function ConditionBuilder({
  value,
  onChange,
  targets,
  palette,
  allowClear = true,
}: {
  value: ConditionValue | null;
  onChange: (value: ConditionValue | null) => void;
  targets: ConditionTarget[];
  palette: FormSchemaPalette | undefined;
  /** Hide the "no condition" toggle — used where a condition is mandatory (CONDITIONAL document mode). */
  allowClear?: boolean;
}) {
  const { t } = useTranslation();

  const active = value !== null;
  const isAnyOf = Boolean(value?.anyOf);
  const arms = (value?.anyOf ?? []) as ConditionArm[];

  function setArm(i: number, arm: ConditionArm) {
    const next = arms.slice();
    next[i] = arm;
    onChange({ anyOf: next });
  }

  function addArm() {
    onChange({ anyOf: [...arms, { ...EMPTY_ARM }] });
  }

  function removeArm(i: number) {
    onChange({ anyOf: arms.filter((_, idx) => idx !== i) });
  }

  function toggleAnyOf(next: boolean) {
    if (next) {
      // Seed the list from whatever single condition already existed, so
      // switching modes doesn't discard work in progress.
      const seed: ConditionArm = value?.field ? (value as ConditionArm) : { ...EMPTY_ARM };
      onChange({ anyOf: [seed] });
    } else {
      // Same, in reverse — the first arm becomes the single condition.
      onChange((arms[0] as ConditionValue) ?? { field: '', equals: '' });
    }
  }

  return (
    <Stack gap="xs">
      {allowClear && (
        <Switch
          label={t('certReq.condition.enable', 'Only apply when a condition holds')}
          checked={active}
          onChange={(e) => onChange(e.currentTarget.checked ? { field: '', equals: '' } : null)}
        />
      )}

      {active && (
        <Stack gap="xs" pl={allowClear ? 'md' : 0}>
          <Switch
            size="sm"
            label={t(
              'certReq.condition.anyOfEnable',
              'Any of these (the value can live on one of several fields)',
            )}
            checked={isAnyOf}
            onChange={(e) => toggleAnyOf(e.currentTarget.checked)}
          />

          {isAnyOf ? (
            <Stack gap="sm">
              {arms.map((arm, i) => (
                <Paper key={i} withBorder p="sm" radius="sm">
                  <Group justify="space-between" mb="xs">
                    <Text fz="xs" fw={600} c="dimmed">
                      {t('certReq.condition.anyOfArm', 'Condition {{n}}', { n: i + 1 })}
                    </Text>
                    <ActionIcon
                      variant="subtle"
                      color="red"
                      size="sm"
                      disabled={arms.length <= 1}
                      onClick={() => removeArm(i)}
                    >
                      <IconTrash size={14} />
                    </ActionIcon>
                  </Group>
                  <ConditionArmFields
                    value={arm}
                    onChange={(next) => setArm(i, next)}
                    targets={targets}
                    palette={palette}
                  />
                </Paper>
              ))}
              <Group>
                <ActionIcon variant="light" onClick={addArm}>
                  <IconPlus size={16} />
                </ActionIcon>
                <Text fz="xs" c="dimmed">
                  {t('certReq.condition.anyOfAdd', 'Add another field')}
                </Text>
              </Group>
            </Stack>
          ) : (
            <ConditionArmFields
              value={value as ConditionArm}
              onChange={(next) => onChange(next)}
              targets={targets}
              palette={palette}
            />
          )}
        </Stack>
      )}
    </Stack>
  );
}
