'use client';
import { FunctionComponent, useMemo, useState } from 'react';
import { useLocale, useTranslations } from 'next-intl';
import { useSnackbar } from 'notistack';
import {
  Box,
  Chip,
  MenuItem,
  Paper,
  Skeleton,
  Stack,
  TextField,
  ToggleButton,
  ToggleButtonGroup,
  Typography,
} from '@mui/material';
import { AppButton, AppLoading, CategoryTile, ErrorState, PriceDisplay, StepperHeader } from '@/components';
import { ApiError } from '@/lib/api/errors';
import { digitsOnly, rialToToman, tomanToRial } from '@/utils';
import {
  useCategoryOptionGroups,
  useCreateVariant,
  useServiceCategories,
  useUpdateVariant,
} from '@/services/catalog';
import { pickCatalogName } from '@/services/catalog/names';
import {
  PRICE_UNITS,
  type NurseServiceVariant,
  type PriceUnit,
  type VariantOptionSelection,
} from '@/services/catalog/types';

interface VariantBuilderProps {
  /** `null` = create (3-step stepper); a variant = edit (category/options locked, price form only). */
  initial: NurseServiceVariant | null;
  onDone: () => void;
  onCancel: () => void;
}

const DEFAULT_UNIT: PriceUnit = 'per_hour';
const MAX_PRICE_DIGITS = 12;
const MAX_DURATION_DIGITS = 4;

/**
 * The nurse variant builder (`CreateVariant` / `UpdateVariant`).
 *
 * **Create** is a 3-step stepper: pick category → answer required/optional option groups → price +
 * unit + duration. Every `is_required` group must be answered before advancing; the price is entered
 * in **Toman** and converted to an IRR digit-string at the field boundary (`tomanToRial`, integer-safe,
 * never a float); the estimated total is shown only from `price` × `sessionCount`, never `price` alone;
 * `display_name` auto-generates from the chosen labels and is editable (left blank ⇒ the server
 * generates it). A duplicate identical listing (`409`) shows a friendly inline warning.
 *
 * **Edit** locks the category + option-set (changing them would change identity) and edits only
 * price/unit/duration/display via `update`.
 */
const VariantBuilder: FunctionComponent<VariantBuilderProps> = ({ initial, onDone, onCancel }) => {
  const t = useTranslations('services');
  const tCatalog = useTranslations('catalog');
  const tc = useTranslations('common');
  const locale = useLocale();
  const { enqueueSnackbar } = useSnackbar();

  const isEdit = initial !== null;

  const createVariant = useCreateVariant();
  const updateVariant = useUpdateVariant();
  const submitting = createVariant.isPending || updateVariant.isPending;

  // --- Create-only state (category → options) ---
  const [activeStep, setActiveStep] = useState(0);
  const [categoryId, setCategoryId] = useState<number | null>(initial?.serviceCategoryId ?? null);
  const [selectedOptions, setSelectedOptions] = useState<Record<number, number>>({});
  const [optionsError, setOptionsError] = useState(false);

  // --- Shared price state (both create step 3 and edit) ---
  // Pre-fill the price field in Toman (the wire carries IRR Rials); the money util does the ÷10.
  const [priceToman, setPriceToman] = useState(initial ? String(rialToToman(initial.price)) : '');
  const [priceUnit, setPriceUnit] = useState<PriceUnit>(initial?.priceUnit ?? DEFAULT_UNIT);
  const [durationStr, setDurationStr] = useState(initial?.sessionCount ? String(initial.sessionCount) : '');
  const [displayNameOverride, setDisplayNameOverride] = useState<string | null>(null);
  const [priceError, setPriceError] = useState(false);
  const [duplicate, setDuplicate] = useState(false);

  const categoriesQuery = useServiceCategories();
  const categories = categoriesQuery.data?.items ?? [];
  const optionGroupsQuery = useCategoryOptionGroups(isEdit ? null : categoryId);
  // Stable reference so the auto-name useMemo below isn't invalidated on every render by a fresh `[]`.
  const groups = useMemo(() => optionGroupsQuery.data ?? [], [optionGroupsQuery.data]);

  const selectedCategory = categories.find((category) => category.id === categoryId) ?? null;
  const missingRequiredGroups = groups.filter((group) => group.isRequired && selectedOptions[group.id] == null);

  // Auto-generated display name preview (create): category + chosen value labels, in the active locale.
  const autoName = useMemo(() => {
    if (isEdit) return initial.displayName;
    if (!selectedCategory) return '';
    const valueLabels = groups
      .map((group) => {
        const valueId = selectedOptions[group.id];
        const value = valueId == null ? null : group.values.find((candidate) => candidate.id === valueId);
        return value ? pickCatalogName(value, locale) : null;
      })
      .filter((label): label is string => label !== null);
    return [pickCatalogName(selectedCategory, locale), ...valueLabels].join(' · ');
  }, [isEdit, initial, selectedCategory, groups, selectedOptions, locale]);

  const displayValue = displayNameOverride ?? autoName;

  const priceValid = priceToman.length > 0 && BigInt(priceToman) > BigInt(0);
  const irr = priceValid ? tomanToRial(priceToman) : null;
  const durationInt = durationStr ? Number(durationStr) : 0;
  const sessionCount = durationInt > 0 ? durationInt : null;

  const selectCategory = (id: number) => {
    if (id === categoryId) return;
    // Switching category invalidates the previous category's option answers + auto-name.
    setCategoryId(id);
    setSelectedOptions({});
    setDisplayNameOverride(null);
    setOptionsError(false);
  };

  const changeOption = (groupId: number, valueId: number | null) => {
    setOptionsError(false);
    // A manual displayName override is intentionally left untouched; the auto-name preview tracks
    // option changes only while the field hasn't been overridden (displayValue = override ?? autoName).
    setSelectedOptions((prev) => {
      const next = { ...prev };
      if (valueId == null) delete next[groupId];
      else next[groupId] = valueId;
      return next;
    });
  };

  const goNextFromOptions = () => {
    if (missingRequiredGroups.length > 0) {
      setOptionsError(true);
      return;
    }
    setActiveStep(2);
  };

  const validatePrice = () => {
    if (!priceValid) {
      setPriceError(true);
      return false;
    }
    return true;
  };

  const submit = () => {
    if (!validatePrice() || irr == null) return;
    const displayName = displayNameOverride?.trim() ? displayNameOverride.trim() : undefined;

    if (isEdit) {
      updateVariant.mutate(
        { id: initial.id, input: { price: irr, priceUnit, sessionCount, displayName } },
        {
          onSuccess: () => {
            enqueueSnackbar(t('saved_toast'), { variant: 'success' });
            onDone();
          },
          onError: () => enqueueSnackbar(t('create_error'), { variant: 'error' }),
        },
      );
      return;
    }

    const options: VariantOptionSelection[] = Object.entries(selectedOptions).map(([groupId, valueId]) => ({
      optionGroupId: Number(groupId),
      optionValueId: valueId,
    }));
    createVariant.mutate(
      { serviceCategoryId: categoryId as number, options, price: irr, priceUnit, sessionCount, displayName },
      {
        onSuccess: () => {
          enqueueSnackbar(t('created_toast'), { variant: 'success' });
          onDone();
        },
        onError: (error) => {
          // The duplicate-listing conflict is a friendly inline warning, never a generic toast.
          if (error instanceof ApiError && error.status === 409) setDuplicate(true);
          else enqueueSnackbar(t('create_error'), { variant: 'error' });
        },
      },
    );
  };

  const priceStep = (
    <Stack sx={{ gap: 2.5 }}>
      <TextField
        label={t('price_label')}
        value={priceToman}
        onChange={(event) => {
          setPriceToman(digitsOnly(event.target.value).slice(0, MAX_PRICE_DIGITS));
          if (priceError) setPriceError(false);
          if (duplicate) setDuplicate(false);
        }}
        error={priceError}
        helperText={priceError ? t('price_required') : t('price_hint')}
        slotProps={{ htmlInput: { dir: 'ltr', inputMode: 'numeric', style: { textAlign: 'start' } } }}
        fullWidth
      />

      <Stack direction={{ xs: 'column', sm: 'row' }} sx={{ gap: 2 }}>
        <TextField
          select
          label={t('unit_label')}
          value={priceUnit}
          onChange={(event) => setPriceUnit(event.target.value as PriceUnit)}
          fullWidth
        >
          {PRICE_UNITS.map((unit) => (
            <MenuItem key={unit} value={unit}>
              {tCatalog(`unit_${unit}`)}
            </MenuItem>
          ))}
        </TextField>

        <TextField
          label={t('duration_label')}
          value={durationStr}
          onChange={(event) => setDurationStr(digitsOnly(event.target.value).slice(0, MAX_DURATION_DIGITS))}
          helperText={t('duration_hint')}
          slotProps={{ htmlInput: { dir: 'ltr', inputMode: 'numeric', style: { textAlign: 'start' } } }}
          fullWidth
        />
      </Stack>

      {irr ? (
        <Paper elevation={0} sx={{ p: 2, borderRadius: 2, bgcolor: 'var(--bal-primary-soft)' }}>
          <PriceDisplay price={irr} priceUnit={priceUnit} sessionCount={sessionCount} showEstimate />
          {!sessionCount ? (
            <Typography variant="caption" sx={{ color: 'text.secondary', display: 'block', mt: 0.5 }}>
              {t('rate_note')}
            </Typography>
          ) : null}
        </Paper>
      ) : null}

      <TextField
        label={t('display_name_label')}
        value={displayValue}
        onChange={(event) => setDisplayNameOverride(event.target.value)}
        helperText={t('display_name_hint')}
        fullWidth
      />

      {duplicate ? (
        <Paper
          elevation={0}
          sx={{
            p: 1.5,
            borderRadius: 2,
            border: '1px solid',
            borderColor: 'divider',
            borderInlineStartWidth: 4,
            borderInlineStartColor: 'var(--bal-warning)',
          }}
        >
          <Typography variant="body2" sx={{ color: 'var(--bal-warning)', fontWeight: 500 }}>
            {t('duplicate_warning')}
          </Typography>
        </Paper>
      ) : null}
    </Stack>
  );

  // --- Edit mode: locked category + options summary, then the price form ---
  if (isEdit) {
    return (
      <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3, maxWidth: 560 }}>
        <Typography variant="h5" component="h1">
          {t('builder_edit_title')}
        </Typography>

        <Paper elevation={0} sx={{ p: 2, borderRadius: 2, border: '1px solid', borderColor: 'divider' }}>
          <Stack sx={{ gap: 1 }}>
            <Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
              {pickCatalogName({ nameFa: initial.categoryNameFa, nameEn: initial.categoryNameEn }, locale)}
            </Typography>
            <Typography variant="caption" sx={{ color: 'text.secondary' }}>
              {t('category_locked')}
            </Typography>
            <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.75, mt: 0.5 }}>
              {initial.options.length > 0 ? (
                initial.options.map((option) => (
                  <Chip
                    key={option.optionGroupId}
                    size="small"
                    label={`${pickCatalogName({ nameFa: option.groupNameFa, nameEn: option.groupNameEn }, locale)}: ${pickCatalogName({ nameFa: option.valueNameFa, nameEn: option.valueNameEn }, locale)}`}
                    sx={{ bgcolor: 'var(--bal-primary-soft)' }}
                  />
                ))
              ) : (
                <Typography variant="caption" sx={{ color: 'text.secondary' }}>
                  {t('summary_none')}
                </Typography>
              )}
            </Box>
          </Stack>
        </Paper>

        {priceStep}

        <Stack direction="row" sx={{ gap: 1, justifyContent: 'flex-end' }}>
          <AppButton variant="text" onClick={onCancel} disabled={submitting}>
            {tc('cancel')}
          </AppButton>
          <AppButton color="primary" variant="contained" onClick={submit} disabled={submitting}>
            {submitting ? tc('saving') : t('submit_save')}
          </AppButton>
        </Stack>
      </Box>
    );
  }

  // --- Create mode: the 3-step stepper ---
  return (
    <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, maxWidth: 560 }}>
      <Typography variant="h5" component="h1">
        {t('builder_add_title')}
      </Typography>

      <StepperHeader
        steps={[t('step_category'), t('step_options'), t('step_price')]}
        activeStep={activeStep}
      />

      {activeStep === 0 ? (
        <Stack sx={{ gap: 1.5 }}>
          <Box>
            <Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
              {t('category_title')}
            </Typography>
            <Typography variant="body2" sx={{ color: 'text.secondary' }}>
              {t('category_subtitle')}
            </Typography>
          </Box>
          {categoriesQuery.isLoading ? (
            <Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 1.5 }}>
              {[0, 1, 2, 3].map((key) => (
                <Skeleton key={key} variant="rounded" height={116} sx={{ borderRadius: 2 }} />
              ))}
            </Box>
          ) : categoriesQuery.isError ? (
            <Stack sx={{ gap: 1, alignItems: 'flex-start' }}>
              <Typography variant="body2" sx={{ color: 'text.secondary' }}>
                {t('categories_error')}
              </Typography>
              <AppButton variant="outlined" color="primary" onClick={() => categoriesQuery.refetch()}>
                {tc('retry')}
              </AppButton>
            </Stack>
          ) : (
            <Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 1.5 }}>
              {categories.map((category) => (
                <CategoryTile
                  key={category.id}
                  label={pickCatalogName(category, locale)}
                  iconKey={category.iconKey}
                  selected={categoryId === category.id}
                  onClick={() => selectCategory(category.id)}
                />
              ))}
            </Box>
          )}
        </Stack>
      ) : null}

      {activeStep === 1 ? (
        <Stack sx={{ gap: 2 }}>
          <Box>
            <Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
              {t('options_title')}
            </Typography>
            <Typography variant="body2" sx={{ color: 'text.secondary' }}>
              {t('options_subtitle')}
            </Typography>
          </Box>

          {optionGroupsQuery.isLoading ? (
            <AppLoading />
          ) : optionGroupsQuery.isError ? (
            // A failed fetch must never read as "this category has zero options" — that would let the
            // nurse skip required options entirely. Block progression until the retry succeeds.
            <ErrorState
              message={t('options_error')}
              retryLabel={tc('retry')}
              onRetry={() => optionGroupsQuery.refetch()}
            />
          ) : groups.length === 0 ? (
            <Typography variant="body2" sx={{ color: 'text.secondary' }}>
              {t('options_none')}
            </Typography>
          ) : (
            groups.map((group) => {
              const isMissing = optionsError && group.isRequired && selectedOptions[group.id] == null;
              return (
                <Stack key={group.id} sx={{ gap: 1 }}>
                  <Stack direction="row" sx={{ gap: 1, alignItems: 'center' }}>
                    <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
                      {pickCatalogName(group, locale)}
                    </Typography>
                    {/* The required badge turns red on a blocked advance to point at the unanswered group. */}
                    <Chip
                      size="small"
                      label={group.isRequired ? t('required_badge') : t('optional_badge')}
                      sx={{
                        bgcolor: isMissing
                          ? 'var(--bal-error)'
                          : group.isRequired
                            ? 'var(--bal-primary-soft)'
                            : 'var(--bal-divider)',
                        color: isMissing
                          ? 'var(--bal-error-contrast)'
                          : group.isRequired
                            ? 'var(--bal-primary)'
                            : 'text.secondary',
                        fontWeight: 500,
                      }}
                    />
                  </Stack>
                  <ToggleButtonGroup
                    exclusive
                    size="small"
                    color="primary"
                    value={selectedOptions[group.id] ?? null}
                    onChange={(_event, valueId: number | null) => changeOption(group.id, valueId)}
                    sx={{ flexWrap: 'wrap' }}
                  >
                    {group.values.map((value) => (
                      <ToggleButton key={value.id} value={value.id}>
                        {pickCatalogName(value, locale)}
                      </ToggleButton>
                    ))}
                  </ToggleButtonGroup>
                </Stack>
              );
            })
          )}

          {optionsError && missingRequiredGroups.length > 0 ? (
            <Typography variant="body2" sx={{ color: 'var(--bal-error)', fontWeight: 500 }}>
              {t('options_incomplete')}
            </Typography>
          ) : null}
        </Stack>
      ) : null}

      {activeStep === 2 ? (
        <Stack sx={{ gap: 1.5 }}>
          <Box>
            <Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
              {t('price_title')}
            </Typography>
          </Box>
          {priceStep}
        </Stack>
      ) : null}

      <Stack direction="row" sx={{ gap: 1, justifyContent: 'space-between', mt: 1 }}>
        <AppButton
          variant="text"
          onClick={activeStep === 0 ? onCancel : () => setActiveStep((step) => step - 1)}
          disabled={submitting}
        >
          {activeStep === 0 ? tc('cancel') : tc('back')}
        </AppButton>

        {activeStep === 0 ? (
          <AppButton
            color="primary"
            variant="contained"
            onClick={() => setActiveStep(1)}
            disabled={categoryId == null}
          >
            {t('next')}
          </AppButton>
        ) : activeStep === 1 ? (
          <AppButton
            color="primary"
            variant="contained"
            onClick={goNextFromOptions}
            disabled={optionGroupsQuery.isError}
          >
            {t('next')}
          </AppButton>
        ) : (
          <AppButton color="primary" variant="contained" onClick={submit} disabled={submitting}>
            {submitting ? tc('saving') : t('submit_create')}
          </AppButton>
        )}
      </Stack>
    </Box>
  );
};

export default VariantBuilder;
