import React, { useEffect, useState } from 'react';
import type { ReactNode } from 'react';
import { useMutation, useQuery } from '@tanstack/react-query';
import { ChevronDownIcon, ClockIcon, MagnifyingGlassIcon, CheckCircleIcon, CpuChipIcon, ExclamationCircleIcon } from '@heroicons/react/24/outline';
import { toast } from 'sonner';
import { api } from '../../lib/api';
import type { ApiResponse } from '../../lib/api';
import { digitsOnly, sanitizeMobileInput, rialToToman, tomanToRial, formatRial, formatDate } from '../../lib/utils';
import { useAuthStore } from '../../stores/authStore';
import Modal from '../ui/Modal';
import PersianDateInput from '../ui/PersianDateInput';
import PriceInput from '../ui/PriceInput';
import SearchableSelect from '../ui/SearchableSelect';
import ServiceSlotPicker from './ServiceSlotPicker';
import type { ServicePick } from './ServiceSlotPicker';
import type { BookingService } from '../../hooks/useDoctorBookingServices';

export interface BookingSlot {
  start: number;
  end: number;
  start_time: string;
  end_time: string;
  doctor_uuid: string;
  doctor_name: string;
}

type StepKey = 'service' | 'patient' | 'confirm';

const STEP_TITLES: Record<StepKey, string> = {
  service: 'خدمت و زمان',
  patient: 'بیمار',
  confirm: 'تأیید و ثبت',
};

interface PatientLookup { found: boolean; name?: string | null; mobile?: string; national_code?: string | null }

/** منبعِ هدفِ نوبت — دستگاه/اتاق/پرسنلی که نوبت رویش می‌نشیند. */
export interface BookingResource {
  uuid: string;
  name: string;
}

/**
 * ثبت نوبت از روی یک اسلات/منبع: جستجوی بیمار، انتخاب سرویس‌ها و زمان، هزینهٔ ویزیت.
 *
 * سه حالت دارد و هر سه یک فرم‌اند — سه مودال یعنی سه رفتار:
 * - اسلاتی: زمان همان اسلاتِ کلیک‌شده است.
 * - سرویسی (پزشک): زمان از `ServiceSlotPicker` و برنامهٔ هفتگیِ پزشک می‌آید.
 * - منبع: همان انتخابگر، ولی زمان‌ها از تقویم خودِ منبع و ثبت با `resource_uuid`.
 */
export default function NewAppointmentModal({
  slot, onClose, onSuccess, serviceMode = false, services = [], date, clinicUuid = null, resource = null,
  treatmentSessionUuid = null, patient = null,
}: {
  slot: BookingSlot;
  onClose: () => void;
  onSuccess: () => void;
  serviceMode?: boolean;
  services?: BookingService[];
  date?: string;
  /** محل نوبت — بدون آن backend نوبت را به مطب شخصی نسبت می‌دهد. */
  clinicUuid?: string | null;
  resource?: BookingResource | null;
  /**
   * رزروِ صریحِ یک جلسهٔ درمان. بیمار می‌تواند چند دورهٔ باز داشته باشد، پس بدون این،
   * اتصال از روی سرویس حدس زده می‌شود و سرویسِ اشتباه یک پروندهٔ موازی می‌سازد.
   */
  treatmentSessionUuid?: string | null;
  /**
   * بیمارِ از پیش معلوم — وقتی مودال از پروندهٔ خودِ بیمار باز می‌شود.
   *
   * مرحلهٔ جستجو رد می‌شود: کسی که پروندهٔ بیمار را باز کرده نباید همان بیمار را
   * دوباره با کد ملی پیدا کند.
   */
  patient?: { name: string | null; mobile: string; national_code: string | null } | null;
}) {
  const [mobile, setMobile] = useState(patient?.mobile ?? '');
  const [lookup, setLookup] = useState<PatientLookup | null>(
    patient === null ? null : { found: true, name: patient.name, mobile: patient.mobile, national_code: patient.national_code },
  );
  const [patientName, setPatientName] = useState(patient?.name ?? '');
  const [nationalCode, setNationalCode] = useState(patient?.national_code ?? '');
  // معیار جستجوی بیمار: کد ملی (پیش‌فرض) یا موبایل.
  const [searchBy, setSearchBy] = useState<'mobile' | 'national'>('national');
  const [pick, setPick] = useState<ServicePick>({ serviceUuids: [], durations: {}, slot: null });
  // اپراتور اختیاری است: خالی گذاشتنش جلسه را در صفِ مشترکِ پرسنلِ مجاز می‌گذارد،
  // پر کردنش آن را از قبل به یک نفر می‌دهد.
  const [staffUuid, setStaffUuid] = useState('');
  /**
   * تاریخ داخل خودِ مودال قابل ویرایش است.
   *
   * prop فقط نقطهٔ شروع را می‌دهد — تاریخِ کارتِ جلسه یا روزِ انتخاب‌شدهٔ صفحه. ولی
   * وقت پیشنهادی همیشه در دسترس نیست و کاربر باید بتواند همان‌جا روز دیگری بگیرد،
   * بدون بستن فرمی که بیمار و سرویسش را پر کرده.
   */
  const [activeDate, setActiveDate] = useState(date ?? '');
  useEffect(() => { setActiveDate(date ?? ''); }, [date]);
  // پیش‌فرضِ پروتکل فقط تا وقتی اعمال می‌شود که کاربر دست نزده باشد؛ وگرنه انتخاب
  // دستیِ منشی با هر تغییرِ سرویس پاک می‌شد.
  const [staffTouched, setStaffTouched] = useState(false);

  const role = useAuthStore(s => s.primaryRole);
  // نوبتِ منبع فقط مسیر پنل را دارد؛ اندپوینت ادمین `resource_uuid` نمی‌شناسد.
  const createEndpoint = role === 'admin' && resource === null
    ? '/api/v1/admin/appointment'
    : '/api/v1/my/appointment';

  // منبع تقویم خودش را دارد، پس نوبتش همیشه سرویسی است — اسلات ثابتی وجود ندارد
  // که بشود رویش نشست.
  const pickerMode = serviceMode || resource !== null;

  // هزینه ویزیت — الزامی بودن از تنظیمات «الزامی کردن هزینه ویزیت». فیلد UI تومان،
  // API ریالی (visit_price_rials). بدون این مقدار، وقتی فلگ فعال است backend خطای ۴۲۲ می‌دهد.
  //
  // قیمت باید از تنظیمات نوبت‌دهیِ *پزشکِ همین اسلات* بیاید، نه از entity کاربر جاری؛
  // منشی/کلینیک قیمت خودشان را ندارند و فیلد صفر می‌ماند. اگر دسترسی به تنظیمات آن
  // پزشک نبود (۴۰۳)، به تنظیمات خودِ کاربر برمی‌گردیم تا فلگ الزامی‌بودن از دست نرود.
  type Pricing = { data: { free_visit_price_rials: number; require_visit_price: boolean } };
  const doctorPricingQ = useQuery<Pricing>({
    queryKey: ['insurance-pricing', slot.doctor_uuid],
    queryFn: () => api.get(`/api/v1/insurance-pricing?doctor_uuid=${encodeURIComponent(slot.doctor_uuid)}`),
    enabled: !!slot.doctor_uuid,
    retry: false,
  });
  const staffQ = useQuery<ApiResponse<{ uuid: string; full_name: string }[]>>({
    queryKey: ['staff-list'],
    queryFn: () => api.get('/api/v1/staff'),
    enabled: pickerMode,
    staleTime: 60_000,
  });

  /**
   * پرسنلِ مجازِ «طول درمان» همان سرویس.
   *
   * منشی نباید چیزی را دوباره انتخاب کند که در تنظیمات سرویس یک بار تعریف شده؛
   * پیش‌فرض همان است و تغییرش آزاد.
   */
  const primaryService = pick.serviceUuids[0] ?? '';
  const protocolQ = useQuery<ApiResponse<{ staff: { uuid: string; name: string }[] } | null>>({
    queryKey: ['service-protocol', primaryService],
    queryFn: () => api.get(`/api/v1/service-item/${primaryService}/treatment-protocol`),
    enabled: primaryService !== '',
    staleTime: 60_000,
  });

  const protocolStaff = protocolQ.data?.data?.staff ?? [];

  useEffect(() => {
    if (staffTouched || protocolStaff.length === 0) return;

    setStaffUuid(protocolStaff[0].uuid);
  }, [protocolStaff, staffTouched]);

  const selfPricingQ = useQuery<Pricing>({
    queryKey: ['insurance-pricing'],
    queryFn: () => api.get('/api/v1/insurance-pricing'),
    enabled: doctorPricingQ.isError || !slot.doctor_uuid,
  });
  const pricing = (doctorPricingQ.data ?? selfPricingQ.data) as any;
  const freeVisit = pricing?.data?.free_visit_price_rials ?? 0;
  const requireVisit = pricing?.data?.require_visit_price ?? false;
  const pricingLoading = doctorPricingQ.isLoading || selfPricingQ.isLoading;
  const [visitPriceToman, setVisitPriceToman] = useState(0);
  const [visitPriceTouched, setVisitPriceTouched] = useState(false);
  useEffect(() => {
    if (!visitPriceTouched && freeVisit > 0) setVisitPriceToman(rialToToman(freeVisit));
  }, [freeVisit, visitPriceTouched]);
  // هزینه ویزیت اختیاری داخل کلپسِ بسته می‌نشیند؛ وقتی الزامی است کلپس همیشه باز است.
  const [visitPriceOpen, setVisitPriceOpen] = useState(false);
  const visitPriceExpanded = requireVisit || visitPriceOpen;
  const [stepIdx, setStepIdx] = useState(0);

  const mobileValid = /^09\d{9}$/.test(mobile);
  const nationalCodeValid = /^\d{10}$/.test(nationalCode);
  // اعتبار کلید جستجو بسته به معیار انتخاب‌شده.
  const searchValid = searchBy === 'mobile' ? mobileValid : nationalCodeValid;
  const serviceTimingValid = !pickerMode || (pick.serviceUuids.length > 0 && !!pick.slot);
  // یک بیمارِ یافت‌شده که کد ملی دارد، بدون فرم اضافی قابل استفاده است.
  const foundWithNationalCode = !!lookup?.found && !!lookup.national_code;
  const needsDetails = lookup !== null && !foundWithNationalCode; // یافت‌نشده، یا یافت‌شده بدون کد ملی

  const effectiveName = foundWithNationalCode ? (lookup?.name ?? '') : patientName.trim();
  const effectiveNationalCode = foundWithNationalCode ? (lookup?.national_code ?? '') : nationalCode;
  const detailsValid = effectiveName.length >= 2 && effectiveNationalCode.length === 10;
  const visitPriceValid = !requireVisit || visitPriceToman > 0;
  const isValid = mobileValid && (foundWithNationalCode || (needsDetails && detailsValid)) && serviceTimingValid && visitPriceValid;

  const search = useMutation({
    mutationFn: () => {
      const q = searchBy === 'mobile'
        ? `mobile=${encodeURIComponent(mobile)}`
        : `national_code=${encodeURIComponent(nationalCode)}`;
      return api.get(`/api/v1/my/appointment/patient-lookup?${q}`);
    },
    onSuccess: (res: any) => {
      const data: PatientLookup = res?.data ?? { found: false };
      setLookup(data);
      setPatientName(data.found ? (data.name ?? '') : '');
      // موبایل و کد ملیِ بیمارِ یافت‌شده را پر می‌کنیم تا ثبت مستقل از معیار جستجو کار کند.
      if (data.found) {
        if (data.mobile) setMobile(data.mobile);
        setNationalCode(data.national_code ?? '');
      } else if (searchBy === 'mobile') {
        setNationalCode('');
      }
    },
    onError: (e: any) => toast.error(e?.response?.data?.errors?.[0]?.message ?? e?.message ?? 'خطا در جستجو'),
  });

  const mutation = useMutation({
    mutationFn: () => api.post(createEndpoint, {
      ...(slot.doctor_uuid ? { doctor_uuid: slot.doctor_uuid } : {}),
      slot_start: pickerMode ? pick.slot!.start : slot.start,
      slot_end: pickerMode ? pick.slot!.end : slot.end,
      patient_mobile: mobile,
      patient_name: effectiveName,
      patient_national_code: effectiveNationalCode,
      ...(clinicUuid ? { clinic_uuid: clinicUuid } : {}),
      ...(staffUuid ? { staff_uuid: staffUuid } : {}),
      ...(treatmentSessionUuid ? { treatment_session_uuid: treatmentSessionUuid } : {}),
      ...(pickerMode ? { service_item_uuids: pick.serviceUuids } : {}),
      // منبع: مدت را سرور از سرویس‌های همین منبع می‌سازد، پس ساعت پایان حدس نیست.
      ...(resource ? {
        resource_uuid: resource.uuid,
        duration_from_services: true,
        service_durations: pick.durations,
      } : {}),
      ...(visitPriceToman > 0 ? { visit_price_rials: tomanToRial(visitPriceToman) } : {}),
    }),
    onSuccess: () => {
      toast.success('نوبت با موفقیت ثبت شد');
      onSuccess();
      onClose();
    },
    onError: (e: any) => {
      const msg = e?.response?.data?.errors?.[0]?.message ?? e?.message ?? 'خطا در ثبت نوبت';
      toast.error(msg);
    },
  });

  // تغییر کلید جستجو نتیجه‌ی جستجوی قبلی را باطل می‌کند تا کاربر دوباره جستجو کند.
  function invalidateLookup() {
    if (lookup !== null) { setLookup(null); setPatientName(''); }
  }
  function onMobileChange(v: string) {
    // ارقام فارسی/عربی → انگلیسی، فقط رقم، حداکثر ۱۱ رقم (کیبورد فارسی هم پذیرفته می‌شود).
    setMobile(sanitizeMobileInput(v));
    if (lookup !== null) { setLookup(null); setPatientName(''); if (searchBy === 'mobile') setNationalCode(''); }
  }
  function onNationalSearchChange(v: string) {
    setNationalCode(digitsOnly(v, 10));
    invalidateLookup();
  }
  // جابه‌جایی معیار جستجو همه‌چیز را از نو شروع می‌کند.
  function onSwitchSearchBy(mode: 'mobile' | 'national') {
    setSearchBy(mode);
    setLookup(null); setPatientName('');
    setMobile(''); setNationalCode('');
  }

  // هدفِ نوبت به‌صورت «برچسب: مقدار» — دو اسمِ لخت کنار هم معلوم نمی‌کند کدام دستگاه
  // است و کدام پزشک. تاریخ هم اینجاست چون تنها جای مودال بود که اصلاً دیده نمی‌شد.
  const targetFacts: { label: string; value: string }[] = [
    resource
      ? { label: 'دستگاه', value: resource.name }
      : pickerMode
        ? { label: 'پزشک', value: slot.doctor_name }
        : { label: 'ساعت', value: `${slot.start_time} تا ${slot.end_time}` },
    ...((resource || !pickerMode) && slot.doctor_name
      ? [{ label: resource ? 'پزشک ناظر' : 'پزشک', value: slot.doctor_name }]
      : []),
    /* در حالت انتخابگر، خودِ فیلدِ تاریخ همین را می‌گوید و تکرارش در هدر نویز است. */
    ...(activeDate && !pickerMode ? [{ label: 'تاریخ', value: formatDate(activeDate) }] : []),
  ];

  /**
   * مراحلِ ویزارد — فقط مراحلی که واقعاً تصمیمی دارند.
   *
   * فرم قبلاً یک صفحهٔ بلند بود: انتخاب سرویس و زمان، جستجوی بیمار و هزینه همه با هم.
   * مرحله‌ای که داده‌اش از قبل معلوم است ساخته نمی‌شود — نوبتِ اسلاتی زمان دارد و
   * فرمِ بازشده از پروندهٔ بیمار، بیمار.
   */
  const stepKeys: StepKey[] = [
    ...(pickerMode && activeDate ? (['service'] as StepKey[]) : []),
    ...(patient === null ? (['patient'] as StepKey[]) : []),
    'confirm',
  ];
  const currentStep = stepKeys[Math.min(stepIdx, stepKeys.length - 1)];
  const isLastStep = currentStep === 'confirm';

  // اولین چیزی که جلوی رفتن به مرحلهٔ بعد (یا ثبت) را گرفته. دکمهٔ خاکستریِ
  // بی‌توضیح یعنی کاربر باید حدس بزند چه چیزی کم است.
  const stepBlockReason: Record<StepKey, string | null> = {
    service: !serviceTimingValid
      ? (pick.serviceUuids.length === 0 ? 'یک سرویس انتخاب کنید' : 'ساعت شروع را انتخاب کنید')
      : null,
    patient: lookup === null
      ? 'ابتدا بیمار را جستجو کنید'
      : needsDetails && !detailsValid
        ? 'مشخصات بیمار را کامل کنید'
        : !mobileValid
          ? 'شماره موبایل بیمار معتبر نیست'
          : null,
    confirm: !visitPriceValid ? 'هزینه ویزیت الزامی است' : null,
  };
  const blockReason = stepBlockReason[currentStep];

  // خلاصهٔ مرحلهٔ آخر: همان چیزی که ثبت می‌شود، پیش از ثبت.
  const pickedServiceNames = pick.serviceUuids
    .map(uuid => services.find(sv => sv.uuid === uuid)?.name ?? uuid);
  const staffName = (staffQ.data?.data ?? []).find(st => st.uuid === staffUuid)?.full_name ?? null;
  const summaryRows: { label: string; value: string }[] = [
    ...targetFacts.map(f => ({ label: f.label, value: f.value })),
    ...(pickerMode && activeDate ? [{ label: 'تاریخ', value: formatDate(activeDate) }] : []),
    ...(pickerMode && pick.slot ? [{ label: 'ساعت', value: pick.slot.start_time }] : []),
    ...(pickedServiceNames.length > 0 ? [{ label: 'سرویس', value: pickedServiceNames.join('، ') }] : []),
    ...(staffName ? [{ label: 'پرسنل', value: staffName }] : []),
    { label: 'بیمار', value: effectiveName || (patient?.name ?? '—') },
    { label: 'موبایل', value: mobile || (patient?.mobile ?? '—') },
  ];

  const priceHint = pricingLoading
    ? 'در حال خواندن تعرفهٔ پزشک…'
    : freeVisit > 0
      ? `تعرفهٔ نوبت‌دهی ${slot.doctor_name}: ${formatRial(freeVisit)} — در صورت نیاز تغییر دهید`
      : 'برای این پزشک تعرفه‌ای ثبت نشده — در صورت نیاز مبلغ را وارد کنید';

  return (
    <Modal
      open
      title="ثبت نوبت"
      // هر بار یک مرحله دیده می‌شود، پس ستون تکی کافی است؛ `md` عرضِ راحتِ
      // انتخابگر سرویس و اسلات است بدون فضای خالی.
      size="md"
      onClose={onClose}
      footer={
        <>
          {/* «قبلی» جای «انصراف» را نمی‌گیرد: بستنِ فرم همیشه باید یک کلیک باشد. */}
          <button className="btn ghost" onClick={onClose}>انصراف</button>
          {stepIdx > 0 && (
            <button className="btn secondary" onClick={() => setStepIdx(i => i - 1)}>
              مرحلهٔ قبل
            </button>
          )}
          {isLastStep ? (
            <button
              className="btn primary"
              onClick={() => mutation.mutate()}
              disabled={!isValid || mutation.isPending}
            >
              {mutation.isPending ? 'در حال ثبت…' : 'ثبت نوبت'}
            </button>
          ) : (
            <button
              className="btn primary"
              onClick={() => setStepIdx(i => i + 1)}
              disabled={blockReason !== null}
            >
              مرحلهٔ بعد
            </button>
          )}
        </>
      }
    >
      {/* هدف نوبت: منبع، یا اسلات/پزشک */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap', marginBottom: 18,
        padding: '10px 14px', borderRadius: 'var(--r-sm)', background: 'var(--primary-soft)',
      }}>
        {resource
          ? <CpuChipIcon style={{ width: 18, height: 18, color: 'var(--primary-700)', flexShrink: 0 }} />
          : <ClockIcon style={{ width: 18, height: 18, color: 'var(--primary-700)', flexShrink: 0 }} />}
        {targetFacts.map(f => (
          <span key={f.label} style={{ fontSize: 13 }}>
            <span style={{ color: 'var(--text-3)' }}>{f.label}: </span>
            <span style={{ color: 'var(--text)', fontWeight: 600 }}>{f.value}</span>
          </span>
        ))}
      </div>

      {stepKeys.length > 1 && (
        <Stepper keys={stepKeys} current={currentStep} />
      )}

      {currentStep === 'service' && (
        <Step title={STEP_TITLES.service}>
          <div className="field-block" style={{ marginBottom: 12, maxWidth: 220 }}>
            <label htmlFor="appt-date">تاریخ نوبت</label>
            <PersianDateInput
              value={activeDate}
              onChange={setActiveDate}
              ariaLabel="تاریخ نوبت"
            />
          </div>

          <ServiceSlotPicker
            doctorUuid={slot.doctor_uuid}
            resourceUuid={resource?.uuid}
            date={activeDate}
            services={services}
            onSelect={setPick}
            clinicUuidOverride={clinicUuid}
          />

          {/* اختیاری به‌عمد: پیش‌فرضِ سیستم صفِ مشترک است و منشی فقط وقتی دخالت
              می‌کند که بیمار اپراتور مشخصی خواسته باشد. */}
          <div className="field-block" style={{ marginTop: 12 }}>
            <label htmlFor="appt-operator">پرسنل <span className="opt">(اختیاری)</span></label>
            <SearchableSelect
              inputId="appt-operator"
              options={(staffQ.data?.data ?? []).map(s => ({ value: s.uuid, label: s.full_name }))}
              value={staffUuid || null}
              onChange={v => { setStaffUuid(v ? String(v) : ''); setStaffTouched(true); }}
              placeholder={protocolQ.isLoading ? 'در حال خواندن پرسنل سرویس…' : 'در صف مشترک پرسنل بماند'}
              isLoading={staffQ.isLoading}
              isClearable
              height={40}
            />
          </div>
        </Step>
      )}

      {currentStep === 'patient' && (
      <Step title={STEP_TITLES.patient}>
        {/* بیمارِ از پیش معلوم: فقط تأیید می‌شود، جستجو لازم نیست. */}
        {patient !== null && (
          <div style={{
            marginBottom: 14, padding: '10px 14px', borderRadius: 'var(--r-sm)',
            background: 'var(--success-bg)', display: 'flex', alignItems: 'center', gap: 10, fontSize: 13,
          }}>
            <CheckCircleIcon style={{ width: 20, height: 20, color: 'var(--success)', flexShrink: 0 }} />
            <div style={{ minWidth: 0 }}>
              <div style={{ fontWeight: 700, color: 'var(--text)' }}>{patient.name || 'بدون نام'}</div>
              <div style={{ color: 'var(--text-2)', fontSize: 12, direction: 'ltr', textAlign: 'start' }}>
                {patient.mobile}{patient.national_code ? ` · ${patient.national_code}` : ''}
              </div>
            </div>
          </div>
        )}

        <div className="field-block" style={{ marginBottom: 14, display: patient === null ? undefined : 'none' }}>
          <label htmlFor="appt-patient-search">جستجوی بیمار <span className="req">*</span></label>
          {/* انتخاب معیار جستجو: موبایل یا کد ملی */}
          <div className="seg" style={{ display: 'flex', marginBottom: 8 }}>
            {(['national', 'mobile'] as const).map(mode => (
              <button
                key={mode}
                type="button"
                className={searchBy === mode ? 'on' : ''}
                aria-pressed={searchBy === mode}
                onClick={() => onSwitchSearchBy(mode)}
                style={{ flex: 1, justifyContent: 'center' }}
              >
                {mode === 'mobile' ? 'شماره موبایل' : 'کد ملی'}
              </button>
            ))}
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <div className="field" style={{ flex: 1 }}>
              {searchBy === 'mobile' ? (
                <input
                  id="appt-patient-search"
                  type="tel"
                  inputMode="numeric"
                  maxLength={11}
                  value={mobile}
                  onChange={e => onMobileChange(e.target.value)}
                  onKeyDown={e => { if (e.key === 'Enter' && searchValid && !search.isPending) search.mutate(); }}
                  placeholder="مثال: 09123456789"
                  style={{ direction: 'ltr' }}
                  autoFocus={!pickerMode}
                />
              ) : (
                <input
                  id="appt-patient-search"
                  type="text"
                  inputMode="numeric"
                  lang="en"
                  maxLength={10}
                  value={nationalCode}
                  onChange={e => onNationalSearchChange(e.target.value)}
                  onKeyDown={e => { if (e.key === 'Enter' && searchValid && !search.isPending) search.mutate(); }}
                  placeholder="کد ملی ۱۰ رقمی"
                  style={{ direction: 'ltr' }}
                  // در حالت انتخابگر، اولین تصمیم «بخش» است نه بیمار؛ فوکوسِ خودکار
                  // اینجا کاربر را از مرحلهٔ یک رد می‌کرد.
                  autoFocus={!pickerMode}
                />
              )}
            </div>
            <button
              className="btn soft"
              onClick={() => search.mutate()}
              disabled={!searchValid || search.isPending}
              style={{ whiteSpace: 'nowrap' }}
            >
              <MagnifyingGlassIcon style={{ width: 16, height: 16 }} />
              {search.isPending ? '...' : 'جستجو'}
            </button>
          </div>
        </div>

      {/* وقتی بیمار از قبل معلوم است، کارت بالا همین را می‌گوید؛ دو بار گفتنش نویز است. */}
      {patient === null && foundWithNationalCode && (
        <div style={{
          marginBottom: 16, padding: '10px 14px', borderRadius: 'var(--r-sm)',
          background: 'var(--success-bg)', fontSize: 13,
          display: 'flex', alignItems: 'center', gap: 10,
        }}>
          <CheckCircleIcon style={{ width: 20, height: 20, color: 'var(--success)', flexShrink: 0 }} />
          <div style={{ minWidth: 0 }}>
            <div style={{ fontWeight: 700, color: 'var(--success)', fontSize: 12 }}>بیمار یافت شد</div>
            <div style={{ fontWeight: 700, color: 'var(--text)' }}>{lookup?.name}</div>
            <div style={{ color: 'var(--text-2)', fontSize: 12 }}>کد ملی: {lookup?.national_code}</div>
          </div>
        </div>
      )}

      {needsDetails && (
        <>
          <div style={{
            fontSize: 12.5, color: 'var(--text-2)', marginBottom: 12,
            padding: '9px 12px', borderRadius: 'var(--r-sm)', background: 'var(--warning-bg)',
          }}>
            {lookup?.found ? 'برای این بیمار کد ملی ثبت نشده — لطفاً تکمیل کنید:' : 'بیماری با این مشخصات یافت نشد — بیمار جدید:'}
          </div>
          <div className="field-block" style={{ marginBottom: 14 }}>
            <label htmlFor="appt-patient-name">نام و نام خانوادگی بیمار <span className="req">*</span></label>
            <div className="field">
              <input
                id="appt-patient-name"
                type="text"
                value={patientName}
                onChange={e => setPatientName(e.target.value)}
                placeholder="مثال: علی محمدی"
              />
            </div>
          </div>
          {/* در جستجو با کد ملی، موبایل هنوز نامعلوم است و برای ثبت لازم می‌شود. */}
          {searchBy === 'national' && (
            <div className="field-block" style={{ marginBottom: 14 }}>
              <label htmlFor="appt-patient-mobile">شماره موبایل بیمار <span className="req">*</span></label>
              <div className="field">
                <input
                  id="appt-patient-mobile"
                  type="tel"
                  inputMode="numeric"
                  maxLength={11}
                  value={mobile}
                  onChange={e => setMobile(sanitizeMobileInput(e.target.value))}
                  placeholder="مثال: 09123456789"
                  style={{ direction: 'ltr' }}
                />
              </div>
            </div>
          )}
          {/* در جستجو با کد ملی، همان مقدار کلیدِ جستجو استفاده می‌شود و فیلد تکراری لازم نیست. */}
          {searchBy === 'mobile' && (
            <div className="field-block" style={{ marginBottom: 14 }}>
              <label htmlFor="appt-patient-national">کد ملی بیمار <span className="req">*</span></label>
              <div className="field">
                <input
                  id="appt-patient-national"
                  type="text"
                  inputMode="numeric"
                  lang="en"
                  maxLength={10}
                  value={nationalCode}
                  onChange={e => setNationalCode(digitsOnly(e.target.value, 10))}
                  placeholder="کد ملی ۱۰ رقمی"
                  style={{ direction: 'ltr' }}
                />
              </div>
            </div>
          )}
        </>
      )}
      </Step>
      )}

      {currentStep === 'confirm' && (
      <Step title={STEP_TITLES.confirm}>
        {/* خلاصهٔ همان چیزی که ثبت می‌شود — تنها جایی که کاربر پیش از ثبت، انتخاب
            سرویس و زمان و بیمار را با هم می‌بیند. */}
        <dl style={{
          display: 'grid', gridTemplateColumns: 'auto 1fr', gap: '8px 14px', margin: '0 0 18px',
          padding: '12px 14px', borderRadius: 'var(--r-sm)', background: 'var(--surface-2)',
          fontSize: 13,
        }}>
          {summaryRows.map(row => (
            <React.Fragment key={row.label}>
              <dt style={{ color: 'var(--text-3)' }}>{row.label}</dt>
              <dd style={{ margin: 0, color: 'var(--text)', fontWeight: 600, minWidth: 0, overflowWrap: 'anywhere' }}>
                {row.value}
              </dd>
            </React.Fragment>
          ))}
        </dl>

      {/* هزینه ویزیت در حالت اختیاری یک کلپسِ بسته است. */}
      <div className="field-block">
        {requireVisit ? (
          <label>
            هزینه ویزیت (تومان)<span className="req"> *</span>
          </label>
        ) : (
          // سرِ کلپس — با کلیک باز/بسته می‌شود (فقط وقتی اختیاری است).
          <button
            type="button"
            onClick={() => setVisitPriceOpen(o => !o)}
            aria-expanded={visitPriceExpanded}
            style={{
              // سرِ کلپس تمام عرض و ۳۶px است: با padding صفر ارتفاعش اندازهٔ یک خط متن
              // می‌شد و روی موبایل عملاً قابل زدن نبود.
              display: 'flex', alignItems: 'center', gap: 6, width: '100%', minHeight: 36,
              background: 'none', border: 'none', cursor: 'pointer', font: 'inherit',
              padding: 0, color: 'var(--text)', textAlign: 'start',
            }}
          >
            <span>هزینه ویزیت (تومان) <span className="opt">(اختیاری)</span></span>
            <ChevronDownIcon
              style={{
                width: 15, height: 15, marginInlineStart: 'auto', flexShrink: 0,
                transition: 'transform .2s var(--ease)',
                transform: visitPriceExpanded ? 'rotate(180deg)' : 'none',
              }}
            />
          </button>
        )}
        {visitPriceExpanded && (
          <>
            <div
              className="field"
              style={requireVisit && visitPriceToman <= 0 ? { borderColor: 'var(--danger)' } : undefined}
            >
              <PriceInput
                value={visitPriceToman}
                onChange={(v) => { setVisitPriceToman(v); setVisitPriceTouched(true); }}
                suffix="تومان"
              />
            </div>
            {requireVisit && visitPriceToman <= 0
              ? <span className="field-err">هزینه ویزیت الزامی است</span>
              : <span className="field-hint">{priceHint}</span>}
            {freeVisit > 0 && visitPriceToman !== rialToToman(freeVisit) && (
              <button
                type="button"
                className="btn ghost sm"
                style={{ marginTop: 8, alignSelf: 'flex-start' }}
                onClick={() => { setVisitPriceToman(rialToToman(freeVisit)); setVisitPriceTouched(true); }}
              >
                استفاده از تعرفهٔ پزشک
              </button>
            )}
          </>
        )}
      </div>
      </Step>
      )}

      {blockReason && (
        <div style={{
          display: 'flex', alignItems: 'center', gap: 6, marginTop: 16,
          fontSize: 12.5, color: 'var(--text-3)',
        }}>
          <ExclamationCircleIcon style={{ width: 15, height: 15, flexShrink: 0 }} />
          {isLastStep ? 'برای ثبت نوبت' : 'برای مرحلهٔ بعد'}: {blockReason}
        </div>
      )}
    </Modal>
  );
}

/** بدنهٔ یک مرحله — عنوانش در `Stepper` هم هست، اینجا سرِ همان بخش است. */
function Step({ title, children }: { title: string; children: ReactNode }) {
  return (
    <section style={{ marginBottom: 18 }}>
      <h3 style={{
        margin: '0 0 10px', fontSize: 13.5, fontWeight: 700, color: 'var(--text)',
      }}>
        {title}
      </h3>
      {children}
    </section>
  );
}

/**
 * نوار مراحل — «کجای کارم و چقدر مانده».
 *
 * فرم قبلاً همهٔ مراحل را با هم نشان می‌داد و مودال از ارتفاع صفحه بلندتر می‌شد؛
 * حالا هر بار یک مرحله دیده می‌شود و این نوار تنها چیزی است که کلِ مسیر را می‌گوید.
 */
function Stepper({ keys, current }: { keys: StepKey[]; current: StepKey }) {
  const activeIdx = keys.indexOf(current);

  return (
    <ol
      aria-label="مراحل ثبت نوبت"
      style={{
        display: 'flex', alignItems: 'center', gap: 8, listStyle: 'none',
        margin: '0 0 18px', padding: 0,
      }}
    >
      {keys.map((key, idx) => {
        const done = idx < activeIdx;
        const active = idx === activeIdx;

        return (
          <li
            key={key}
            aria-current={active ? 'step' : undefined}
            style={{ display: 'flex', alignItems: 'center', gap: 8, flex: idx === keys.length - 1 ? '0 0 auto' : 1 }}
          >
            <span style={{
              display: 'grid', placeItems: 'center', width: 22, height: 22, borderRadius: 999,
              flexShrink: 0, fontSize: 11.5, fontWeight: 700,
              background: active || done ? 'var(--primary)' : 'var(--surface-3)',
              color: active || done ? 'var(--on-primary)' : 'var(--text-3)',
            }}>
              {idx + 1}
            </span>
            <span style={{
              fontSize: 12.5, whiteSpace: 'nowrap',
              fontWeight: active ? 700 : 500,
              color: active ? 'var(--text)' : 'var(--text-3)',
            }}>
              {STEP_TITLES[key]}
            </span>
            {idx < keys.length - 1 && (
              <span style={{
                flex: 1, height: 1, minWidth: 12,
                background: done ? 'var(--primary)' : 'var(--border)',
              }} />
            )}
          </li>
        );
      })}
    </ol>
  );
}
