'use client';
import { Suspense, useMemo, useState } from 'react';
import { useLocale, useTranslations } from 'next-intl';
import { useRouter, useSearchParams } from 'next/navigation';
import {
  Avatar,
  Box,
  Chip,
  MenuItem,
  Skeleton,
  Stack,
  TextField,
  ToggleButton,
  ToggleButtonGroup,
  Typography,
} from '@mui/material';
import {
  AppButton,
  AppIcon,
  AppLoading,
  EmptyState,
  JalaliDateIntentPicker,
  PriceDisplay,
  StepperHeader,
  TrustBadge,
} from '@/components';
import { todayIso } from '@/components/common/JalaliDatePicker';
import { ROUTES } from '@/constants';
import { ApiError } from '@/lib/api/errors';
import { usePatients } from '@/services/patients';
import { useAddresses } from '@/services/addresses';
import { useNurseProfile } from '@/services/search';
import type { NurseProfile } from '@/services/search/types';
import { useCreateBookingRequest } from '@/services/bookingRequests';
import { CUSTOMER_NOTES_MAX_LENGTH } from '@/services/bookingRequests/constants';
import { formatNumber } from '@/utils';
import type {
  BookingRequestDisplayContext,
  RequiredCaregiverGender,
} from '@/services/bookingRequests/types';
import type { CustomerAddress } from '@/services/addresses/types';

const GENDER_OPTIONS: RequiredCaregiverGender[] = ['female', 'male', 'any'];

interface TimeWindowOption {
  key: 'morning' | 'afternoon' | 'evening';
  start: string;
  end: string;
}

const TIME_WINDOWS: TimeWindowOption[] = [
  { key: 'morning', start: '08:00', end: '12:00' },
  { key: 'afternoon', start: '12:00', end: '16:00' },
  { key: 'evening', start: '16:00', end: '20:00' },
];

type TouchedField = 'patient' | 'service' | 'address' | 'date' | 'time' | 'gender';

/**
 * C4 — Booking-request form (فرم درخواست). The destination of the C3 "درخواست رزرو" CTA (it carries the
 * `nurse_id`, an optional `variant_id`, and the same-gender `required_gender` intent from search). The
 * family picks a patient (f2), one of the nurse's service variants (f4/search profile), a saved address
 * (f3), a future date + time window, the **first-class caregiver-gender** preference, and stage-1 notes,
 * then sends the request → lands on C5. Money-free: no price breakdown, no booking row (that's f9/b9).
 * `useSearchParams` requires a Suspense boundary.
 */
export default function BookingRequestFormPage() {
  return (
    <Suspense fallback={<AppLoading />}>
      <BookingRequestForm />
    </Suspense>
  );
}

function BookingRequestForm() {
  const t = useTranslations('booking');
  const locale = useLocale();
  const router = useRouter();
  const query = useSearchParams();

  const nurseId = Number(query.get('nurse_id'));
  const hasNurse = Number.isInteger(nurseId) && nurseId > 0;
  const variantIdParam = Number(query.get('variant_id')) || null;
  const genderParam = query.get('required_gender');
  // Recovery hand-off from C5's "request again with another time" — reopens this same nurse/variant
  // prefilled with the patient + address of the terminal request, extending the C3 handoff params.
  const patientIdParam = Number(query.get('patient_id')) || null;
  const addressIdParam = Number(query.get('address_id')) || null;

  const profileQuery = useNurseProfile(hasNurse ? nurseId : undefined);
  const patientsQuery = usePatients();
  const addressesQuery = useAddresses();
  const createRequest = useCreateBookingRequest();

  const profile = profileQuery.data;
  const patients = useMemo(() => patientsQuery.data?.items ?? [], [patientsQuery.data]);
  const addresses = useMemo(() => addressesQuery.data?.items ?? [], [addressesQuery.data]);
  const services = useMemo(() => profile?.services ?? [], [profile]);

  const [patientId, setPatientId] = useState<number | ''>(patientIdParam ?? '');
  const [variantSel, setVariantSel] = useState<number | ''>(variantIdParam ?? '');
  const [addressSel, setAddressSel] = useState<number | ''>(addressIdParam ?? '');
  const [addressEditing, setAddressEditing] = useState(false);
  const [gender, setGender] = useState<RequiredCaregiverGender | ''>(
    genderParam === 'male' || genderParam === 'female' ? genderParam : '',
  );
  const [date, setDate] = useState('');
  const [windowSel, setWindowSel] = useState<TimeWindowOption['key'] | 'custom' | null>(null);
  const [timeStart, setTimeStart] = useState('');
  const [timeEnd, setTimeEnd] = useState('');
  const [notes, setNotes] = useState('');
  const [touched, setTouched] = useState<Record<TouchedField, boolean>>({
    patient: false,
    service: false,
    address: false,
    date: false,
    time: false,
    gender: false,
  });
  const [pastDateError, setPastDateError] = useState(false);
  const [formError, setFormError] = useState<string | null>(null);

  const markTouched = (field: TouchedField) =>
    setTouched((prev) => (prev[field] ? prev : { ...prev, [field]: true }));

  // Effective selection = the user's explicit choice, else a sensible default derived from the loaded
  // data. Computed during render (no setState-in-effect): the variant defaults to the carried one / the
  // first offered, the address to the primary / first.
  const firstVariantId: number | '' = services.length > 0 ? services[0].variantId : '';
  const variantId = variantSel !== '' ? variantSel : firstVariantId;
  const primaryAddressId: number | '' =
    addresses.length > 0 ? (addresses.find((address) => address.isPrimary)?.id ?? addresses[0].id) : '';
  const addressId = addressSel !== '' ? addressSel : primaryAddressId;

  const selectedVariant = useMemo(
    () => services.find((service) => service.variantId === variantId),
    [services, variantId],
  );
  const selectedAddress = useMemo(
    () => addresses.find((address) => address.id === addressId),
    [addresses, addressId],
  );
  const selectedPatient = useMemo(
    () => patients.find((patient) => patient.id === patientId),
    [patients, patientId],
  );

  // A concrete gender that contradicts the (single) nurse's gender is a same-gender mismatch (400) —
  // block it inline before the round-trip; the server re-validates and is authoritative.
  const genderMismatch =
    gender !== '' && gender !== 'any' && profile != null && gender !== profile.nurseGender;

  const requiredChosen =
    patientId !== '' && variantId !== '' && addressId !== '' && gender !== '' && date !== '' && timeStart !== '' && timeEnd !== '';

  const regionLabel = (address: CustomerAddress): string => {
    const city = locale === 'en' ? address.cityNameEn : address.cityNameFa;
    const district =
      address.districtId == null
        ? t('address_whole_city')
        : locale === 'en'
          ? address.districtNameEn
          : address.districtNameFa;
    return `${address.title} · ${city} · ${district}`;
  };

  const selectWindow = (option: TimeWindowOption) => {
    setWindowSel(option.key);
    setTimeStart(option.start);
    setTimeEnd(option.end);
    if (pastDateError) setPastDateError(false);
  };

  const missingFieldLabels: string[] = [];
  if (patientId === '') missingFieldLabels.push(t('cta_missing_patient'));
  if (variantId === '') missingFieldLabels.push(t('cta_missing_service'));
  if (addressId === '') missingFieldLabels.push(t('cta_missing_address'));
  if (date === '') missingFieldLabels.push(t('cta_missing_date'));
  if (timeStart === '' || timeEnd === '') missingFieldLabels.push(t('cta_missing_time'));
  if (gender === '') missingFieldLabels.push(t('cta_missing_gender'));

  const handleSubmit = () => {
    setFormError(null);
    if (!requiredChosen) return;
    if (timeEnd <= timeStart) return;
    // Future date+time guard (local wall-clock, matching the wire's date + time fields). Evaluated in the
    // handler (not render) so the render path stays pure; the result drives the inline date error.
    if (Date.parse(`${date}T${timeStart}`) < Date.now()) {
      setPastDateError(true);
      return;
    }
    setPastDateError(false);
    if (genderMismatch) return;

    const context: BookingRequestDisplayContext | undefined =
      profile && selectedVariant && selectedAddress && selectedPatient
        ? {
            nurseName: profile.nurseName,
            nurseRating: profile.averageRating,
            nurseTotalReviews: profile.totalReviews,
            patientName: selectedPatient.displayName,
            variantLabel: selectedVariant.displayName,
            variantPriceUnit: selectedVariant.priceUnit,
            variantPrice: selectedVariant.priceIrr,
            addressTitle: selectedAddress.title,
            cityId: selectedAddress.cityId,
            cityNameFa: selectedAddress.cityNameFa,
            cityNameEn: selectedAddress.cityNameEn,
            districtId: selectedAddress.districtId,
            districtNameFa: selectedAddress.districtNameFa,
            districtNameEn: selectedAddress.districtNameEn,
            addressLine: selectedAddress.addressLine,
            postalCode: selectedAddress.postalCode,
            recipientName: selectedAddress.recipientName,
            recipientPhone: selectedAddress.recipientPhone,
          }
        : undefined;

    createRequest.mutate(
      {
        payload: {
          nurseId,
          variantId: variantId as number,
          patientId: patientId as number,
          customerAddressId: addressId as number,
          requestedDate: date,
          requestedTimeStart: `${timeStart}:00`,
          requestedTimeEnd: `${timeEnd}:00`,
          requiredCaregiverGender: gender as RequiredCaregiverGender,
          customerNotes: notes.trim() || null,
        },
        context,
      },
      {
        onSuccess: (dto) => {
          router.push(`/${locale}${ROUTES.BOOKING_REQUEST_STATUS}/${dto.id}`);
        },
        onError: (error) => setFormError(mapCreateError(error, t)),
      },
    );
  };

  if (!hasNurse) {
    return (
      <EmptyState
        icon="search"
        title={t('missing_nurse_title')}
        body={t('missing_nurse_body')}
        action={
          <AppButton
            variant="contained"
            color="primary"
            onClick={() => router.push(`/${locale}${ROUTES.SEARCH}`)}
            sx={{ m: 0 }}
          >
            {t('missing_nurse_cta')}
          </AppButton>
        }
      />
    );
  }

  if (profileQuery.isLoading) return <FormSkeleton />;

  const timeError = touched.time && timeStart !== '' && timeEnd !== '' && timeEnd <= timeStart;
  const pastError = pastDateError;

  return (
    <Stack sx={{ gap: 3 }}>
      {profile ? <NurseIdentityBar profile={profile} /> : null}

      <Box>
        <Typography variant="h5" component="h1">
          {t('request_title')}
        </Typography>
        <Typography variant="body2" sx={{ color: 'text.secondary' }}>
          {t('form_subtitle')}
        </Typography>
      </Box>

      <Box>
        <Typography variant="subtitle2" sx={{ fontWeight: 700, mb: 0.5 }}>
          {t('whathappens_title')}
        </Typography>
        <StepperHeader steps={[t('step_submitted'), t('step_awaiting'), t('step_payment')]} activeStep={0} />
      </Box>

      {/* Patient */}
      {patients.length === 0 ? (
        <FieldEmpty
          label={t('patient_label')}
          message={t('patient_empty')}
          ctaLabel={t('patient_add_cta')}
          onCta={() => router.push(`/${locale}${ROUTES.PATIENTS}`)}
        />
      ) : (
        <TextField
          select
          label={t('patient_label')}
          value={patientId}
          error={touched.patient && patientId === ''}
          helperText={touched.patient && patientId === '' ? t('error_patient_required') : undefined}
          onChange={(event) => setPatientId(Number(event.target.value))}
          onBlur={() => markTouched('patient')}
          fullWidth
        >
          <MenuItem value="" disabled>
            {t('patient_placeholder')}
          </MenuItem>
          {patients.map((patient) => (
            <MenuItem key={patient.id} value={patient.id}>
              {patient.displayName}
            </MenuItem>
          ))}
        </TextField>
      )}

      {/* Service variant */}
      <Stack sx={{ gap: 1 }}>
        {services.length === 0 ? (
          <Typography variant="body2" sx={{ color: 'text.secondary' }}>
            {t('service_empty')}
          </Typography>
        ) : (
          <TextField
            select
            label={t('service_label')}
            value={variantId}
            error={touched.service && variantId === ''}
            helperText={touched.service && variantId === '' ? t('error_service_required') : undefined}
            onChange={(event) => setVariantSel(Number(event.target.value))}
            onBlur={() => markTouched('service')}
            fullWidth
          >
            <MenuItem value="" disabled>
              {t('service_placeholder')}
            </MenuItem>
            {services.map((service) => (
              <MenuItem key={service.variantId} value={service.variantId}>
                {service.displayName}
              </MenuItem>
            ))}
          </TextField>
        )}
        {selectedVariant ? (
          <PriceDisplay
            price={selectedVariant.priceIrr}
            priceUnit={selectedVariant.priceUnit}
            sessionCount={selectedVariant.sessionCount}
          />
        ) : null}
      </Stack>

      {/* Address — a compact confirmation row once resolved, with a way back to the select. */}
      {addresses.length === 0 ? (
        <FieldEmpty
          label={t('address_label')}
          message={t('address_empty')}
          ctaLabel={t('address_add_cta')}
          onCta={() => router.push(`/${locale}${ROUTES.ADDRESSES}`)}
        />
      ) : addressEditing || !selectedAddress ? (
        <TextField
          select
          label={t('address_label')}
          value={addressId}
          error={touched.address && addressId === ''}
          helperText={touched.address && addressId === '' ? t('error_address_required') : undefined}
          onChange={(event) => {
            setAddressSel(Number(event.target.value));
            setAddressEditing(false);
          }}
          onBlur={() => markTouched('address')}
          fullWidth
        >
          <MenuItem value="" disabled>
            {t('address_placeholder')}
          </MenuItem>
          {addresses.map((address) => (
            <MenuItem key={address.id} value={address.id}>
              {address.title} · {locale === 'en' ? address.cityNameEn : address.cityNameFa}
            </MenuItem>
          ))}
        </TextField>
      ) : (
        <Stack
          direction="row"
          sx={{
            gap: 1.5,
            alignItems: 'center',
            p: 1.5,
            border: '1px solid',
            borderColor: 'divider',
            borderRadius: 'var(--bal-radius-md)',
          }}
        >
          <AppIcon icon="location" size={20} color="var(--bal-text-secondary)" />
          <Stack sx={{ gap: 0.25, minWidth: 0, flexGrow: 1 }}>
            <Typography variant="body2" sx={{ fontWeight: 500 }} noWrap>
              {regionLabel(selectedAddress)}
            </Typography>
            {selectedAddress.addressLine ? (
              <Typography variant="caption" sx={{ color: 'text.secondary' }} noWrap>
                {selectedAddress.addressLine}
              </Typography>
            ) : null}
          </Stack>
          <AppButton variant="text" size="small" onClick={() => setAddressEditing(true)} sx={{ flexShrink: 0 }}>
            {t('address_change_cta')}
          </AppButton>
        </Stack>
      )}

      {/* Date */}
      <Stack sx={{ gap: 1 }} onBlur={() => markTouched('date')}>
        <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
          {t('date_label')}
        </Typography>
        <JalaliDateIntentPicker
          value={date}
          onChange={(iso) => {
            setDate(iso);
            if (pastDateError) setPastDateError(false);
          }}
          min={todayIso()}
          todayLabel={t('date_today')}
          tomorrowLabel={t('date_tomorrow')}
          pickOtherLabel={t('date_pick_other')}
        />
        {pastError ? (
          <Typography variant="caption" sx={{ color: 'var(--bal-error)' }}>
            {t('error_past_date')}
          </Typography>
        ) : touched.date && date === '' ? (
          <Typography variant="caption" sx={{ color: 'var(--bal-error)' }}>
            {t('error_date_required')}
          </Typography>
        ) : null}
      </Stack>

      {/* Time window — presets kill the end<=start error class; «زمان دلخواه» reveals free time fields. */}
      <Stack sx={{ gap: 1 }} onBlur={() => markTouched('time')}>
        <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
          {t('time_window_label')}
        </Typography>
        <Stack direction="row" sx={{ gap: 1, flexWrap: 'wrap' }}>
          {TIME_WINDOWS.map((option) => (
            <Chip
              key={option.key}
              clickable
              label={t(`window_${option.key}`)}
              onClick={() => selectWindow(option)}
              color={windowSel === option.key ? 'primary' : undefined}
              variant={windowSel === option.key ? 'filled' : 'outlined'}
              data-window={option.key}
            />
          ))}
          <Chip
            clickable
            label={t('window_custom')}
            onClick={() => setWindowSel('custom')}
            color={windowSel === 'custom' ? 'primary' : undefined}
            variant={windowSel === 'custom' ? 'filled' : 'outlined'}
            data-window="custom"
          />
        </Stack>
        {windowSel === 'custom' ? (
          <Stack direction={{ xs: 'column', sm: 'row' }} sx={{ gap: 2 }}>
            <TextField
              type="time"
              label={t('time_start_label')}
              value={timeStart}
              onChange={(event) => setTimeStart(event.target.value)}
              slotProps={{ inputLabel: { shrink: true } }}
              fullWidth
            />
            <TextField
              type="time"
              label={t('time_end_label')}
              value={timeEnd}
              error={timeError}
              helperText={timeError ? t('error_time_range') : undefined}
              onChange={(event) => setTimeEnd(event.target.value)}
              slotProps={{ inputLabel: { shrink: true } }}
              fullWidth
            />
          </Stack>
        ) : null}
        {touched.time && (timeStart === '' || timeEnd === '') ? (
          <Typography variant="caption" sx={{ color: 'var(--bal-error)' }}>
            {t('error_time_required')}
          </Typography>
        ) : null}
      </Stack>

      {/* Caregiver gender — first-class, three-way, never silently defaulted */}
      <Stack sx={{ gap: 1 }} onBlur={() => markTouched('gender')}>
        <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
          {t('gender_label')}
        </Typography>
        <ToggleButtonGroup
          exclusive
          color="primary"
          value={gender || null}
          onChange={(_event, next: RequiredCaregiverGender | null) => {
            if (next) setGender(next);
          }}
          sx={{
            '& .MuiToggleButton-root': {
              flex: 1,
              py: 1.25,
              fontWeight: 700,
              borderColor: touched.gender && gender === '' ? 'var(--bal-error)' : undefined,
            },
          }}
        >
          {GENDER_OPTIONS.map((option) => (
            <ToggleButton key={option} value={option} data-gender={option}>
              {t(`gender_${option}`)}
            </ToggleButton>
          ))}
        </ToggleButtonGroup>
        <Typography variant="caption" sx={{ color: 'text.secondary' }}>
          {t('gender_hint')}
        </Typography>
        {touched.gender && gender === '' ? (
          <Typography variant="caption" sx={{ color: 'var(--bal-error)' }}>
            {t('error_gender_required')}
          </Typography>
        ) : null}
        {genderMismatch ? (
          <Typography variant="caption" sx={{ color: 'var(--bal-error)' }}>
            {t('error_gender_mismatch')}
          </Typography>
        ) : null}
      </Stack>

      {/* Stage-1 notes */}
      <Stack sx={{ gap: 0.5 }}>
        <TextField
          label={t('notes_label')}
          placeholder={t('notes_placeholder')}
          value={notes}
          onChange={(event) => setNotes(event.target.value.slice(0, CUSTOMER_NOTES_MAX_LENGTH))}
          multiline
          minRows={3}
          fullWidth
          helperText={t('notes_hint')}
        />
        <Typography variant="caption" sx={{ color: 'text.secondary', textAlign: 'end' }}>
          {t('notes_counter', { count: notes.length, max: CUSTOMER_NOTES_MAX_LENGTH })}
        </Typography>
      </Stack>

      {formError ? (
        <Typography variant="body2" sx={{ color: 'var(--bal-error)' }}>
          {formError}
        </Typography>
      ) : null}

      <Stack sx={{ gap: 1 }}>
        <AppButton
          color="primary"
          variant="contained"
          size="large"
          startIcon="requests"
          disabled={!requiredChosen || genderMismatch || createRequest.isPending}
          onClick={handleSubmit}
          sx={{ py: 1.5 }}
        >
          {createRequest.isPending ? t('submitting') : t('submit')}
        </AppButton>
        {!requiredChosen && missingFieldLabels.length > 0 ? (
          <Typography variant="caption" sx={{ color: 'text.secondary', textAlign: 'center' }}>
            {t('cta_missing_caption', { fields: missingFieldLabels.join(locale === 'fa' ? '، ' : ', ') })}
          </Typography>
        ) : null}
      </Stack>
    </Stack>
  );
}

/** The sticky "who you're inviting home" identity summary — avatar, name, rating, trust badge, gender. */
function NurseIdentityBar({ profile }: { profile: NurseProfile }) {
  const t = useTranslations('booking');
  const locale = useLocale();
  const name = profile.nurseName.trim() || t('unnamed_nurse');
  const ratingLabel = formatNumber(profile.averageRating, locale, {
    minimumFractionDigits: 1,
    maximumFractionDigits: 1,
  });

  return (
    <Stack
      direction="row"
      data-nurse-identity-bar
      sx={{
        position: 'sticky',
        top: 0,
        zIndex: 2,
        gap: 1.5,
        alignItems: 'center',
        p: 1.5,
        border: '1px solid',
        borderColor: 'divider',
        borderRadius: 'var(--bal-radius-md)',
        bgcolor: 'background.paper',
      }}
    >
      <Avatar
        src={profile.avatarUrl ?? undefined}
        sx={{ width: 44, height: 44, bgcolor: 'var(--bal-primary-soft)', color: 'var(--bal-primary)', fontWeight: 700 }}
      >
        {name.charAt(0)}
      </Avatar>
      <Stack sx={{ gap: 0.25, minWidth: 0, flexGrow: 1 }}>
        <Stack direction="row" sx={{ gap: 1, alignItems: 'center', flexWrap: 'wrap' }}>
          <Typography variant="subtitle1" sx={{ fontWeight: 700 }} noWrap>
            {name}
          </Typography>
          <TrustBadge state={profile.isVerified ? 'verified' : 'unverified'} />
        </Stack>
        <Stack direction="row" sx={{ gap: 1, alignItems: 'center', flexWrap: 'wrap' }}>
          <Stack direction="row" sx={{ gap: 0.5, alignItems: 'center' }}>
            <AppIcon icon="star" size={15} color="var(--bal-rating)" />
            <Typography variant="caption" sx={{ fontWeight: 700 }}>
              {ratingLabel}
            </Typography>
            <Typography variant="caption" sx={{ color: 'text.secondary' }}>
              ({formatNumber(profile.totalReviews, locale)})
            </Typography>
          </Stack>
          <Chip
            size="small"
            variant="outlined"
            label={t(`gender_${profile.nurseGender}`)}
            sx={{ height: 20, fontSize: '0.7rem' }}
          />
        </Stack>
      </Stack>
    </Stack>
  );
}

/** Map a create `ApiError` (domain 400/404 codes) to a translated, user-facing message. */
function mapCreateError(error: unknown, t: (key: string) => string): string {
  if (error instanceof ApiError) {
    switch (error.code) {
      case 'gender_required':
        return t('error_gender_required');
      case 'invalid_time_range':
        return t('error_time_range');
      case 'past_date':
        return t('error_past_date');
      case 'notes_too_long':
        return t('error_notes_long');
      case 'gender_mismatch':
        return t('error_gender_mismatch');
      case 'inactive_variant':
      case 'not_bookable':
        return t('error_not_bookable');
      case 'not_found':
        return t('error_tenancy');
      default:
        break;
    }
    if (error.status === 404) return t('error_tenancy');
  }
  return t('error_generic');
}

function FieldEmpty({
  label,
  message,
  ctaLabel,
  onCta,
}: {
  label: string;
  message: string;
  ctaLabel: string;
  onCta: () => void;
}) {
  return (
    <Stack sx={{ gap: 0.5 }}>
      <Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
        {label}
      </Typography>
      <EmptyState
        title={message}
        action={
          <AppButton variant="outlined" color="primary" startIcon="add" onClick={onCta}>
            {ctaLabel}
          </AppButton>
        }
      />
    </Stack>
  );
}

function FormSkeleton() {
  return (
    <Stack sx={{ gap: 2.5 }}>
      <Skeleton variant="rounded" height={76} />
      <Skeleton variant="text" width="50%" height={36} />
      <Skeleton variant="rounded" height={48} />
      {[0, 1, 2, 3].map((key) => (
        <Skeleton key={key} variant="rounded" height={56} />
      ))}
      <Skeleton variant="rounded" height={96} />
    </Stack>
  );
}
