'use client';
import { Suspense, useMemo, useState } from 'react';
import { useLocale, useTranslations } from 'next-intl';
import { useRouter, useSearchParams } from 'next/navigation';
import {
  Box,
  MenuItem,
  Paper,
  Skeleton,
  Stack,
  TextField,
  ToggleButton,
  ToggleButtonGroup,
  Typography,
} from '@mui/material';
import { AppButton, AppLoading, EmptyState, PriceDisplay } from '@/components';
import { AddressMapPicker } from '@/components/geography';
import { ROUTES } from '@/constants';
import { ApiError } from '@/lib/api/errors';
import { cityCentroid } from '@/services/geography/constants';
import { usePatients } from '@/services/patients';
import { useAddresses } from '@/services/addresses';
import { useNurseProfile } from '@/services/search';
import { useCreateBookingRequest } from '@/services/bookingRequests';
import { CUSTOMER_NOTES_MAX_LENGTH } from '@/services/bookingRequests/constants';
import type {
  BookingRequestDisplayContext,
  RequiredCaregiverGender,
} from '@/services/bookingRequests/types';

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

/**
 * 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 tAddress = useTranslations('address');
  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');

  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 | ''>('');
  const [variantSel, setVariantSel] = useState<number | ''>(variantIdParam ?? '');
  const [addressSel, setAddressSel] = useState<number | ''>('');
  const [gender, setGender] = useState<RequiredCaregiverGender | ''>(
    genderParam === 'male' || genderParam === 'female' ? genderParam : '',
  );
  const [date, setDate] = useState('');
  const [timeStart, setTimeStart] = useState('09:00');
  const [timeEnd, setTimeEnd] = useState('13:00');
  const [notes, setNotes] = useState('');
  const [attempted, setAttempted] = useState(false);
  const [pastDateError, setPastDateError] = useState(false);
  const [formError, setFormError] = useState<string | null>(null);

  // 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 = (): string => {
    if (!selectedAddress) return '';
    const city = locale === 'en' ? selectedAddress.cityNameEn : selectedAddress.cityNameFa;
    const district =
      selectedAddress.districtId == null
        ? t('address_whole_city')
        : locale === 'en'
          ? selectedAddress.districtNameEn
          : selectedAddress.districtNameFa;
    return `${selectedAddress.title} · ${city} · ${district}`;
  };

  const handleSubmit = () => {
    setAttempted(true);
    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 = attempted && timeStart !== '' && timeEnd !== '' && timeEnd <= timeStart;
  const pastError = pastDateError;

  return (
    <Stack sx={{ gap: 3 }}>
      <Box>
        <Typography variant="h5" component="h1">
          {t('request_title')}
        </Typography>
        <Typography variant="body2" sx={{ color: 'text.secondary' }}>
          {t('form_subtitle')}
        </Typography>
      </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={attempted && patientId === ''}
          helperText={attempted && patientId === '' ? t('error_patient_required') : undefined}
          onChange={(event) => setPatientId(Number(event.target.value))}
          fullWidth
        >
          <MenuItem value="" disabled>
            {t('patient_placeholder')}
          </MenuItem>
          {patients.map((patient) => (
            <MenuItem key={patient.id} value={patient.id}>
              {patient.displayName}
            </MenuItem>
          ))}
        </TextField>
      )}

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

      {/* Address */}
      {addresses.length === 0 ? (
        <FieldEmpty
          label={t('address_label')}
          message={t('address_empty')}
          ctaLabel={t('address_add_cta')}
          onCta={() => router.push(`/${locale}${ROUTES.ADDRESSES}`)}
        />
      ) : (
        <Stack sx={{ gap: 1 }}>
          <TextField
            select
            label={t('address_label')}
            value={addressId}
            error={attempted && addressId === ''}
            helperText={attempted && addressId === '' ? t('error_address_required') : undefined}
            onChange={(event) => setAddressSel(Number(event.target.value))}
            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>
          {selectedAddress ? (
            <Paper elevation={0} sx={{ p: 1.5, border: '1px solid', borderColor: 'divider', borderRadius: 2 }}>
              <Typography variant="body2" sx={{ color: 'text.secondary', mb: 1 }}>
                {regionLabel()}
                {selectedAddress.addressLine ? ` — ${selectedAddress.addressLine}` : ''}
              </Typography>
              {selectedAddress.latitude != null && selectedAddress.longitude != null ? (
                // Read-only preview of the address's stored pin (the pin itself is set in the f3 book).
                <Box sx={{ pointerEvents: 'none' }}>
                  <AddressMapPicker
                    value={{ latitude: selectedAddress.latitude, longitude: selectedAddress.longitude }}
                    onChange={() => undefined}
                    center={cityCentroid(selectedAddress.cityId)}
                    helperText={regionLabel()}
                    latLabel={tAddress('map_lat')}
                    lngLabel={tAddress('map_lng')}
                  />
                </Box>
              ) : null}
            </Paper>
          ) : null}
        </Stack>
      )}

      {/* Date + time */}
      <Stack direction={{ xs: 'column', sm: 'row' }} sx={{ gap: 2 }}>
        <TextField
          type="date"
          label={t('date_label')}
          value={date}
          error={(attempted && date === '') || pastError}
          helperText={pastError ? t('error_past_date') : attempted && date === '' ? t('error_date_required') : undefined}
          onChange={(event) => {
            setDate(event.target.value);
            if (pastDateError) setPastDateError(false);
          }}
          slotProps={{ inputLabel: { shrink: true } }}
          fullWidth
        />
        <TextField
          type="time"
          label={t('time_start_label')}
          value={timeStart}
          onChange={(event) => {
            setTimeStart(event.target.value);
            if (pastDateError) setPastDateError(false);
          }}
          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>

      {/* Caregiver gender — first-class, three-way, never silently defaulted */}
      <Stack sx={{ gap: 1 }}>
        <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: attempted && 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>
        {attempted && 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 */}
      <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', mt: -2 }}>
        {t('notes_counter', { count: notes.length, max: CUSTOMER_NOTES_MAX_LENGTH })}
      </Typography>

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

      <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>
    </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="text" width="50%" height={36} />
      {[0, 1, 2, 3].map((key) => (
        <Skeleton key={key} variant="rounded" height={56} />
      ))}
      <Skeleton variant="rounded" height={96} />
    </Stack>
  );
}
