import React, { useState, useEffect, useMemo, useRef, useCallback } from 'react';
import { createPortal } from 'react-dom';
import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import {
  ArrowRightIcon, PencilIcon, TrashIcon,
  XCircleIcon, PhoneIcon, CalendarIcon,
  ClipboardDocumentIcon, EllipsisVerticalIcon, StarIcon,
  BuildingOfficeIcon, MapPinIcon, AcademicCapIcon, UserIcon,
  PlusIcon, CameraIcon, ChevronDownIcon, ChevronRightIcon, ChevronLeftIcon,
  CheckCircleIcon, XMarkIcon, ExclamationTriangleIcon,
  HeartIcon, CheckIcon, IdentificationIcon, DocumentTextIcon, GlobeAltIcon,
} from '@heroicons/react/24/outline';
import { StarIcon as StarSolid } from '@heroicons/react/24/solid';
import { toast } from 'sonner';
import { MapContainer, TileLayer, Marker, useMapEvents, useMap } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { api, ApiError } from '../lib/api';
import { useAuthStore } from '../stores/authStore';
import type { ApiResponse } from '../lib/api';
import { formatNumber, iranMobileOptionalSchema, toDate, toGregorianDate } from '../lib/utils';
import MobileInput from '../components/ui/MobileInput';
import Modal from '../components/ui/Modal';
import ConfirmDialog from '../components/ui/ConfirmDialog';
import NotificationMobileCard from '../components/ui/NotificationMobileCard';
import GlobalSearchableSelect from '../components/ui/SearchableSelect';
import PersianDatePicker from '../components/ui/PersianDatePicker';
import ImageCropModal from '../components/ImageCropModal';

// Fix leaflet default marker icons
delete (L.Icon.Default.prototype as any)._getIconUrl;
L.Icon.Default.mergeOptions({
  iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
  iconUrl:       'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
  shadowUrl:     'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
});

// ── Types ──────────────────────────────────────────────────────────────────

interface DoctorDetail {
  id: string; uuid: string; name: string; gender: string | null;
  experience: number; activity_time: string | null; medical_system_code: string | null;
  detail: string | null; degree: string | null; active: boolean;
  specialties: { id: string; uuid: string; name: string; parent_id: string | null }[];
  img: { url: string; fid: number }[];
  expertise: { id: string; uuid: string; name: string }[];
  satisfaction: string; point: string;
  social_media: {
    instagram: string | null; telegram: string | null; aparat: string | null;
    youtube: string | null; linkedin: string | null;
  } | null;
  address: AddressData[];
  state: { id: string; name: string }[];
  city:  { id: string; name: string }[];
  clinics: { id: string; uuid: string; name: string; address: string | null; telephone: string | null }[];
  representation: { id: number; uuid: string; full_name: string | null } | null;
}

interface SpecialtyOpt { id: number; uuid: string; name: string; parent_id: number | null; }
interface ServiceOpt   { id: number; uuid: string; name: string; specialty_id: number | null; }
interface ProvinceOpt  { id: number; uuid: string; name: string; }
interface CityOpt      { id: number; uuid: string; name: string; }
interface ImageFileData { fid: number; uuid: string; url: string; filename: string; filemime: string; filesize: number; }

interface AddressData {
  id: string; uuid: string;
  type: 'personal' | 'clinic';
  clinic_id: string | null;
  clinic_name: string | null;
  name: string | null; address: string | null;
  telephone: string | null;
  map: { latitude: string | null; longitude: string | null };
  city:     { id: string; name: string } | null;
  province: { id: string; name: string } | null;
}

// ── Schedule types ─────────────────────────────────────────────────────────

interface SlotConfig       { start: string; end: string; duration: number; }
interface DateOverrideData   { uuid: string; doctor_uuid: string; date: number; active: boolean; reason: string | null; custom_slots: SessionConfig[]; }
interface HolidayData        { uuid: string; doctor_uuid: string; start_date: number; end_date: number; active: boolean; reason: string | null; }

interface SessionConfig {
  active: boolean;
  location_id: number | null;
  start_time: string;
  end_time: string;
  duration_per_patient: number;
  has_rest: boolean;
  rest_interval: number;
  time_to_rest: number;
  patient_limit: number | null;
}
interface NewDayConfig { sessions: SessionConfig[]; }
type NewScheduleMap = Record<string, NewDayConfig>;
interface BookingMeta {
  online_booking_enabled: boolean;
  booking_window_value: number;
  booking_window_unit: 'week' | 'month';
}
const DEFAULT_BOOKING_META: BookingMeta = {
  online_booking_enabled: true,
  booking_window_value: 1,
  booking_window_unit: 'month',
};
interface WeeklyScheduleData { uuid: string; doctor_uuid: string; schedule: NewScheduleMap; meta?: BookingMeta; }

// ── Constants ──────────────────────────────────────────────────────────────

const DEGREE_LABELS: Record<string, string> = {
  general: 'عمومی', specialist: 'متخصص',
  expert: 'فوق تخصص', subspecialistplus: 'فلوشیپ',
};
const DEGREE_OPTIONS = [
  { value: 'general',           label: 'عمومی' },
  { value: 'specialist',        label: 'متخصص' },
  { value: 'expert',            label: 'فوق تخصص' },
  { value: 'subspecialistplus', label: 'فلوشیپ' },
];
const GENDER_OPTIONS = [{ value: 'man', label: 'مرد' }, { value: 'woman', label: 'زن' }];
const AVATAR_COLORS  = [
  'from-blue-500 to-cyan-500', 'from-violet-500 to-purple-600',
  'from-emerald-500 to-teal-600', 'from-rose-500 to-pink-600',
  'from-amber-500 to-orange-600',
];
const SPECIALTY_COLORS = [
  'bg-red-100 dark:bg-red-400/10 text-red-700 dark:text-red-300',
  'bg-blue-100 dark:bg-blue-400/10 text-blue-700 dark:text-blue-300',
  'bg-emerald-100 dark:bg-emerald-400/10 text-emerald-700 dark:text-emerald-300',
  'bg-violet-100 dark:bg-violet-400/10 text-violet-700 dark:text-violet-300',
  'bg-orange-100 dark:bg-orange-400/10 text-orange-700 dark:text-orange-300',
  'bg-pink-100 dark:bg-pink-400/10 text-pink-700 dark:text-pink-300',
];

// ── Persian (Jalali) date utilities ───────────────────────────────────────

// eslint-disable-next-line @typescript-eslint/no-require-imports
const jalaali = require('jalaali-js') as {
  toJalaali: (gy: number, gm: number, gd: number) => { jy: number; jm: number; jd: number };
  toGregorian: (jy: number, jm: number, jd: number) => { gy: number; gm: number; gd: number };
  jalaaliMonthLength: (jy: number, jm: number) => number;
};

const JALALI_MONTHS     = ['فروردین','اردیبهشت','خرداد','تیر','مرداد','شهریور','مهر','آبان','آذر','دی','بهمن','اسفند'];
const JALALI_DAYS_SHORT = ['ش','ی','د','س','چ','پ','ج'];

function toPersianNums(n: number | string): string {
  return String(n).replace(/\d/g, d => '۰۱۲۳۴۵۶۷۸۹'[parseInt(d)]);
}
function gToJ(gDate: string): { jy: number; jm: number; jd: number } | null {
  if (!gDate) return null;
  const [gy, gm, gd] = gDate.split('-').map(Number);
  return jalaali.toJalaali(gy, gm, gd);
}
function jToG(jy: number, jm: number, jd: number): string {
  const { gy, gm, gd } = jalaali.toGregorian(jy, jm, jd);
  return `${gy}-${String(gm).padStart(2,'0')}-${String(gd).padStart(2,'0')}`;
}
function formatPersianDate(gDate: string): string {
  const j = gToJ(gDate);
  if (!j) return gDate;
  return `${toPersianNums(j.jd)} ${JALALI_MONTHS[j.jm - 1]} ${toPersianNums(j.jy)}`;
}
function jFirstDayOfWeek(jy: number, jm: number): number {
  const { gy, gm, gd } = jalaali.toGregorian(jy, jm, 1);
  const dow = new Date(gy, gm - 1, gd).getDay(); // 0=Sunday
  return (dow + 1) % 7; // 0=Saturday, 1=Sunday, ..., 6=Friday
}
function todayGregorian(): string { return new Date().toISOString().slice(0, 10); }

// ── Persian Date Picker ────────────────────────────────────────────────────

function PersianDateInput({ value, onChange, minDate, placeholder }: {
  value: string; onChange: (v: string) => void; minDate?: string; placeholder?: string;
}) {
  const [open,    setOpen]    = useState(false);
  const [viewJy,  setViewJy]  = useState(0);
  const [viewJm,  setViewJm]  = useState(0);
  const [rect,    setRect]    = useState<DOMRect | null>(null);
  const btnRef  = useRef<HTMLButtonElement>(null);
  const dropRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;
    const j = gToJ(value || todayGregorian());
    if (j) { setViewJy(j.jy); setViewJm(j.jm); }
  }, [open, value]);

  useEffect(() => {
    if (!open) return;
    const onDown = (e: MouseEvent) => {
      if (dropRef.current && !dropRef.current.contains(e.target as Node) &&
          btnRef.current  && !btnRef.current.contains(e.target as Node)) setOpen(false);
    };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);

  const openPicker = () => {
    if (!btnRef.current) return;
    setRect(btnRef.current.getBoundingClientRect());
    setOpen(v => !v);
  };

  const prevMonth = () => viewJm === 1  ? (setViewJy(y => y - 1), setViewJm(12)) : setViewJm(m => m - 1);
  const nextMonth = () => viewJm === 12 ? (setViewJy(y => y + 1), setViewJm(1))  : setViewJm(m => m + 1);

  const selectDay = (jd: number) => { onChange(jToG(viewJy, viewJm, jd)); setOpen(false); };

  const selectedJ = gToJ(value);
  const minJ      = minDate ? gToJ(minDate) : null;
  const todayJ    = gToJ(todayGregorian());

  const daysInMonth = viewJy && viewJm ? jalaali.jalaaliMonthLength(viewJy, viewJm) : 0;
  const firstDow    = viewJy && viewJm ? jFirstDayOfWeek(viewJy, viewJm) : 0;

  const isSelected = (jd: number) => !!selectedJ && selectedJ.jy === viewJy && selectedJ.jm === viewJm && selectedJ.jd === jd;
  const isToday    = (jd: number) => !!todayJ    && todayJ.jy    === viewJy && todayJ.jm    === viewJm && todayJ.jd    === jd;
  const isDisabled = (jd: number) => {
    if (!minJ) return false;
    if (viewJy < minJ.jy) return true;
    if (viewJy === minJ.jy && viewJm < minJ.jm) return true;
    if (viewJy === minJ.jy && viewJm === minJ.jm && jd < minJ.jd) return true;
    return false;
  };

  const dropStyle: React.CSSProperties = rect
    ? { position: 'fixed', top: rect.bottom + 4, left: rect.left, zIndex: 9999, width: 288 }
    : {};

  return (
    <div>
      <button ref={btnRef} type="button" onClick={openPicker}
        className="cp-input h-11 flex items-center justify-between w-full cursor-pointer">
        <span className={value ? 'text-slate-800 dark:text-slate-200' : 'text-slate-400 dark:text-slate-500'}>
          {value ? formatPersianDate(value) : (placeholder ?? 'انتخاب تاریخ')}
        </span>
        <CalendarIcon className="w-4 h-4 text-slate-400 shrink-0 mr-2" />
      </button>
      {open && createPortal(
        <div ref={dropRef} style={dropStyle}
          className="bg-white dark:bg-gray-900 border border-slate-200 dark:border-gray-700 rounded-2xl shadow-2xl overflow-hidden">
          <div className="flex items-center justify-between px-4 py-3 border-b border-slate-100 dark:border-gray-800">
            <button type="button" onClick={nextMonth}
              className="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-slate-100 dark:hover:bg-gray-800 text-slate-500 transition-colors">
              <ChevronRightIcon className="w-4 h-4" />
            </button>
            <span className="text-sm font-semibold text-slate-800 dark:text-slate-100" dir="rtl">
              {viewJm > 0 ? JALALI_MONTHS[viewJm - 1] : ''} {toPersianNums(viewJy)}
            </span>
            <button type="button" onClick={prevMonth}
              className="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-slate-100 dark:hover:bg-gray-800 text-slate-500 transition-colors">
              <ChevronLeftIcon className="w-4 h-4" />
            </button>
          </div>
          <div className="grid grid-cols-7 px-3 pt-2 pb-1">
            {JALALI_DAYS_SHORT.map(d => (
              <div key={d} className="text-center text-xs font-medium text-slate-400 dark:text-slate-500 py-1">{d}</div>
            ))}
          </div>
          <div className="grid grid-cols-7 gap-0.5 px-3 pb-3">
            {Array.from({ length: firstDow }).map((_, i) => <div key={`e-${i}`} />)}
            {Array.from({ length: daysInMonth }).map((_, i) => {
              const jd  = i + 1;
              const sel = isSelected(jd);
              const tod = isToday(jd);
              const dis = isDisabled(jd);
              return (
                <button key={jd} type="button" disabled={dis} onClick={() => selectDay(jd)}
                  className={`w-full aspect-square flex items-center justify-center rounded-lg text-sm transition-colors
                    ${dis ? 'text-slate-300 dark:text-gray-600 cursor-not-allowed'
                          : sel ? 'bg-primary-500 text-white font-semibold shadow-sm'
                               : tod ? 'ring-1 ring-primary-400 text-primary-700 dark:text-primary-300 font-medium'
                                     : 'text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-gray-800'}`}>
                  {toPersianNums(jd)}
                </button>
              );
            })}
          </div>
        </div>,
        document.body
      )}
    </div>
  );
}

const SCHEDULE_DAYS = [
  { key: '0', label: 'شنبه'     },
  { key: '1', label: 'یکشنبه'   },
  { key: '2', label: 'دوشنبه'   },
  { key: '3', label: 'سه‌شنبه'  },
  { key: '4', label: 'چهارشنبه' },
  { key: '5', label: 'پنجشنبه'  },
  { key: '6', label: 'جمعه'     },
];
const DURATION_OPTS = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60];
const DEFAULT_SESSION: SessionConfig = {
  active: true, location_id: null,
  start_time: '09:00', end_time: '13:00',
  duration_per_patient: 20,
  has_rest: false, rest_interval: 60, time_to_rest: 10,
  patient_limit: null,
};
const EMPTY_NEW_SCHEDULE: NewScheduleMap = Object.fromEntries(
  SCHEDULE_DAYS.map(d => [d.key, { sessions: [] }])
);

function parseMinutes(t: string): number {
  const [h, m] = t.split(':').map(Number);
  return h * 60 + m;
}

function hasOverlap(sessions: SessionConfig[]): boolean {
  const active = sessions.filter(s => s.active);
  const sorted = [...active].sort((a, b) => parseMinutes(a.start_time) - parseMinutes(b.start_time));
  for (let i = 0; i < sorted.length - 1; i++) {
    if (parseMinutes(sorted[i].end_time) > parseMinutes(sorted[i + 1].start_time)) return true;
  }
  return false;
}

function addMinutes(time: string, mins: number): string {
  const total   = Math.min(parseMinutes(time) + mins, 23 * 60 + 59);
  const h = Math.floor(total / 60).toString().padStart(2, '0');
  const m = (total % 60).toString().padStart(2, '0');
  return `${h}:${m}`;
}

function calcSlotCount(session: SessionConfig): number {
  const totalMins = parseMinutes(session.end_time) - parseMinutes(session.start_time);
  if (totalMins <= 0 || session.duration_per_patient <= 0) return 0;
  const dur   = session.duration_per_patient;
  const limit = session.patient_limit ?? Infinity;
  let current = 0, elapsedWork = 0, count = 0;
  while (current + dur <= totalMins) {
    if (count >= limit) break;
    if (session.has_rest && session.rest_interval > 0 && elapsedWork > 0 && elapsedWork >= session.rest_interval) {
      current += session.time_to_rest; elapsedWork = 0; continue;
    }
    current += dur; elapsedWork += dur; count++;
  }
  return count;
}
function tsToDate(ts: number): string {
  return new Date(ts * 1000).toISOString().slice(0, 10);
}

// ── Image upload ───────────────────────────────────────────────────────────

async function uploadDoctorImage(file: File): Promise<ImageFileData> {
  const token = useAuthStore.getState().token;
  const res = await fetch('/file/upload/clinic_pro/doctor/field_image', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/octet-stream',
      'Content-Disposition': `attachment; filename="${file.name}"`,
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
    },
    body: file,
  });
  const json = await res.json().catch(() => ({}));
  if (res.status === 401) {
    useAuthStore.getState().logout();
    window.location.replace('/admin/login');
    throw new Error('نشست منقضی شده است');
  }
  if (!res.ok) throw new Error(json?.errors?.[0]?.message ?? 'خطا در آپلود تصویر');
  return json.data as ImageFileData;
}

// ── Avatar with upload ─────────────────────────────────────────────────────

function DoctorAvatar({ name, img, idx, onUpload, uploading }: {
  name: string; img: string | null; idx: number;
  onUpload?: (f: File) => void; uploading: boolean;
}) {
  const ref = useRef<HTMLInputElement>(null);
  const initials = name.split(' ').filter(Boolean).map(w => w[0]).join('').toUpperCase().slice(0, 2) || 'Dr';
  return (
    <div
      className={`relative shrink-0 group ${onUpload ? 'cursor-pointer' : ''}`}
      onClick={() => onUpload && ref.current?.click()}
    >
      {img
        ? <img src={img} alt={name} className="w-24 h-24 rounded-2xl object-cover shadow-xl ring-4 ring-white dark:ring-gray-900" />
        : <div className={`w-24 h-24 rounded-2xl bg-gradient-to-br ${AVATAR_COLORS[idx % AVATAR_COLORS.length]} flex items-center justify-center text-white text-3xl font-bold shadow-xl ring-4 ring-white dark:ring-gray-900`}>{initials}</div>
      }
      {onUpload && (
        <div className="absolute inset-0 rounded-2xl bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
          {uploading
            ? <span className="w-6 h-6 border-2 border-white/40 border-t-white rounded-full animate-spin" />
            : <CameraIcon className="w-7 h-7 text-white" />
          }
        </div>
      )}
      {onUpload && (
        <input ref={ref} type="file" accept="image/*" className="hidden"
          onChange={e => { const f = e.target.files?.[0]; if (f) onUpload(f); e.target.value = ''; }} />
      )}
    </div>
  );
}

// ── Info Card ──────────────────────────────────────────────────────────────

function InfoCard({ icon: Icon, label, value, mono = false, copyable = false }: {
  icon: React.ElementType; label: string; value: React.ReactNode;
  mono?: boolean; copyable?: boolean;
}) {
  return (
    <div className="flex items-start gap-3 p-4 rounded-xl bg-slate-50 dark:bg-gray-800/60 hover:bg-slate-100 dark:hover:bg-gray-800 transition-colors group">
      <div className="w-9 h-9 rounded-lg bg-white dark:bg-gray-700 shadow-sm flex items-center justify-center shrink-0">
        <Icon className="w-4 h-4 text-slate-500 dark:text-slate-400" />
      </div>
      <div className="flex-1 min-w-0">
        <p className="text-[11px] font-medium text-slate-400 dark:text-slate-500 uppercase tracking-wide mb-0.5">{label}</p>
        <p className={`text-sm font-medium text-slate-800 dark:text-slate-200 ${mono ? 'font-mono' : ''}`} dir={mono ? 'ltr' : undefined}>
          {value ?? <span className="text-slate-400 font-normal">ثبت نشده</span>}
        </p>
      </div>
      {copyable && typeof value === 'string' && (
        <button onClick={() => { navigator.clipboard.writeText(value); toast.success('کپی شد'); }}
          className="opacity-0 group-hover:opacity-100 w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-slate-600 transition-all shrink-0">
          <ClipboardDocumentIcon className="w-3.5 h-3.5" />
        </button>
      )}
    </div>
  );
}

function StarRating({ rate }: { rate: number }) {
  return (
    <div className="flex items-center gap-1" dir="ltr">
      {Array.from({ length: 5 }).map((_, i) =>
        i < Math.round(rate)
          ? <StarSolid key={i} className="w-4 h-4 text-amber-400" />
          : <StarIcon key={i} className="w-4 h-4 text-slate-300 dark:text-slate-600" />
      )}
      <span className="text-sm font-medium text-slate-600 dark:text-slate-400 mr-1">{rate.toFixed(1)}</span>
    </div>
  );
}

// ── Map Picker ─────────────────────────────────────────────────────────────

const IRAN_CENTER: [number, number] = [32.4279, 53.6880];

async function geocodeCityInIran(cityName: string): Promise<[number, number] | null> {
  const url = `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(cityName + ',ایران')}&format=json&countrycodes=ir&limit=1`;
  // nominatim گاهی روی اولین فراخوان خالی/۴۲۹ برمی‌گرداند؛ یک retry تا انتخاب اول هم کار کند.
  for (let attempt = 0; attempt < 2; attempt++) {
    try {
      const res = await fetch(url, { headers: { 'Accept-Language': 'fa' } });
      const data = await res.json();
      if (data?.[0]) return [parseFloat(data[0].lat), parseFloat(data[0].lon)];
    } catch {
      /* تلاش بعدی */
    }
    if (attempt === 0) await new Promise(r => setTimeout(r, 900));
  }
  return null;
}

function MapClickHandler({ onPick }: { onPick: (lat: number, lng: number) => void }) {
  useMapEvents({ click: e => onPick(e.latlng.lat, e.latlng.lng) });
  return null;
}

function MapController({ flyTarget }: { flyTarget: [number, number] | null }) {
  const map = useMap();
  // نقشهٔ تازه‌مانت‌شده ابعادش را نگرفته؛ flyTo بی‌اثر می‌ماند تا invalidateSize صدا زده شود.
  useEffect(() => {
    map.invalidateSize();
  }, [map]);
  useEffect(() => {
    if (!flyTarget) return;
    map.invalidateSize();
    map.flyTo(flyTarget, 12, { duration: 1.2 });
  }, [flyTarget, map]);
  return null;
}

function MapPicker({ lat, lng, onChange, flyTarget }: {
  lat: number | null; lng: number | null;
  onChange: (lat: number, lng: number) => void;
  flyTarget?: [number, number] | null;
}) {
  const pos: [number, number] | null = lat !== null && lng !== null ? [lat, lng] : null;
  const center: [number, number] = pos ?? IRAN_CENTER;
  return (
    <div className="rounded-xl overflow-hidden border border-slate-200 dark:border-gray-700" style={{ height: 300 }}>
      <MapContainer center={center} zoom={pos ? 13 : 5} style={{ height: '100%', width: '100%' }}>
        <TileLayer
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          attribution='&copy; <a href="https://openstreetmap.org">OpenStreetMap</a>'
        />
        <MapClickHandler onPick={onChange} />
        <MapController flyTarget={flyTarget ?? null} />
        {pos && <Marker position={pos} />}
      </MapContainer>
    </div>
  );
}

// ── Searchable Select ──────────────────────────────────────────────────────

function SearchableSelect({ options, value, onChange, placeholder, disabled = false }: {
  options: { value: number; label: string }[];
  value: number | null;
  onChange: (value: number | null, label: string | null) => void;
  placeholder?: string;
  disabled?: boolean;
}) {
  const [open, setOpen]     = useState(false);
  const [q, setQ]           = useState('');
  const [rect, setRect]     = useState<DOMRect | null>(null);
  const btnRef              = useRef<HTMLButtonElement>(null);
  const dropRef             = useRef<HTMLDivElement>(null);

  const filtered = useMemo(
    () => q ? options.filter(o => o.label.includes(q)) : options,
    [options, q]
  );
  const selected = useMemo(() => options.find(o => o.value === value) ?? null, [options, value]);

  const openDropdown = () => {
    if (disabled || !btnRef.current) return;
    setRect(btnRef.current.getBoundingClientRect());
    setOpen(v => !v);
    setQ('');
  };

  useEffect(() => {
    if (!open) return;
    const onDown = (e: MouseEvent) => {
      if (
        dropRef.current && !dropRef.current.contains(e.target as Node) &&
        btnRef.current  && !btnRef.current.contains(e.target as Node)
      ) setOpen(false);
    };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);

  const dropStyle: React.CSSProperties = rect
    ? { position: 'fixed', top: rect.bottom + 4, left: rect.left, width: rect.width, zIndex: 9999 }
    : {};

  return (
    <div>
      <button
        ref={btnRef}
        type="button"
        disabled={disabled}
        onClick={openDropdown}
        className={`cp-input h-11 flex items-center justify-between text-right w-full ${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'} ${!selected ? 'text-slate-400 dark:text-slate-500' : ''}`}
      >
        <span className="truncate flex-1">{selected?.label ?? placeholder ?? 'انتخاب کنید'}</span>
        <ChevronDownIcon className={`w-4 h-4 shrink-0 mr-2 transition-transform ${open ? 'rotate-180' : ''}`} />
      </button>

      {open && createPortal(
        <div ref={dropRef} style={dropStyle}
          className="bg-white dark:bg-gray-900 border border-slate-200 dark:border-gray-700 rounded-xl shadow-2xl overflow-hidden">
          <div className="p-2 border-b border-slate-100 dark:border-gray-700">
            <input autoFocus type="text" value={q} onChange={e => setQ(e.target.value)}
              placeholder="جستجو..." className="cp-input text-sm h-8" />
          </div>
          <div className="max-h-52 overflow-y-auto">
            <button type="button"
              onClick={() => { onChange(null, null); setOpen(false); setQ(''); }}
              className="w-full text-right px-3 py-2 text-sm text-slate-400 hover:bg-slate-50 dark:hover:bg-gray-800">
              {placeholder ?? 'انتخاب کنید'}
            </button>
            {filtered.map(o => (
              <button key={o.value} type="button"
                onClick={() => { onChange(o.value, o.label); setOpen(false); setQ(''); }}
                className={`w-full text-right px-3 py-2 text-sm hover:bg-slate-50 dark:hover:bg-gray-800 ${value === o.value ? 'text-primary-600 dark:text-primary-400 font-medium bg-primary-50 dark:bg-primary-500/10' : 'text-slate-700 dark:text-slate-300'}`}>
                {o.label}
              </button>
            ))}
            {filtered.length === 0 && (
              <p className="text-xs text-slate-400 text-center py-3">نتیجه‌ای یافت نشد</p>
            )}
          </div>
        </div>,
        document.body
      )}
    </div>
  );
}

// ── Hierarchical Specialty Picker ──────────────────────────────────────────

function HierarchicalSpecialtyPicker({ selected, onChange, specialties }: {
  selected: number[]; onChange: (ids: number[]) => void;
  specialties: SpecialtyOpt[];
}) {
  const [q, setQ] = useState('');
  const [openParents, setOpenParents] = useState<Set<number>>(new Set());

  const parents  = useMemo(() => specialties.filter(s => s.parent_id === null), [specialties]);
  const childMap = useMemo(() => {
    const m = new Map<number, SpecialtyOpt[]>();
    specialties.filter(s => s.parent_id !== null).forEach(s => {
      const arr = m.get(s.parent_id!) ?? [];
      arr.push(s);
      m.set(s.parent_id!, arr);
    });
    return m;
  }, [specialties]);

  const filteredFlat = useMemo(() => q ? specialties.filter(s => s.name.includes(q)) : [], [q, specialties]);

  const toggleParent = (id: number) => setOpenParents(prev => {
    const n = new Set(prev);
    n.has(id) ? n.delete(id) : n.add(id);
    return n;
  });
  const toggleSelect = (id: number) =>
    onChange(selected.includes(id) ? selected.filter(x => x !== id) : [...selected, id]);

  const selectedLabels = useMemo(
    () => selected.map(id => ({ id, name: specialties.find(s => s.id === id)?.name ?? '' })).filter(x => x.name),
    [selected, specialties]
  );

  return (
    <div className="border border-slate-200 dark:border-gray-700 rounded-xl overflow-hidden">
      <div className="p-2 border-b border-slate-100 dark:border-gray-700 bg-slate-50 dark:bg-gray-800/60">
        <input type="text" value={q} onChange={e => setQ(e.target.value)} placeholder="جستجوی تخصص..."
          className="cp-input text-sm h-8" />
      </div>
      {selectedLabels.length > 0 && (
        <div className="px-3 py-2 border-b border-slate-100 dark:border-gray-700 flex flex-wrap gap-1">
          {selectedLabels.map(({ id, name }) => (
            <span key={id} className="inline-flex items-center gap-1 text-xs px-2.5 py-0.5 rounded-full bg-primary-100 dark:bg-primary-500/20 text-primary-700 dark:text-primary-300">
              {name}
              <button type="button" onClick={() => toggleSelect(id)} className="hover:text-primary-900 dark:hover:text-primary-100 leading-none">×</button>
            </span>
          ))}
        </div>
      )}
      <div className="max-h-56 overflow-y-auto">
        {q ? (
          filteredFlat.length > 0
            ? filteredFlat.map(s => (
                <label key={s.id} className="flex items-center gap-2.5 px-3 py-2 hover:bg-slate-50 dark:hover:bg-gray-800 cursor-pointer">
                  <input type="checkbox" checked={selected.includes(s.id)} onChange={() => toggleSelect(s.id)}
                    className="rounded border-slate-300 dark:border-gray-600 text-primary-600 shrink-0" />
                  <span className="text-sm text-slate-700 dark:text-slate-300">{s.name}</span>
                  {s.parent_id !== null && (
                    <span className="text-[10px] text-slate-400 mr-auto truncate max-w-[100px]">
                      {specialties.find(p => p.id === s.parent_id)?.name}
                    </span>
                  )}
                </label>
              ))
            : <p className="text-xs text-slate-400 text-center py-4">نتیجه‌ای یافت نشد</p>
        ) : (
          parents.map(parent => {
            const children = childMap.get(parent.id) ?? [];
            const isOpen   = openParents.has(parent.id);
            const numSel   = children.filter(c => selected.includes(c.id)).length;
            return (
              <div key={parent.id}>
                <div className="flex items-center gap-2 px-3 py-2.5 hover:bg-slate-50 dark:hover:bg-gray-800 cursor-pointer select-none"
                  onClick={() => children.length > 0 ? toggleParent(parent.id) : toggleSelect(parent.id)}>
                  {children.length > 0
                    ? (isOpen
                        ? <ChevronDownIcon  className="w-4 h-4 text-slate-400 shrink-0" />
                        : <ChevronRightIcon className="w-4 h-4 text-slate-400 shrink-0" />)
                    : <input type="checkbox" checked={selected.includes(parent.id)} readOnly
                        className="rounded border-slate-300 dark:border-gray-600 text-primary-600 pointer-events-none shrink-0" />
                  }
                  <span className={`text-sm font-medium ${numSel > 0 ? 'text-primary-600 dark:text-primary-400' : 'text-slate-700 dark:text-slate-300'}`}>
                    {parent.name}
                  </span>
                  {numSel > 0 && (
                    <span className="mr-auto text-[10px] bg-primary-100 dark:bg-primary-500/20 text-primary-700 dark:text-primary-300 px-1.5 py-0.5 rounded-full">
                      {numSel} انتخاب
                    </span>
                  )}
                </div>
                {isOpen && children.map(child => (
                  <label key={child.id} className="flex items-center gap-2.5 px-3 py-2 pr-9 hover:bg-slate-50 dark:hover:bg-gray-800 cursor-pointer">
                    <input type="checkbox" checked={selected.includes(child.id)} onChange={() => toggleSelect(child.id)}
                      className="rounded border-slate-300 dark:border-gray-600 text-primary-600 shrink-0" />
                    <span className="text-sm text-slate-600 dark:text-slate-400">{child.name}</span>
                  </label>
                ))}
              </div>
            );
          })
        )}
      </div>
    </div>
  );
}

// ── Services Picker — filtered by selected specialties ─────────────────────

function ServicesPicker({ selected, onChange, services, specialties, selectedSpecialtyIds }: {
  selected: number[]; onChange: (ids: number[]) => void;
  services: ServiceOpt[]; specialties: SpecialtyOpt[]; selectedSpecialtyIds: number[];
}) {
  const [q, setQ] = useState('');
  const [activeTab, setActiveTab] = useState<number | 'all'>('all');

  // Only services whose specialty is currently selected
  const relevantServices = useMemo(() => {
    if (selectedSpecialtyIds.length === 0) return [];
    return services.filter(s => s.specialty_id !== null && selectedSpecialtyIds.includes(s.specialty_id));
  }, [services, selectedSpecialtyIds]);

  // Specialties that actually have services
  const tabs = useMemo(() => {
    const specIds = [...new Set(relevantServices.map(s => s.specialty_id!))];
    return specIds.map(id => ({ id, name: specialties.find(s => s.id === id)?.name ?? String(id) }));
  }, [relevantServices, specialties]);

  const visibleServices = useMemo(() => {
    let pool = activeTab === 'all' ? relevantServices : relevantServices.filter(s => s.specialty_id === activeTab);
    return q ? pool.filter(s => s.name.includes(q)) : pool;
  }, [activeTab, relevantServices, q]);

  const toggle = (id: number) =>
    onChange(selected.includes(id) ? selected.filter(x => x !== id) : [...selected, id]);

  const selectedLabels = useMemo(
    () => selected.map(id => ({ id, name: services.find(s => s.id === id)?.name ?? '' })).filter(x => x.name),
    [selected, services]
  );

  if (selectedSpecialtyIds.length === 0) {
    return (
      <div className="rounded-xl border border-dashed border-slate-300 dark:border-gray-600 py-8 text-center text-slate-400 dark:text-slate-500 text-sm">
        ابتدا تخصص‌هایی را انتخاب کنید تا خدمات نمایش داده شوند
      </div>
    );
  }

  if (relevantServices.length === 0) {
    return (
      <div className="rounded-xl border border-dashed border-slate-300 dark:border-gray-600 py-8 text-center text-slate-400 dark:text-slate-500 text-sm">
        خدمتی برای تخصص‌های انتخاب‌شده یافت نشد
      </div>
    );
  }

  return (
    <div className="border border-slate-200 dark:border-gray-700 rounded-xl overflow-hidden">
      {tabs.length > 1 && (
        <div className="flex gap-1 px-2 py-2 border-b border-slate-100 dark:border-gray-700 bg-slate-50 dark:bg-gray-800/60 overflow-x-auto">
          {[{ id: 'all' as const, name: 'همه' }, ...tabs].map(t => (
            <button key={String(t.id)} type="button"
              onClick={() => setActiveTab(t.id as number | 'all')}
              className={`px-2.5 py-1 rounded-lg text-xs font-medium whitespace-nowrap transition-colors ${activeTab === t.id ? 'bg-primary-600 text-white' : 'text-slate-600 dark:text-slate-400 hover:bg-white dark:hover:bg-gray-700'}`}>
              {t.name}
            </button>
          ))}
        </div>
      )}
      <div className="p-2 border-b border-slate-100 dark:border-gray-700">
        <input type="text" value={q} onChange={e => setQ(e.target.value)} placeholder="جستجوی خدمت..."
          className="cp-input text-sm h-8" />
      </div>
      {selectedLabels.length > 0 && (
        <div className="px-3 py-2 border-b border-slate-100 dark:border-gray-700 flex flex-wrap gap-1">
          {selectedLabels.map(({ id, name }) => (
            <span key={id} className="inline-flex items-center gap-1 text-xs px-2.5 py-0.5 rounded-full bg-emerald-100 dark:bg-emerald-500/20 text-emerald-700 dark:text-emerald-300">
              {name}
              <button type="button" onClick={() => toggle(id)} className="hover:text-emerald-900 dark:hover:text-emerald-100 leading-none">×</button>
            </span>
          ))}
        </div>
      )}
      <div className="max-h-44 overflow-y-auto">
        {visibleServices.map(svc => (
          <label key={svc.id} className="flex items-center gap-2.5 px-3 py-2 hover:bg-slate-50 dark:hover:bg-gray-800 cursor-pointer">
            <input type="checkbox" checked={selected.includes(svc.id)} onChange={() => toggle(svc.id)}
              className="rounded border-slate-300 dark:border-gray-600 text-emerald-600 shrink-0" />
            <span className="text-sm text-slate-700 dark:text-slate-300">{svc.name}</span>
          </label>
        ))}
        {visibleServices.length === 0 && q && (
          <p className="text-xs text-slate-400 text-center py-4">نتیجه‌ای یافت نشد</p>
        )}
      </div>
    </div>
  );
}

// ── Address Modal ──────────────────────────────────────────────────────────

const addrSchema = z.object({
  name:        z.string().optional(),
  address:     z.string().min(1, 'آدرس کامل اجباری است'),
  telephone:   z.string().min(1, 'تلفن اجباری است').max(20),
  province_id: z.number().nullable().optional(),
  city_id:     z.number().nullable().optional(),
  latitude:    z.string().optional(),
  longitude:   z.string().optional(),
}).superRefine((data, ctx) => {
  if (!data.province_id) {
    ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'استان اجباری است', path: ['province_id'] });
  }
  if (!data.city_id) {
    ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'شهر اجباری است', path: ['city_id'] });
  }
});
type AddrForm = z.infer<typeof addrSchema>;

function AddressModal({ open, onClose, existing, doctorUuid, onSaved }: {
  open: boolean; onClose: () => void;
  existing: AddressData | null; doctorUuid: string;
  onSaved: () => void;
}) {
  const { register, handleSubmit, watch, setValue, reset, formState: { errors } } = useForm<AddrForm>({
    resolver: zodResolver(addrSchema),
  });
  const provinceId = watch('province_id');
  const cityId     = watch('city_id');
  const latStr     = watch('latitude');
  const lngStr     = watch('longitude');
  const lat = latStr ? parseFloat(latStr) : null;
  const lng = lngStr ? parseFloat(lngStr) : null;
  const [mapFlyTarget, setMapFlyTarget] = useState<[number, number] | null>(null);

  const provincesQ = useQuery({
    queryKey: ['provinces'],
    queryFn: () => api.get<ApiResponse<any>>('/api/v1/provinces'),
    staleTime: 600_000, enabled: open,
  });
  const citiesQ = useQuery({
    queryKey: ['cities', provinceId],
    queryFn: () => api.get<ApiResponse<any>>(`/api/v1/cities?province_id=${provinceId}`),
    enabled: open && !!provinceId, staleTime: 300_000,
  });

  const provinces: ProvinceOpt[] = useMemo(
    () => provincesQ.data?.data?.data ?? provincesQ.data?.data ?? [], [provincesQ.data]);
  const cities: CityOpt[] = useMemo(
    () => citiesQ.data?.data?.data ?? citiesQ.data?.data ?? [], [citiesQ.data]);

  useEffect(() => {
    if (open) {
      reset({
        name:        existing?.name ?? '',
        address:     existing?.address ?? '',
        telephone:   existing?.telephone ?? '',
        province_id: existing?.province ? Number(existing.province.id) : null,
        city_id:     existing?.city ? Number(existing.city.id) : null,
        latitude:    existing?.map?.latitude ?? '',
        longitude:   existing?.map?.longitude ?? '',
      });
    }
  }, [open, existing, reset]);

  const saveMut = useMutation({
    mutationFn: (values: AddrForm) => {
      const body: Record<string, unknown> = {
        name:        values.name || undefined,
        address:     values.address || undefined,
        telephone:   values.telephone || undefined,
        province_id: values.province_id ?? undefined,
        city_id:     values.city_id ?? undefined,
        latitude:    values.latitude ? parseFloat(values.latitude) : undefined,
        longitude:   values.longitude ? parseFloat(values.longitude) : undefined,
      };
      if (existing) return api.patch<ApiResponse<any>>(`/api/v1/clinic-pro/doctor-address/${existing.id}`, body);
      return api.post<ApiResponse<any>>('/api/v1/clinic-pro/doctor-address', { ...body, doctor_uuid: doctorUuid });
    },
    onSuccess: () => { toast.success(existing ? 'آدرس ویرایش شد' : 'آدرس اضافه شد'); onSaved(); onClose(); },
    onError:   (e: Error) => toast.error(e.message),
  });

  return (
    <Modal open={open} title={existing ? 'ویرایش آدرس' : 'افزودن آدرس جدید'} size="lg" onClose={onClose}
      footer={
        <>
          <button type="button" onClick={onClose} className="cp-btn-secondary px-5">لغو</button>
          <button form="addr-form" type="submit" disabled={saveMut.isPending} className="btn primary sm">
            {saveMut.isPending ? 'در حال ذخیره...' : 'ذخیره'}
          </button>
        </>
      }
    >
      <form id="addr-form" onSubmit={handleSubmit(v => saveMut.mutate(v))} className="space-y-4">

        {/* Row 1: Name + Phone */}
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
          <div>
            <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">نام مطب / کلینیک</label>
            <input type="text" className="input" placeholder="مثال: کلینیک مهر" {...register('name')} />
          </div>
          <div>
            <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
              تلفن <span className="text-red-500">*</span>
            </label>
            <input type="text" dir="ltr" className="cp-input text-left" placeholder="021..." {...register('telephone')} />
            {errors.telephone && <p className="text-xs text-red-500 mt-1">{errors.telephone.message}</p>}
          </div>
        </div>

        {/* Row 2: Full address */}
        <div>
          <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
            آدرس کامل <span className="text-red-500">*</span>
          </label>
          <textarea rows={2} className="cp-input resize-none" placeholder="خیابان، کوچه، پلاک..." {...register('address')} />
          {errors.address && <p className="text-xs text-red-500 mt-1">{errors.address.message}</p>}
        </div>

        {/* Row 3: Province + City side by side */}
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
          <div>
            <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
              استان <span className="text-red-500">*</span>
            </label>
            <SearchableSelect
              options={provinces.map(p => ({ value: p.id, label: p.name }))}
              value={provinceId ?? null}
              placeholder="انتخاب استان"
              onChange={(val) => {
                setValue('province_id', val);
                setValue('city_id', null);
                setMapFlyTarget(null);
              }}
            />
            {errors.province_id && <p className="text-xs text-red-500 mt-1">{errors.province_id.message}</p>}
          </div>
          <div>
            <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
              شهر <span className="text-red-500">*</span>
            </label>
            <SearchableSelect
              options={cities.map(c => ({ value: c.id, label: c.name }))}
              value={cityId ?? null}
              placeholder={provinceId ? 'انتخاب شهر' : 'ابتدا استان انتخاب کنید'}
              disabled={!provinceId}
              onChange={(val, label) => {
                setValue('city_id', val);
                if (label) {
                  geocodeCityInIran(label).then(coords => {
                    if (coords) setMapFlyTarget(coords);
                  });
                }
              }}
            />
            {errors.city_id && <p className="text-xs text-red-500 mt-1">{errors.city_id.message}</p>}
          </div>
        </div>

        {/* Row 4: Map */}
        <div>
          <div className="flex items-center justify-between mb-1.5">
            <label className="block text-sm font-medium text-slate-700 dark:text-slate-300">
              موقعیت روی نقشه
            </label>
            {lat && lng && (
              <span className="text-xs text-slate-500 dark:text-slate-400 font-mono" dir="ltr">
                {lat.toFixed(5)}, {lng.toFixed(5)}
              </span>
            )}
          </div>
          <p className="text-xs text-slate-400 dark:text-slate-500 mb-2">
            برای دقت بیشتر روی نقشه کلیک کنید
          </p>
          <MapPicker lat={lat} lng={lng} flyTarget={mapFlyTarget}
            onChange={(lt, ln) => { setValue('latitude', String(lt)); setValue('longitude', String(ln)); }} />
          {lat && lng && (
            <button type="button"
              onClick={() => { setValue('latitude', ''); setValue('longitude', ''); }}
              className="mt-2 text-xs text-red-500 hover:underline flex items-center gap-1">
              <XMarkIcon className="w-3 h-3" />حذف موقعیت
            </button>
          )}
        </div>
      </form>
    </Modal>
  );
}

// ── Address Card ───────────────────────────────────────────────────────────

function AddressCard({ addr, onEdit, onDelete, readOnly = false }: {
  addr: AddressData; onEdit: () => void; onDelete: () => void; readOnly?: boolean;
}) {
  return (
    <div className="flex items-start gap-3 p-4 rounded-xl bg-slate-50 dark:bg-gray-800/60 border border-slate-100 dark:border-gray-700 group">
      <div className="w-9 h-9 rounded-lg bg-white dark:bg-gray-700 shadow-sm flex items-center justify-center shrink-0 mt-0.5">
        <MapPinIcon className="w-4 h-4 text-slate-400" />
      </div>
      <div className="flex-1 min-w-0">
        {addr.name && <p className="text-sm font-semibold text-slate-800 dark:text-slate-200 mb-0.5">{addr.name}</p>}
        {addr.address && <p className="text-xs text-slate-600 dark:text-slate-400 leading-relaxed">{addr.address}</p>}
        <div className="flex flex-wrap gap-x-3 gap-y-1 mt-1.5">
          {addr.telephone && (
            <span className="text-xs text-slate-500 dark:text-slate-400 flex items-center gap-1 font-mono" dir="ltr">
              <PhoneIcon className="w-3 h-3" />{addr.telephone}
            </span>
          )}
          {addr.province && (
            <span className="text-xs text-slate-500 dark:text-slate-400">
              {addr.province.name}{addr.city ? ` — ${addr.city.name}` : ''}
            </span>
          )}
          {addr.map.latitude && addr.map.longitude && (
            <span className="text-xs text-slate-400 dark:text-slate-500 flex items-center gap-1" dir="ltr">
              <MapPinIcon className="w-3 h-3" />{parseFloat(addr.map.latitude).toFixed(4)}, {parseFloat(addr.map.longitude).toFixed(4)}
            </span>
          )}
        </div>
      </div>
      {!readOnly && (
        <div className="opacity-0 group-hover:opacity-100 flex items-center gap-1 transition-opacity shrink-0">
          <button onClick={onEdit}
            className="w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-500/10 transition-colors">
            <PencilIcon className="w-3.5 h-3.5" />
          </button>
          <button onClick={onDelete}
            className="w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
            <TrashIcon className="w-3.5 h-3.5" />
          </button>
        </div>
      )}
    </div>
  );
}

// ── Schedule components ────────────────────────────────────────────────────

// انتخاب ساعت و دقیقه به صورت دو منوی جدا (24 ساعته، دقیقه با گام 5)
const HOUR_VALUES   = Array.from({ length: 24 }, (_, h) => h.toString().padStart(2, '0'));
const MINUTE_VALUES = Array.from({ length: 12 }, (_, i) => (i * 5).toString().padStart(2, '0'));

function TimeSelect({ value, onChange }: { value: string; onChange: (v: string) => void }) {
  const [hRaw, mRaw] = (value || '00:00').split(':');
  const h = (hRaw ?? '00').padStart(2, '0');
  // دقیقه را به نزدیک‌ترین گام ۵ گرد کن تا همیشه در لیست موجود باشد
  const mNum = Math.round((Number(mRaw) || 0) / 5) * 5;
  const m = (mNum >= 60 ? 55 : mNum).toString().padStart(2, '0');

  return (
    <div className="cp-time-select" style={{ display: 'flex', alignItems: 'center', gap: 6 }} dir="ltr">
      <select className="cp-input" style={{ height: 38, width: 72, textAlign: 'center' }}
        value={h} onChange={e => onChange(`${e.target.value}:${m}`)}>
        {HOUR_VALUES.map(hv => <option key={hv} value={hv}>{hv}</option>)}
      </select>
      <span style={{ fontWeight: 700, color: 'var(--text-2)' }}>:</span>
      <select className="cp-input" style={{ height: 38, width: 72, textAlign: 'center' }}
        value={m} onChange={e => onChange(`${h}:${e.target.value}`)}>
        {MINUTE_VALUES.map(mv => <option key={mv} value={mv}>{mv}</option>)}
      </select>
    </div>
  );
}

function SlotEditor({ slots, onChange }: {
  slots: SlotConfig[];
  onChange: (slots: SlotConfig[]) => void;
}) {
  const add    = () => onChange([...slots, { start: '09:00', end: '13:00', duration: 30 }]);
  const remove = (i: number) => onChange(slots.filter((_, idx) => idx !== i));
  const update = (i: number, field: keyof SlotConfig, val: string | number) =>
    onChange(slots.map((s, idx) => idx === i ? { ...s, [field]: val } : s));

  return (
    <div className="space-y-2">
      {slots.map((slot, i) => (
        <div key={i} className="flex items-center gap-2 flex-wrap">
          <span className="text-xs text-slate-500 dark:text-slate-400 shrink-0">از</span>
          <input type="time" value={slot.start} onChange={e => update(i, 'start', e.target.value)}
            className="cp-input h-8 w-28 text-sm font-mono text-center px-2" />
          <span className="text-xs text-slate-500 dark:text-slate-400 shrink-0">تا</span>
          <input type="time" value={slot.end} onChange={e => update(i, 'end', e.target.value)}
            className="cp-input h-8 w-28 text-sm font-mono text-center px-2" />
          <div style={{ width: 110 }}>
            <GlobalSearchableSelect
              options={DURATION_OPTS.map(d => ({ value: d, label: `${d} دقیقه` }))}
              value={slot.duration}
              onChange={(v) => update(i, 'duration', Number(v))}
              height={32}
            />
          </div>
          <button type="button" onClick={() => remove(i)}
            className="w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors shrink-0">
            <XMarkIcon className="w-3.5 h-3.5" />
          </button>
        </div>
      ))}
      <button type="button" onClick={add}
        className="flex items-center gap-1.5 text-xs text-primary-600 dark:text-primary-400 hover:underline mt-1">
        <PlusIcon className="w-3.5 h-3.5" />افزودن بازه زمانی
      </button>
    </div>
  );
}

function SessionEditor({ session, onChange, onRemove, addresses }: {
  session: SessionConfig;
  onChange: (s: SessionConfig) => void;
  onRemove: () => void;
  addresses: AddressData[];
}) {
  const upd = <K extends keyof SessionConfig>(k: K, v: SessionConfig[K]) =>
    onChange({ ...session, [k]: v });
  const slotCount = calcSlotCount(session);

  return (
    <div style={{ border: '1px solid var(--border)', borderRadius: 'var(--r)', background: 'var(--surface)', overflow: 'hidden' }}>

      {/* Time inputs */}
      <div style={{ padding: '14px 16px 12px', display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))', gap: 12, alignItems: 'end' }}>
        <div>
          <div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>از ساعت</div>
          <TimeSelect value={session.start_time} onChange={v => upd('start_time', v)} />
        </div>
        <div>
          <div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>تا ساعت</div>
          <TimeSelect value={session.end_time} onChange={v => upd('end_time', v)} />
        </div>
        <button type="button" className="mini-btn danger" onClick={onRemove} style={{ marginBottom: 1 }}>
          <TrashIcon style={{ width: 15, height: 15 }} />
        </button>
      </div>

      {/* Duration + Location */}
      <div style={{ padding: '0 16px 14px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <div>
          <div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>بازه هر نوبت</div>
          <GlobalSearchableSelect
            options={DURATION_OPTS.map(d => ({ value: d, label: `${d} دقیقه` }))}
            value={session.duration_per_patient}
            onChange={(v) => upd('duration_per_patient', Number(v))}
          />
        </div>
        <div>
          <div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>مکان نوبت</div>
          <GlobalSearchableSelect
            options={[
              ...addresses.filter(a => a.type === 'personal').map(a => ({
                value: Number(a.id),
                label: `🏥 ${a.name ?? a.address ?? `مطب ${a.id}`}`,
              })),
              ...addresses.filter(a => a.type === 'clinic').map(a => ({
                value: Number(a.id),
                label: `🏨 ${a.clinic_name ? `${a.clinic_name}${a.name ? ` — ${a.name}` : ''}` : (a.name ?? a.address ?? `کلینیک ${a.id}`)}`,
              })),
            ]}
            value={session.location_id ?? null}
            onChange={(v) => upd('location_id', v ? Number(v) : null)}
            placeholder="انتخاب مکان"
            isClearable
          />
        </div>
      </div>

      {/* Rest toggle */}
      <div style={{
        padding: '12px 16px', display: 'flex', alignItems: 'center',
        justifyContent: 'space-between', borderTop: '1px solid var(--border)',
      }}>
        <div>
          <b style={{ fontSize: 13 }}>استراحت دوره‌ای بین نوبت‌ها</b>
          <div className="muted" style={{ fontSize: 12, marginTop: 2 }}>توقف خودکار پس از مدت کاری مشخص</div>
        </div>
        <button type="button" onClick={() => upd('has_rest', !session.has_rest)}
          style={{
            position: 'relative', width: 40, height: 22, borderRadius: 99,
            border: 'none', cursor: 'pointer', flexShrink: 0,
            background: session.has_rest ? 'var(--primary)' : 'var(--surface-3)',
            transition: 'background .2s',
          }}>
          <span style={{
            position: 'absolute', top: 3, borderRadius: '50%',
            width: 16, height: 16, background: '#fff',
            boxShadow: '0 1px 3px rgba(0,0,0,.25)',
            insetInlineStart: session.has_rest ? 20 : 3,
          }} />
        </button>
      </div>

      {/* Rest params */}
      {session.has_rest && (
        <div style={{ padding: '0 16px 14px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
          <div>
            <div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>هر (دقیقه کار)</div>
            <div className="field">
              <input type="number" min={1} value={session.rest_interval}
                onChange={e => upd('rest_interval', Number(e.target.value))} />
            </div>
          </div>
          <div>
            <div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>استراحت (دقیقه)</div>
            <div className="field">
              <input type="number" min={1} value={session.time_to_rest}
                onChange={e => upd('time_to_rest', Number(e.target.value))} />
            </div>
          </div>
        </div>
      )}

      {/* Slot count footer */}
      <div style={{
        padding: '10px 16px', display: 'flex', alignItems: 'center',
        justifyContent: 'space-between', borderTop: '1px solid var(--border)',
        background: 'var(--surface-2)',
      }}>
        <span className="muted" style={{ fontSize: 12 }}>تعداد نوبت محاسبه‌شده در این بازه</span>
        {slotCount > 0 ? (
          <span className="badge green">
            <CheckCircleIcon style={{ width: 13, height: 13 }} />
            {slotCount} نوبت
          </span>
        ) : (
          <span className="muted" style={{ fontSize: 12 }}>—</span>
        )}
      </div>
    </div>
  );
}

// ── Weekly Schedule Tab ────────────────────────────────────────────────────

function WeeklyScheduleTab({ doctorUuid, addresses, readOnly = false }: { doctorUuid: string; addresses: AddressData[]; readOnly?: boolean }) {
  const qc = useQueryClient();
  const [scheduleMap,  setScheduleMap]  = useState<NewScheduleMap>(EMPTY_NEW_SCHEDULE);
  const [scheduleUuid, setScheduleUuid] = useState<string | null>(null);
  const [expandedDay,  setExpandedDay]  = useState<string | null>(null);
  const [meta, setMeta] = useState<BookingMeta>(DEFAULT_BOOKING_META);

  const scheduleQ = useQuery({
    queryKey: ['doctor-schedule', doctorUuid],
    queryFn:  () => api.get<ApiResponse<any>>(`/api/v1/appointment-settings/weekly-schedule/${doctorUuid}`),
    staleTime: 0,
    retry: (count, err) => !(err instanceof ApiError && err.status === 404),
  });

  useEffect(() => {
    if (scheduleQ.data) {
      const d: WeeklyScheduleData = scheduleQ.data?.data?.data ?? scheduleQ.data?.data;
      if (d?.schedule) {
        const merged: NewScheduleMap = { ...EMPTY_NEW_SCHEDULE };
        for (const key of Object.keys(d.schedule)) {
          const raw = d.schedule[key] as any;
          if (raw?.sessions) merged[key] = { sessions: raw.sessions };
        }
        setScheduleMap(merged);
        setScheduleUuid(d.uuid);
      }
      if (d?.meta) setMeta({ ...DEFAULT_BOOKING_META, ...d.meta });
    } else if (scheduleQ.error instanceof ApiError && scheduleQ.error.status === 404) {
      setScheduleMap(EMPTY_NEW_SCHEDULE); setScheduleUuid(null);
    }
  }, [scheduleQ.data, scheduleQ.error]);

  const overlapDays = useMemo(() =>
    Object.fromEntries(SCHEDULE_DAYS.map(d => [d.key, hasOverlap(scheduleMap[d.key]?.sessions ?? [])]))
  , [scheduleMap]);
  const hasAnyOverlap = Object.values(overlapDays).some(Boolean);

  const totalSlots = useMemo(() =>
    SCHEDULE_DAYS.reduce((sum, d) => {
      const sessions = scheduleMap[d.key]?.sessions ?? [];
      return sum + sessions.filter(s => s.active).reduce((s2, s) => s2 + calcSlotCount(s), 0);
    }, 0)
  , [scheduleMap]);

  const missingLocation = addresses.length > 0 && SCHEDULE_DAYS.some(d =>
    (scheduleMap[d.key]?.sessions ?? []).some(s => s.active && s.location_id === null)
  );

  const saveMut = useMutation({
    mutationFn: () => {
      if (hasAnyOverlap) throw new Error('تداخل زمانی در برنامه وجود دارد');
      if (missingLocation) throw new Error('مکان مطب برای همه بازه‌های فعال الزامی است');
      return scheduleUuid
        ? api.patch<ApiResponse<any>>(`/api/v1/appointment-settings/weekly-schedule/${doctorUuid}`, { schedule: scheduleMap, meta })
        : api.post<ApiResponse<any>>('/api/v1/appointment-settings/weekly-schedule', { doctor_uuid: doctorUuid, schedule: scheduleMap, meta });
    },
    onSuccess: (res) => {
      const d: WeeklyScheduleData = res?.data?.data ?? res?.data;
      if (d?.uuid && !scheduleUuid) setScheduleUuid(d.uuid);
      toast.success('برنامه هفتگی ذخیره شد');
      qc.invalidateQueries({ queryKey: ['doctor-schedule', doctorUuid] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const setDaySessions = (key: string, sessions: SessionConfig[]) =>
    setScheduleMap(prev => ({ ...prev, [key]: { sessions } }));

  const addSession = (key: string) => {
    const existing = scheduleMap[key]?.sessions ?? [];
    const lastEnd  = existing.length > 0
      ? existing.reduce((max, s) => parseMinutes(s.end_time) > parseMinutes(max) ? s.end_time : max, '00:00')
      : '09:00';
    const newStart    = existing.length > 0 ? addMinutes(lastEnd, 30) : '09:00';
    const newEnd      = addMinutes(newStart, 240);
    const defaultLoc  = addresses.length > 0 ? Number(addresses[0].id) : null;
    setDaySessions(key, [...existing, { ...DEFAULT_SESSION, start_time: newStart, end_time: newEnd, location_id: defaultLoc }]);
    setExpandedDay(key);
  };

  const removeSession = (key: string, idx: number) =>
    setDaySessions(key, (scheduleMap[key]?.sessions ?? []).filter((_, i) => i !== idx));

  const updateSession = (key: string, idx: number, s: SessionConfig) =>
    setDaySessions(key, (scheduleMap[key]?.sessions ?? []).map((old, i) => i === idx ? s : old));

  if (scheduleQ.isLoading) return (
    <div className="space-y-3">{Array.from({ length: 4 }).map((_, i) => <div key={i} className="h-12 rounded-xl skeleton" />)}</div>
  );

  if (addresses.length === 0) return (
    <div className="flex flex-col items-center justify-center gap-3 py-12 text-center">
      <div className="w-12 h-12 rounded-full bg-amber-50 dark:bg-amber-500/10 border border-amber-200 dark:border-amber-500/30 flex items-center justify-center">
        <MapPinIcon className="w-6 h-6 text-amber-500" />
      </div>
      <div>
        <p className="text-sm font-medium text-slate-700 dark:text-slate-200">ابتدا آدرس مطب را ثبت کنید</p>
        <p className="text-xs text-slate-400 dark:text-slate-500 mt-1">برنامه کاری نیاز به حداقل یک مکان نوبت دارد</p>
      </div>
    </div>
  );

  // نمای فقط‌خواندنی برای نماینده: برنامه‌ی هفتگی به‌صورت متن، بدون فرم.
  if (readOnly) {
    if (scheduleQ.isLoading) {
      return <div className="space-y-2">{Array.from({ length: 4 }).map((_, i) => <div key={i} className="h-12 rounded-xl skeleton" />)}</div>;
    }
    return (
      <div className="space-y-2">
        {totalSlots > 0 && (
          <div className="flex items-center gap-2 mb-3 p-3 rounded-xl bg-emerald-50 dark:bg-emerald-500/10 border border-emerald-200 dark:border-emerald-500/30">
            <CalendarIcon className="w-4 h-4 text-emerald-600 dark:text-emerald-400 shrink-0" />
            <span className="text-sm text-emerald-700 dark:text-emerald-300">
              مجموع <strong>{totalSlots}</strong> نوبت در هفته
            </span>
          </div>
        )}
        {SCHEDULE_DAYS.map(day => {
          const activeSessions = (scheduleMap[day.key]?.sessions ?? []).filter(s => s.active);
          const daySlots = activeSessions.reduce((sum, s) => sum + calcSlotCount(s), 0);
          return (
            <div key={day.key} className="flex items-center gap-3 px-4 py-3 rounded-xl border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800/40">
              <b style={{ fontSize: 14, minWidth: 56, flexShrink: 0 }}>{day.label}</b>
              <div className="flex-1 flex gap-2 items-center flex-wrap min-w-0">
                {activeSessions.length > 0 ? activeSessions.map((s, i) => (
                  <span key={i} className="text-xs px-2 py-1 rounded-lg bg-slate-100 dark:bg-gray-700 text-slate-700 dark:text-slate-200" style={{ fontFamily: 'monospace', direction: 'ltr', whiteSpace: 'nowrap' }}>
                    {s.start_time} – {s.end_time}
                  </span>
                )) : (
                  <span className="muted text-xs">تعطیل</span>
                )}
              </div>
              {daySlots > 0 && (
                <span className="badge green" style={{ flexShrink: 0 }}>
                  <span className="bdot" />{daySlots} نوبت
                </span>
              )}
            </div>
          );
        })}
      </div>
    );
  }

  return (
    <div className="space-y-2">
      {/* ─ خلاصه کل هفته */}
      {totalSlots > 0 && (
        <div className="flex items-center gap-2 mb-3 p-3 rounded-xl bg-emerald-50 dark:bg-emerald-500/10 border border-emerald-200 dark:border-emerald-500/30">
          <CalendarIcon className="w-4 h-4 text-emerald-600 dark:text-emerald-400 shrink-0" />
          <span className="text-sm text-emerald-700 dark:text-emerald-300">
            مجموع <strong>{totalSlots}</strong> نوبت در هفته
          </span>
        </div>
      )}

      {/* ─ نوبت‌دهی آنلاین */}
      <div className="mb-3 rounded-xl border border-slate-200 dark:border-gray-700 overflow-hidden">
        {/* header + toggle */}
        <label className="flex items-center justify-between gap-3 px-4 py-3 cursor-pointer bg-slate-50 dark:bg-gray-800/50">
          <div className="flex items-center gap-2">
            <GlobeAltIcon className="w-4 h-4 text-slate-500 dark:text-slate-400 shrink-0" />
            <span className="text-sm font-medium text-slate-700 dark:text-slate-200">نوبت‌دهی آنلاین</span>
          </div>
          <span className="relative inline-flex shrink-0">
            <input
              type="checkbox"
              className="peer sr-only"
              checked={meta.online_booking_enabled}
              onChange={(e) => setMeta(m => ({ ...m, online_booking_enabled: e.target.checked }))}
            />
            <span className="w-10 h-6 rounded-full bg-slate-300 dark:bg-gray-600 transition-colors peer-checked:bg-emerald-500" />
            <span className="absolute top-0.5 right-0.5 w-5 h-5 rounded-full bg-white shadow transition-transform peer-checked:-translate-x-4" />
          </span>
        </label>

        {/* booking window control */}
        <div className={`px-4 py-3 transition-opacity ${meta.online_booking_enabled ? '' : 'opacity-50 pointer-events-none'}`}>
          <div className="flex flex-wrap items-center gap-2">
            <span className="text-sm text-slate-600 dark:text-slate-400">رزرو آنلاین تا</span>
            <div className="flex items-stretch rounded-lg border border-slate-200 dark:border-gray-700 overflow-hidden bg-white dark:bg-gray-900">
              <input
                type="number"
                min={1}
                value={meta.booking_window_value}
                disabled={!meta.online_booking_enabled}
                onChange={(e) => setMeta(m => ({ ...m, booking_window_value: Math.max(1, Number(e.target.value) || 1) }))}
                className="w-14 text-center text-sm bg-transparent border-0 focus:outline-none focus:ring-0 px-2 py-1.5"
              />
              <select
                value={meta.booking_window_unit}
                disabled={!meta.online_booking_enabled}
                onChange={(e) => setMeta(m => ({ ...m, booking_window_unit: e.target.value as 'week' | 'month' }))}
                className="text-sm bg-slate-50 dark:bg-gray-800 border-0 border-r border-slate-200 dark:border-gray-700 focus:outline-none focus:ring-0 px-2 py-1.5"
              >
                <option value="week">هفته</option>
                <option value="month">ماه</option>
              </select>
            </div>
            <span className="text-sm text-slate-600 dark:text-slate-400">آینده</span>
          </div>
          <p className="text-xs text-slate-400 dark:text-slate-500 mt-2 leading-relaxed">
            بیمار فقط تا این بازه می‌تواند آنلاین نوبت بگیرد؛ روزهای بعد از آن روی تقویم غیرفعال‌اند.
          </p>
        </div>
      </div>

      {SCHEDULE_DAYS.map(day => {
        const sessions       = scheduleMap[day.key]?.sessions ?? [];
        const isOverlap      = overlapDays[day.key];
        const isExpanded     = expandedDay === day.key;
        const activeSessions = sessions.filter(s => s.active);
        const daySlots       = activeSessions.reduce((sum, s) => sum + calcSlotCount(s), 0);

        return (
          <div key={day.key} className="card" style={{ overflow: 'hidden' }}>
            {/* Day header */}
            <div
              style={{ padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer', userSelect: 'none' }}
              onClick={() => setExpandedDay(isExpanded ? null : day.key)}
            >
              <b style={{ fontSize: 14, minWidth: 56, flexShrink: 0 }}>{day.label}</b>
              <div style={{ flex: 1, display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap', minWidth: 0 }}>
                {activeSessions.map((s, i) => (
                  <span key={i} style={{ fontSize: 13, fontFamily: 'monospace', color: 'var(--text-2)', direction: 'ltr', whiteSpace: 'nowrap' }}>
                    {s.start_time} — {s.end_time}
                  </span>
                ))}
                {sessions.length === 0 && <span className="muted" style={{ fontSize: 12 }}>تعطیل</span>}
              </div>
              {daySlots > 0 && (
                <span className="badge green" style={{ flexShrink: 0 }}>
                  <span className="bdot" />{daySlots} نوبت
                </span>
              )}
              {isOverlap && (
                <span className="badge red" style={{ flexShrink: 0 }}>
                  <ExclamationTriangleIcon style={{ width: 12, height: 12 }} />تداخل
                </span>
              )}
              <button type="button" className="btn ghost sm" style={{ flexShrink: 0 }}
                onClick={e => { e.stopPropagation(); addSession(day.key); }}>
                <PlusIcon style={{ width: 13, height: 13 }} />
                بازه
              </button>
              <ChevronDownIcon style={{
                width: 16, height: 16, flexShrink: 0, color: 'var(--text-3)',
                transform: isExpanded ? 'rotate(180deg)' : 'none',
                transition: 'transform .2s',
              }} />
            </div>

            {/* Expanded sessions */}
            {isExpanded && (
              <div style={{ borderTop: '1px solid var(--border)', padding: '12px 16px 16px', display: 'flex', flexDirection: 'column', gap: 10 }}>
                {sessions.length === 0 ? (
                  <div style={{ textAlign: 'center', padding: '16px 0' }}>
                    <p className="muted" style={{ fontSize: 13, marginBottom: 10 }}>هیچ بازه‌ای برای این روز تنظیم نشده</p>
                    <button type="button" onClick={() => addSession(day.key)} className="btn ghost sm">
                      <PlusIcon style={{ width: 14, height: 14 }} />
                      افزودن اولین بازه
                    </button>
                  </div>
                ) : sessions.map((session, idx) => (
                  <SessionEditor key={idx} session={session} addresses={addresses}
                    onChange={s => updateSession(day.key, idx, s)}
                    onRemove={() => removeSession(day.key, idx)} />
                ))}
              </div>
            )}
          </div>
        );
      })}

      <div className="flex items-center justify-between pt-3">
        {(hasAnyOverlap || missingLocation) && (
          <p className="text-xs text-red-500 dark:text-red-400 flex items-center gap-1">
            <ExclamationTriangleIcon className="w-3.5 h-3.5" />
            {hasAnyOverlap ? 'تداخل زمانی در برنامه وجود دارد' : 'مکان مطب برای همه بازه‌ها الزامی است'}
          </p>
        )}
        {!readOnly && (
          <button type="button" onClick={() => saveMut.mutate()}
            disabled={saveMut.isPending || hasAnyOverlap || missingLocation}
            className="btn primary sm" style={{ marginInlineStart: 'auto', opacity: (saveMut.isPending || hasAnyOverlap || missingLocation) ? 0.5 : 1 }}>
            {saveMut.isPending ? 'در حال ذخیره...' : 'ذخیره برنامه هفتگی'}
          </button>
        )}
      </div>
    </div>
  );
}

// ── Date Override Modal ────────────────────────────────────────────────────

function DateOverrideModal({ open, onClose, existing, doctorUuid, onSaved, addresses }: {
  open: boolean; onClose: () => void;
  existing: DateOverrideData | null; doctorUuid: string;
  onSaved: () => void; addresses: AddressData[];
}) {
  const [dateStr,      setDateStr]      = useState('');
  const [overrideType, setOverrideType] = useState<'closed' | 'custom'>('closed');
  const [reason,       setReason]       = useState('');
  const [slots,        setSlots]        = useState<SessionConfig[]>([]);

  const toSession = useCallback((s: any): SessionConfig => ({
    active:               true,
    start_time:           s.start_time ?? s.start ?? '09:00',
    end_time:             s.end_time   ?? s.end   ?? '13:00',
    duration_per_patient: s.duration_per_patient ?? s.duration ?? 20,
    location_id:          s.location_id ?? null,
    has_rest:             s.has_rest    ?? false,
    rest_interval:        s.rest_interval ?? 60,
    time_to_rest:         s.time_to_rest  ?? 10,
    patient_limit:        s.patient_limit ?? null,
  }), []);

  useEffect(() => {
    if (open) {
      if (existing) {
        setDateStr(tsToDate(existing.date));
        setOverrideType(existing.active ? 'custom' : 'closed');
        setReason(existing.reason ?? '');
        setSlots((existing.custom_slots ?? []).map(toSession));
      } else {
        setDateStr(new Date().toISOString().slice(0, 10));
        setOverrideType('closed'); setReason(''); setSlots([]);
      }
    }
  }, [open, existing, toSession]);

  const saveMut = useMutation({
    mutationFn: () => {
      const active = overrideType === 'custom';
      const body   = { date: dateStr, active, reason: reason || undefined, custom_slots: active ? slots : [] };
      if (existing)
        return api.patch<ApiResponse<any>>(`/api/v1/appointment-settings/date-override/${existing.uuid}`, body);
      return api.post<ApiResponse<any>>('/api/v1/appointment-settings/date-override', { ...body, doctor_uuid: doctorUuid });
    },
    onSuccess: () => { toast.success(existing ? 'ویرایش شد' : 'تاریخ خاص اضافه شد'); onSaved(); onClose(); },
    onError: (e: Error) => toast.error(e.message),
  });

  return (
    <Modal open={open} title={existing ? 'ویرایش تاریخ خاص' : 'افزودن تاریخ خاص'} size="md" onClose={onClose}
      footer={
        <>
          <button type="button" onClick={onClose} className="cp-btn-secondary px-5">لغو</button>
          <button type="button" onClick={() => saveMut.mutate()} disabled={saveMut.isPending || !dateStr}
            className="btn primary sm">
            {saveMut.isPending ? 'در حال ذخیره...' : 'ذخیره'}
          </button>
        </>
      }
    >
      <div className="space-y-4">
        <div>
          <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">تاریخ</label>
          <PersianDateInput value={dateStr} onChange={setDateStr} placeholder="انتخاب تاریخ" />
        </div>

        {/* ─ نوع override */}
        <div>
          <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">نوع این روز</label>
          <div className="grid grid-cols-2 gap-2">
            <button type="button"
              onClick={() => setOverrideType('closed')}
              className={`flex items-center gap-2 p-3 rounded-xl border-2 text-sm transition-colors text-right ${overrideType === 'closed' ? 'border-red-400 bg-red-50 dark:bg-red-500/10 text-red-700 dark:text-red-300' : 'border-slate-200 dark:border-gray-700 text-slate-600 dark:text-slate-400 hover:border-slate-300'}`}>
              <XCircleIcon className="w-5 h-5 shrink-0" />
              <div>
                <p className="font-medium leading-tight">تعطیل است</p>
                <p className="text-xs opacity-70 mt-0.5">هیچ نوبتی داده نمی‌شود</p>
              </div>
            </button>
            <button type="button"
              onClick={() => setOverrideType('custom')}
              className={`flex items-center gap-2 p-3 rounded-xl border-2 text-sm transition-colors text-right ${overrideType === 'custom' ? 'border-amber-400 bg-amber-50 dark:bg-amber-500/10 text-amber-700 dark:text-amber-300' : 'border-slate-200 dark:border-gray-700 text-slate-600 dark:text-slate-400 hover:border-slate-300'}`}>
              <CalendarIcon className="w-5 h-5 shrink-0" />
              <div>
                <p className="font-medium leading-tight">ساعات خاص</p>
                <p className="text-xs opacity-70 mt-0.5">جایگزین برنامه هفتگی</p>
              </div>
            </button>
          </div>
        </div>

        <div>
          <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">دلیل (اختیاری)</label>
          <input type="text" value={reason} onChange={e => setReason(e.target.value)}
            placeholder={overrideType === 'closed' ? 'مثال: سفر، مریضی، کنگره...' : 'مثال: شیفت اضطراری، ویزیت خاص...'}
            className="input" />
        </div>

        {overrideType === 'custom' && (
          <div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
              <label style={{ fontSize: 13, fontWeight: 600, color: 'var(--text-2)' }}>بازه‌های زمانی</label>
              <button type="button" className="btn ghost sm"
                onClick={() => setSlots(prev => [...prev, {
                  ...DEFAULT_SESSION,
                  location_id: addresses[0] ? Number(addresses[0].id) : null,
                }])}>
                <PlusIcon style={{ width: 13, height: 13 }} />
                افزودن بازه
              </button>
            </div>
            {slots.length === 0 ? (
              <p className="muted" style={{ fontSize: 12 }}>اگر خالی بماند از برنامه هفتگی استفاده می‌شود</p>
            ) : (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                {slots.map((session, idx) => (
                  <SessionEditor key={idx} session={session} addresses={addresses}
                    onChange={s => setSlots(prev => prev.map((old, i) => i === idx ? s : old))}
                    onRemove={() => setSlots(prev => prev.filter((_, i) => i !== idx))} />
                ))}
              </div>
            )}
          </div>
        )}

        {overrideType === 'closed' && (
          <div className="flex items-start gap-2 p-3 rounded-xl bg-red-50 dark:bg-red-500/10 border border-red-200 dark:border-red-500/30">
            <ExclamationTriangleIcon className="w-4 h-4 text-red-500 shrink-0 mt-0.5" />
            <p className="text-xs text-red-700 dark:text-red-400">
              در این روز هیچ نوبتی داده نخواهد شد، حتی اگر در برنامه هفتگی ساعات کاری داشته باشید.
            </p>
          </div>
        )}
      </div>
    </Modal>
  );
}

// ── Date Overrides Tab ─────────────────────────────────────────────────────

function DateOverridesTab({ doctorUuid, addresses, readOnly = false }: { doctorUuid: string; addresses: AddressData[]; readOnly?: boolean }) {
  const qc = useQueryClient();
  const [modalOpen,    setModalOpen]    = useState(false);
  const [editing,      setEditing]      = useState<DateOverrideData | null>(null);
  const [deletingUuid, setDeletingUuid] = useState<string | null>(null);

  const listQ = useQuery({
    queryKey: ['doctor-overrides', doctorUuid],
    queryFn:  () => api.get<ApiResponse<any>>(`/api/v1/appointment-settings/date-override/list/${doctorUuid}`),
    staleTime: 0,
  });
  const overrides: DateOverrideData[] = useMemo(
    () => listQ.data?.data?.data ?? listQ.data?.data ?? [], [listQ.data]
  );

  const deleteMut = useMutation({
    mutationFn: (uuid: string) => api.delete<ApiResponse<any>>(`/api/v1/appointment-settings/date-override/${uuid}`),
    onSuccess: () => { toast.success('حذف شد'); setDeletingUuid(null); qc.invalidateQueries({ queryKey: ['doctor-overrides', doctorUuid] }); },
    onError: (e: Error) => toast.error(e.message),
  });

  if (listQ.isLoading) return (
    <div className="space-y-2">{Array.from({ length: 3 }).map((_, i) => <div key={i} className="h-14 rounded-xl skeleton" />)}</div>
  );

  if (addresses.length === 0) return (
    <div className="flex flex-col items-center justify-center gap-3 py-12 text-center">
      <div className="w-12 h-12 rounded-full bg-amber-50 dark:bg-amber-500/10 border border-amber-200 dark:border-amber-500/30 flex items-center justify-center">
        <MapPinIcon className="w-6 h-6 text-amber-500" />
      </div>
      <div>
        <p className="text-sm font-medium text-slate-700 dark:text-slate-200">ابتدا آدرس مطب را ثبت کنید</p>
        <p className="text-xs text-slate-400 dark:text-slate-500 mt-1">برنامه کاری نیاز به حداقل یک مکان نوبت دارد</p>
      </div>
    </div>
  );

  return (
    <div className="space-y-3">
      {!readOnly && (
        <div className="flex justify-end">
          <button type="button" onClick={() => { setEditing(null); setModalOpen(true); }}
            className="flex items-center gap-1.5 text-sm cp-btn-primary">
            <PlusIcon className="w-4 h-4" />تاریخ خاص جدید
          </button>
        </div>
      )}
      {overrides.length === 0 ? (
        <div className="text-center py-10 text-slate-400 dark:text-slate-500">
          <CalendarIcon className="w-10 h-10 mx-auto mb-2 opacity-30" />
          <p className="text-sm">هیچ تاریخ خاصی تنظیم نشده</p>
        </div>
      ) : (
        <div className="space-y-2">
          {overrides.map(ov => (
            <div key={ov.uuid} className="flex items-center gap-3 p-3.5 rounded-xl border border-slate-200 dark:border-gray-700 bg-white dark:bg-gray-800/60">
              <div className="flex-1 min-w-0">
                <div className="flex items-center gap-2 flex-wrap">
                  <span className="text-sm font-medium text-slate-800 dark:text-slate-200">{formatPersianDate(tsToDate(ov.date))}</span>
                  {ov.active ? (
                    <span className="text-xs px-2 py-0.5 rounded-full font-medium bg-amber-100 dark:bg-amber-500/20 text-amber-700 dark:text-amber-300">
                      {ov.custom_slots.length > 0 ? `${ov.custom_slots.length} بازه سفارشی` : 'ساعات خاص'}
                    </span>
                  ) : (
                    <span className="text-xs px-2 py-0.5 rounded-full font-medium bg-red-100 dark:bg-red-500/20 text-red-600 dark:text-red-400">
                      تعطیل
                    </span>
                  )}
                </div>
                {ov.reason && <p className="text-xs text-slate-400 dark:text-slate-500 mt-0.5">{ov.reason}</p>}
              </div>
              {!readOnly && (
                <div className="flex items-center gap-1 shrink-0">
                  <button onClick={() => { setEditing(ov); setModalOpen(true); }}
                    className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-500/10 transition-colors">
                    <PencilIcon className="w-3.5 h-3.5" />
                  </button>
                  <button onClick={() => setDeletingUuid(ov.uuid)}
                    className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
                    <TrashIcon className="w-3.5 h-3.5" />
                  </button>
                </div>
              )}
            </div>
          ))}
        </div>
      )}
      <DateOverrideModal open={modalOpen} onClose={() => { setModalOpen(false); setEditing(null); }}
        existing={editing} doctorUuid={doctorUuid} addresses={addresses}
        onSaved={() => qc.invalidateQueries({ queryKey: ['doctor-overrides', doctorUuid] })} />
      <ConfirmDialog open={!!deletingUuid} title="حذف تاریخ خاص" message="آیا از حذف این تاریخ خاص اطمینان دارید؟"
        confirmLabel="بله، حذف کن" danger loading={deleteMut.isPending}
        onConfirm={() => deletingUuid && deleteMut.mutate(deletingUuid)} onCancel={() => setDeletingUuid(null)} />
    </div>
  );
}

// ── Holiday Modal ──────────────────────────────────────────────────────────

function HolidayModal({ open, onClose, existing, doctorUuid, onSaved }: {
  open: boolean; onClose: () => void;
  existing: HolidayData | null; doctorUuid: string;
  onSaved: () => void;
}) {
  const [startDate, setStartDate] = useState('');
  const [endDate,   setEndDate]   = useState('');
  const [reason,    setReason]    = useState('');

  useEffect(() => {
    if (open) {
      if (existing) {
        setStartDate(tsToDate(existing.start_date));
        setEndDate(tsToDate(existing.end_date));
        setReason(existing.reason ?? '');
      } else {
        const today = new Date().toISOString().slice(0, 10);
        setStartDate(today); setEndDate(today); setReason('');
      }
    }
  }, [open, existing]);

  const invalid = !startDate || !endDate || endDate < startDate;

  const saveMut = useMutation({
    mutationFn: () => {
      const body = { start_date: startDate, end_date: endDate, reason: reason || undefined };
      if (existing)
        return api.patch<ApiResponse<any>>(`/api/v1/appointment-settings/holidays/${existing.uuid}`, body);
      return api.post<ApiResponse<any>>('/api/v1/appointment-settings/holidays', { ...body, doctor_uuid: doctorUuid });
    },
    onSuccess: () => { toast.success(existing ? 'تعطیلات ویرایش شد' : 'تعطیلات اضافه شد'); onSaved(); onClose(); },
    onError: (e: Error) => toast.error(e.message),
  });

  return (
    <Modal open={open} title={existing ? 'ویرایش تعطیلات' : 'افزودن تعطیلات'} size="md" onClose={onClose}
      footer={
        <>
          <button type="button" onClick={onClose} className="cp-btn-secondary px-5">لغو</button>
          <button type="button" onClick={() => saveMut.mutate()} disabled={saveMut.isPending || invalid}
            className="btn primary sm">
            {saveMut.isPending ? 'در حال ذخیره...' : 'ذخیره'}
          </button>
        </>
      }
    >
      <div className="space-y-4">
        <div className="grid grid-cols-2 gap-3">
          <div>
            <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">از تاریخ</label>
            <PersianDateInput value={startDate} onChange={v => { setStartDate(v); if (endDate && v > endDate) setEndDate(v); }} />
          </div>
          <div>
            <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">تا تاریخ</label>
            <PersianDateInput value={endDate} onChange={setEndDate} minDate={startDate} />
          </div>
        </div>
        {endDate && startDate && endDate < startDate && (
          <p className="text-xs text-red-500">تاریخ پایان نمی‌تواند قبل از تاریخ شروع باشد</p>
        )}
        <div>
          <label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">دلیل (اختیاری)</label>
          <input type="text" value={reason} onChange={e => setReason(e.target.value)}
            placeholder="مثال: سفر، کنگره پزشکی..." className="input" />
        </div>
      </div>
    </Modal>
  );
}

// ── Holidays Tab ───────────────────────────────────────────────────────────

function HolidaysTab({ doctorUuid, readOnly = false }: { doctorUuid: string; readOnly?: boolean }) {
  const qc = useQueryClient();
  const [modalOpen,    setModalOpen]    = useState(false);
  const [editing,      setEditing]      = useState<HolidayData | null>(null);
  const [deletingUuid, setDeletingUuid] = useState<string | null>(null);

  const listQ = useQuery({
    queryKey: ['doctor-holidays', doctorUuid],
    queryFn:  () => api.get<ApiResponse<any>>(`/api/v1/appointment-settings/holidays/list/${doctorUuid}`),
    staleTime: 0,
  });
  const holidays: HolidayData[] = useMemo(
    () => listQ.data?.data?.data ?? listQ.data?.data ?? [], [listQ.data]
  );

  const deleteMut = useMutation({
    mutationFn: (uuid: string) => api.delete<ApiResponse<any>>(`/api/v1/appointment-settings/holidays/${uuid}`),
    onSuccess: () => { toast.success('حذف شد'); setDeletingUuid(null); qc.invalidateQueries({ queryKey: ['doctor-holidays', doctorUuid] }); },
    onError: (e: Error) => toast.error(e.message),
  });

  const toggleMut = useMutation({
    mutationFn: (h: HolidayData) => api.patch<ApiResponse<any>>(`/api/v1/appointment-settings/holidays/${h.uuid}`, { active: !h.active }),
    onSuccess: () => { toast.success('وضعیت بروز شد'); qc.invalidateQueries({ queryKey: ['doctor-holidays', doctorUuid] }); },
    onError: (e: Error) => toast.error(e.message),
  });

  if (listQ.isLoading) return (
    <div className="space-y-2">{Array.from({ length: 3 }).map((_, i) => <div key={i} className="h-14 rounded-xl skeleton" />)}</div>
  );

  return (
    <div className="space-y-3">
      {!readOnly && (
        <div className="flex justify-end">
          <button type="button" onClick={() => { setEditing(null); setModalOpen(true); }}
            className="flex items-center gap-1.5 text-sm cp-btn-primary">
            <PlusIcon className="w-4 h-4" />تعطیلات جدید
          </button>
        </div>
      )}
      {holidays.length === 0 ? (
        <div className="text-center py-10 text-slate-400 dark:text-slate-500">
          <CalendarIcon className="w-10 h-10 mx-auto mb-2 opacity-30" />
          <p className="text-sm">هیچ تعطیلاتی ثبت نشده</p>
        </div>
      ) : (
        <div className="space-y-2">
          {holidays.map(h => {
            const now       = Math.floor(Date.now() / 1000);
            const isPast    = h.end_date < now;
            const isCurrent = h.start_date <= now && h.end_date >= now;
            const sameDay   = tsToDate(h.start_date) === tsToDate(h.end_date);
            return (
              <div key={h.uuid} className={`flex items-center gap-3 p-3.5 rounded-xl border transition-colors ${isCurrent ? 'border-red-200 dark:border-red-500/30 bg-red-50/40 dark:bg-red-500/5' : isPast ? 'border-slate-200 dark:border-gray-700 bg-slate-50/40 dark:bg-gray-800/30 opacity-70' : 'border-orange-200 dark:border-orange-500/30 bg-orange-50/30 dark:bg-orange-500/5'}`}>
                <div className="flex-1 min-w-0">
                  <div className="flex items-center gap-2 flex-wrap">
                    <span className={`text-sm font-medium ${isPast ? 'text-slate-500 dark:text-slate-400' : 'text-slate-800 dark:text-slate-200'}`}>
                      {sameDay ? formatPersianDate(tsToDate(h.start_date)) : `${formatPersianDate(tsToDate(h.start_date))} تا ${formatPersianDate(tsToDate(h.end_date))}`}
                    </span>
                    {isCurrent && h.active && (
                      <span className="text-xs px-2 py-0.5 rounded-full font-medium bg-red-100 dark:bg-red-500/20 text-red-600 dark:text-red-400 animate-pulse">در جریان</span>
                    )}
                    {!isCurrent && !isPast && h.active && (
                      <span className="text-xs px-2 py-0.5 rounded-full font-medium bg-orange-100 dark:bg-orange-500/20 text-orange-600 dark:text-orange-400">آینده</span>
                    )}
                    {isPast && (
                      <span className="text-xs px-2 py-0.5 rounded-full bg-slate-100 dark:bg-gray-700 text-slate-400 dark:text-slate-500">گذشته</span>
                    )}
                    {!h.active && (
                      <span className="text-xs px-2 py-0.5 rounded-full bg-slate-100 dark:bg-gray-700 text-slate-400 dark:text-slate-500">غیرفعال</span>
                    )}
                  </div>
                  {h.reason && <p className="text-xs text-slate-400 dark:text-slate-500 mt-0.5">{h.reason}</p>}
                </div>
                {!readOnly && (
                  <div className="flex items-center gap-1 shrink-0">
                    <button onClick={() => toggleMut.mutate(h)} disabled={toggleMut.isPending}
                      className={`text-xs px-2.5 h-7 rounded-lg border transition-colors disabled:opacity-50 ${h.active ? 'border-slate-200 dark:border-gray-600 text-slate-500 dark:text-slate-400 hover:border-slate-300 hover:text-slate-700 dark:hover:text-slate-200' : 'border-emerald-200 dark:border-emerald-500/40 text-emerald-600 dark:text-emerald-400 hover:bg-emerald-50 dark:hover:bg-emerald-500/10'}`}>
                      {h.active ? 'غیرفعال' : 'فعال'}
                    </button>
                    <button onClick={() => { setEditing(h); setModalOpen(true); }}
                      className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-500/10 transition-colors">
                      <PencilIcon className="w-3.5 h-3.5" />
                    </button>
                    <button onClick={() => setDeletingUuid(h.uuid)}
                      className="w-8 h-8 flex items-center justify-center rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors">
                      <TrashIcon className="w-3.5 h-3.5" />
                    </button>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}
      <HolidayModal open={modalOpen} onClose={() => { setModalOpen(false); setEditing(null); }}
        existing={editing} doctorUuid={doctorUuid}
        onSaved={() => qc.invalidateQueries({ queryKey: ['doctor-holidays', doctorUuid] })} />
      <ConfirmDialog open={!!deletingUuid} title="حذف تعطیلات" message="آیا از حذف این تعطیلات اطمینان دارید؟"
        confirmLabel="بله، حذف کن" danger loading={deleteMut.isPending}
        onConfirm={() => deletingUuid && deleteMut.mutate(deletingUuid)} onCancel={() => setDeletingUuid(null)} />
    </div>
  );
}

// ── Schedule Section ───────────────────────────────────────────────────────

const SCHEDULE_TABS = [
  { id: 'weekly'    as const, label: 'ساعات کاری هفتگی' },
  { id: 'overrides' as const, label: 'تاریخ‌های خاص'    },
  { id: 'holidays'  as const, label: 'تعطیلات'          },
];

function ScheduleSection({ doctorUuid, readOnly = false }: { doctorUuid: string; readOnly?: boolean }) {
  const [tab, setTab] = useState<'weekly' | 'overrides' | 'holidays'>('weekly');

  const locationsQ = useQuery({
    queryKey: ['available-locations', doctorUuid],
    queryFn:  () => api.get<ApiResponse<any>>(`/api/v1/appointment-settings/available-locations/${doctorUuid}`),
    enabled:  !!doctorUuid,
    staleTime: 30_000,
  });
  const availableLocations: AddressData[] = locationsQ.data?.data?.data ?? locationsQ.data?.data ?? [];

  return (
    <div className="cp-card p-6">
      <h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300 mb-4">برنامه کاری</h2>
      <div className="flex gap-1 mb-5 bg-slate-100 dark:bg-gray-800 p-1 rounded-xl">
        {SCHEDULE_TABS.map(t => (
          <button key={t.id} type="button" onClick={() => setTab(t.id)}
            className={`flex-1 text-xs sm:text-sm font-medium px-2 py-1.5 rounded-lg transition-colors ${
              tab === t.id
                ? 'bg-white dark:bg-gray-700 text-slate-800 dark:text-slate-100 shadow-sm'
                : 'text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-slate-200'
            }`}>
            {t.label}
          </button>
        ))}
      </div>
      {tab === 'weekly'    && <WeeklyScheduleTab doctorUuid={doctorUuid} addresses={availableLocations} readOnly={readOnly} />}
      {tab === 'overrides' && <DateOverridesTab  doctorUuid={doctorUuid} addresses={availableLocations} readOnly={readOnly} />}
      {tab === 'holidays'  && <HolidaysTab        doctorUuid={doctorUuid} readOnly={readOnly} />}
    </div>
  );
}

// ── Edit form helpers ──────────────────────────────────────────────────────

const EDIT_DEGREE_OPTIONS = [
  { value: 'general',           label: 'پزشک عمومی' },
  { value: 'specialist',        label: 'متخصص' },
  { value: 'expert',            label: 'فوق تخصص' },
  { value: 'subspecialistplus', label: 'فلوشیپ' },
];

const EDIT_GENDER_OPTIONS = [
  { value: 'man',   label: 'مرد', icon: '♂' },
  { value: 'woman', label: 'زن',  icon: '♀' },
];

function EditField({ label, required, error, hint, children }: {
  label: string; required?: boolean; error?: string; hint?: string; children: React.ReactNode;
}) {
  return (
    <div>
      <label className="cp-label">
        {label}{required && <span style={{ color: 'var(--danger)', marginRight: 2 }}>*</span>}
      </label>
      {children}
      {hint && !error && <p style={{ fontSize: 12, color: 'var(--text-3)', marginTop: 5 }}>{hint}</p>}
      {error && <p style={{ fontSize: 12, color: 'var(--danger)', marginTop: 5 }}>{error}</p>}
    </div>
  );
}

function EditSectionHeader({ icon, title, badge }: { icon: React.ReactNode; title: string; badge?: string }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 18, paddingBottom: 14, borderBottom: '1px solid var(--border)' }}>
      <div style={{ width: 30, height: 30, borderRadius: 8, background: 'var(--primary-soft)', color: 'var(--primary-700)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
        {icon}
      </div>
      <span style={{ fontSize: 14, fontWeight: 700, color: 'var(--text)' }}>{title}</span>
      {badge && (
        <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--primary-700)', background: 'var(--primary-soft)', padding: '2px 10px', borderRadius: 999 }}>
          {badge}
        </span>
      )}
    </div>
  );
}

interface EditSelectedEntry { parentId: number | null; childId: number }

function EditSpecialtyPicker({ selected, onChange, specialties }: {
  selected: number[]; onChange: (ids: number[]) => void;
  specialties: SpecialtyOpt[];
}) {
  const [activeParentId, setActiveParentId] = useState<number | null>(null);

  const parents  = useMemo(() => specialties.filter(s => s.parent_id === null), [specialties]);
  const childMap = useMemo(() => {
    const m: Record<number, SpecialtyOpt[]> = {};
    specialties.forEach(s => {
      if (s.parent_id !== null) {
        if (!m[s.parent_id]) m[s.parent_id] = [];
        m[s.parent_id].push(s);
      }
    });
    return m;
  }, [specialties]);

  const activeChildren = activeParentId !== null ? (childMap[activeParentId] ?? []) : [];

  const selectedChildOfParent = (parentId: number): number | null => {
    const kids = childMap[parentId] ?? [];
    return kids.find(k => selected.includes(k.id))?.id ?? null;
  };

  const isRootSelected = (parentId: number): boolean =>
    !(childMap[parentId]?.length) && selected.includes(parentId);

  const selectChild = (child: SpecialtyOpt) => {
    const parentId = child.parent_id!;
    if (selected.includes(child.id)) {
      onChange([]);
    } else {
      onChange([parentId, child.id]);
    }
  };

  const selectRoot = (root: SpecialtyOpt) => {
    if (selected.includes(root.id)) {
      onChange([]);
    } else {
      setActiveParentId(null);
      onChange([root.id]);
    }
  };

  const removeEntry = () => { onChange([]); setActiveParentId(null); };

  const chips: EditSelectedEntry[] = useMemo(() => {
    return selected
      .map(id => {
        const s = specialties.find(x => x.id === id);
        if (!s) return null;
        if (s.parent_id !== null) return { parentId: s.parent_id, childId: s.id };
        const hasSelectedChild = (childMap[s.id] ?? []).some(k => selected.includes(k.id));
        if (!hasSelectedChild && !(childMap[s.id]?.length)) return { parentId: null, childId: s.id };
        return null;
      })
      .filter(Boolean) as EditSelectedEntry[];
  }, [selected, specialties, childMap]);

  return (
    <div style={{ border: '1px solid var(--border)', borderRadius: 'var(--r)', overflow: 'hidden' }}>
      {chips.length > 0 && (
        <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--border)', background: 'var(--surface-2)', display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {chips.map(({ parentId, childId }) => {
            const child  = specialties.find(x => x.id === childId);
            const parent = parentId !== null ? specialties.find(x => x.id === parentId) : null;
            if (!child) return null;
            const label = parent ? `${parent.name} — ${child.name}` : child.name;
            return (
              <span key={childId} style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 12, padding: '3px 10px 3px 6px', borderRadius: 999, background: 'var(--primary-soft2)', color: 'var(--primary-700)', fontWeight: 600 }}>
                {label}
                <button type="button" onClick={() => removeEntry()}
                  style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 16, height: 16, borderRadius: '50%', background: 'var(--primary-soft)', border: 'none', cursor: 'pointer', color: 'var(--primary-600)', padding: 0, fontSize: 12, fontWeight: 700 }}>×</button>
              </span>
            );
          })}
        </div>
      )}
      <div style={{ display: 'flex', minHeight: 240 }}>
        <div style={{ width: '45%', borderLeft: '1px solid var(--border)', overflowY: 'auto', maxHeight: 300 }}>
          <div style={{ padding: '8px 14px', fontSize: 11, fontWeight: 700, color: 'var(--text-3)', background: 'var(--surface-2)', borderBottom: '1px solid var(--border)', letterSpacing: '.2px' }}>
            گروه تخصصی
          </div>
          {parents.map(p => {
            const hasChildren  = (childMap[p.id]?.length ?? 0) > 0;
            const childSel     = hasChildren ? selectedChildOfParent(p.id) : null;
            const rootSel      = !hasChildren && isRootSelected(p.id);
            const isMarked     = childSel !== null || rootSel;
            const isActive     = activeParentId === p.id;
            const hasSelection = selected.length > 0;
            const isDisabled   = hasSelection && !isMarked;
            return (
              <button
                key={p.id}
                type="button"
                disabled={isDisabled}
                onClick={() => hasChildren ? setActiveParentId(p.id) : selectRoot(p)}
                style={{
                  width: '100%', textAlign: 'right', display: 'flex', alignItems: 'center', gap: 8,
                  padding: '10px 14px', fontSize: 13, border: 'none',
                  cursor: isDisabled ? 'not-allowed' : 'pointer',
                  opacity: isDisabled ? 0.35 : 1,
                  background: isActive ? 'var(--primary-soft)' : rootSel ? 'var(--primary-soft)' : 'transparent',
                  color: (isActive || rootSel) ? 'var(--primary-700)' : 'var(--text)',
                  fontWeight: (isActive || rootSel) ? 700 : 400,
                  borderRight: (isActive || rootSel) ? '3px solid var(--primary)' : '3px solid transparent',
                  transition: 'all .14s',
                }}
              >
                <span style={{ flex: 1, lineHeight: 1.4 }}>{p.name}</span>
                {isMarked && !rootSel && (
                  <span style={{ fontSize: 11, fontWeight: 700, background: 'var(--primary)', color: 'var(--on-primary)', borderRadius: 999, padding: '1px 7px', minWidth: 20, textAlign: 'center' }}>
                    ✓
                  </span>
                )}
                {rootSel && <CheckIcon style={{ width: 14, height: 14, color: 'var(--primary)', flexShrink: 0 }} />}
                {hasChildren && <ChevronLeftIcon style={{ width: 14, height: 14, color: 'var(--text-3)', flexShrink: 0 }} />}
              </button>
            );
          })}
        </div>
        <div style={{ flex: 1, overflowY: 'auto', maxHeight: 300 }}>
          {activeParentId === null ? (
            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', color: 'var(--text-3)', fontSize: 13, gap: 8, padding: 24 }}>
              <HeartIcon style={{ width: 32, height: 32, opacity: .3 }} />
              <span>یک گروه تخصصی انتخاب کنید</span>
            </div>
          ) : activeChildren.length === 0 ? (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', color: 'var(--text-3)', fontSize: 13 }}>
              زیرمجموعه‌ای یافت نشد
            </div>
          ) : (
            <>
              <div style={{ padding: '8px 14px', fontSize: 11, fontWeight: 700, color: 'var(--text-3)', background: 'var(--surface-2)', borderBottom: '1px solid var(--border)', letterSpacing: '.2px' }}>
                انتخاب تخصص — یک مورد
              </div>
              {activeChildren.map(s => {
                const checked = selected.includes(s.id);
                return (
                  <button
                    key={s.id}
                    type="button"
                    onClick={() => selectChild(s)}
                    style={{
                      width: '100%', textAlign: 'right', display: 'flex', alignItems: 'center', gap: 10,
                      padding: '10px 14px', border: 'none', cursor: 'pointer',
                      background: checked ? 'var(--primary-soft)' : 'transparent',
                      borderBottom: '1px solid var(--border)',
                      transition: 'background .12s',
                    }}
                  >
                    <div style={{ width: 18, height: 18, borderRadius: '50%', border: `2px solid ${checked ? 'var(--primary)' : 'var(--border-2)'}`, background: checked ? 'var(--primary)' : 'transparent', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, transition: 'all .12s' }}>
                      {checked && <div style={{ width: 7, height: 7, borderRadius: '50%', background: 'var(--on-primary)' }} />}
                    </div>
                    <span style={{ fontSize: 13, color: checked ? 'var(--primary-700)' : 'var(--text)', fontWeight: checked ? 600 : 400 }}>{s.name}</span>
                  </button>
                );
              })}
            </>
          )}
        </div>
      </div>
    </div>
  );
}

// ── Edit schema ────────────────────────────────────────────────────────────

const urlOrEmpty = z.string().url('آدرس نامعتبر است').optional().or(z.literal(''));

const editSchema = z.object({
  name:                z.string().min(2, 'نام حداقل ۲ کاراکتر'),
  gender:              z.enum(['man', 'woman']).optional().or(z.literal('')),
  degree:              z.string().optional().or(z.literal('')),
  medical_system_code: z.string().max(30).optional().or(z.literal('')),
  mobile_number:       iranMobileOptionalSchema.optional(),
  info:                z.string().max(2000).optional().or(z.literal('')),
  specialties:         z.array(z.number()).optional(),
  services:            z.array(z.number()).optional(),
  social_instagram:    urlOrEmpty,
  social_telegram:     urlOrEmpty,
  social_aparat:       urlOrEmpty,
  social_youtube:      urlOrEmpty,
  social_linkedin:     urlOrEmpty,
});
type EditForm = z.infer<typeof editSchema>;

// ── Clinic Invitations Section ─────────────────────────────────────────────

interface InvitationItem {
  uuid: string;
  status: string;
  invited_name: string | null;
  invited_specialty: string | null;
  invited_at: number;
  expires_at: number;
  clinic: { uuid: string; name: string; logo: string | null };
}

function ClinicInvitationsSection() {
  const qc = useQueryClient();
  const [respondingUuid, setRespondingUuid] = useState<string | null>(null);

  const { data, isLoading } = useQuery({
    queryKey: ['doctor-my-invitations'],
    queryFn: () => api.get<ApiResponse<InvitationItem[]>>('/api/v1/doctor/invitations'),
    staleTime: 30_000,
  });

  const invitations: InvitationItem[] = useMemo(() => {
    const raw = data?.data;
    return (raw as any)?.data ?? raw ?? [];
  }, [data]);

  const respondMut = useMutation({
    mutationFn: ({ uuid, action }: { uuid: string; action: 'accept' | 'reject' }) =>
      api.post<ApiResponse<any>>(`/api/v1/doctor/invitation/${uuid}/respond`, { action }),
    onSuccess: (_, vars) => {
      toast.success(vars.action === 'accept' ? 'دعوتنامه پذیرفته شد' : 'دعوتنامه رد شد');
      setRespondingUuid(null);
      qc.invalidateQueries({ queryKey: ['doctor-my-invitations'] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  if (!isLoading && invitations.length === 0) return null;

  return (
    <div className="cp-card p-6">
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16 }}>
        <div style={{ width: 30, height: 30, borderRadius: 8, background: 'var(--primary-soft)', color: 'var(--primary-700)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
          <BuildingOfficeIcon style={{ width: 16, height: 16 }} />
        </div>
        <span style={{ fontSize: 14, fontWeight: 700, color: 'var(--text)' }}>دعوت‌نامه‌های کلینیک</span>
        {invitations.length > 0 && (
          <span style={{ fontSize: 12, fontWeight: 600, color: 'var(--primary-700)', background: 'var(--primary-soft)', padding: '2px 10px', borderRadius: 999 }}>
            {invitations.length} دعوت جدید
          </span>
        )}
      </div>

      {isLoading ? (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {[1, 2].map(i => <div key={i} className="skeleton" style={{ height: 72, borderRadius: 'var(--r)' }} />)}
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          {invitations.map(inv => {
            const isExpired = Date.now() / 1000 > inv.expires_at;
            const busy = respondMut.isPending && respondingUuid === inv.uuid;
            return (
              <div key={inv.uuid} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '14px 16px', borderRadius: 'var(--r)', border: '1px solid var(--border)', background: 'var(--surface-2)' }}>
                {/* Logo */}
                <div style={{ width: 44, height: 44, borderRadius: 10, overflow: 'hidden', flexShrink: 0, background: 'var(--surface)', border: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  {inv.clinic.logo
                    ? <img src={inv.clinic.logo} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
                    : <BuildingOfficeIcon style={{ width: 20, height: 20, color: 'var(--text-3)' }} />
                  }
                </div>

                {/* Info */}
                <div style={{ flex: 1, minWidth: 0 }}>
                  <p style={{ fontWeight: 700, fontSize: 14, color: 'var(--text)', marginBottom: 3 }}>{inv.clinic.name}</p>
                  {inv.invited_specialty && (
                    <p style={{ fontSize: 12, color: 'var(--text-2)' }}>تخصص: {inv.invited_specialty}</p>
                  )}
                  <p style={{ fontSize: 11, color: isExpired ? 'var(--danger)' : 'var(--text-3)', marginTop: 2 }}>
                    {isExpired ? 'منقضی شده' : `انقضا: ${new Date(inv.expires_at * 1000).toLocaleDateString('fa-IR')}`}
                  </p>
                </div>

                {/* Actions */}
                {!isExpired && (
                  <div style={{ display: 'flex', gap: 8, flexShrink: 0 }}>
                    <button
                      disabled={busy}
                      onClick={() => { setRespondingUuid(inv.uuid); respondMut.mutate({ uuid: inv.uuid, action: 'reject' }); }}
                      style={{ height: 34, padding: '0 14px', borderRadius: 'var(--r-sm)', border: '1.5px solid var(--border)', background: 'var(--surface)', color: 'var(--text-2)', fontSize: 13, fontWeight: 600, cursor: busy ? 'not-allowed' : 'pointer', opacity: busy ? 0.5 : 1 }}>
                      رد
                    </button>
                    <button
                      disabled={busy}
                      onClick={() => { setRespondingUuid(inv.uuid); respondMut.mutate({ uuid: inv.uuid, action: 'accept' }); }}
                      style={{ height: 34, padding: '0 14px', borderRadius: 'var(--r-sm)', border: '1.5px solid var(--primary)', background: 'var(--primary)', color: 'var(--on-primary)', fontSize: 13, fontWeight: 600, cursor: busy ? 'not-allowed' : 'pointer', opacity: busy ? 0.5 : 1 }}>
                      {busy ? '...' : 'پذیرفتن'}
                    </button>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ── Main Page ──────────────────────────────────────────────────────────────

export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfile?: boolean }) {
  const { uuid: paramUuid } = useParams<{ uuid: string }>();
  const navigate  = useNavigate();
  const [searchParams] = useSearchParams();
  const qc = useQueryClient();
  const primaryRole = useAuthStore(s => s.primaryRole);
  const dbUuid      = useAuthStore(s => s.dbUuid);
  const doctorUuid  = useAuthStore(s => s.doctorUuid);
  const uuid = isOwnProfile ? (doctorUuid ?? dbUuid ?? undefined) : paramUuid;
  // نماینده فقط مشاهده می‌کند؛ هیچ بخشی قابل ویرایش نیست.
  const isReadOnly = primaryRole === 'representation';

  const [editOpen,         setEditOpen]         = useState(searchParams.get('edit') === '1');
  const [deleteOpen,       setDeleteOpen]       = useState(false);
  const [toggleConfirm,    setToggleConfirm]    = useState(false);
  const [menuOpen,         setMenuOpen]         = useState(false);
  const [uploadingImg,     setUploadingImg]     = useState(false);
  const [cropState,        setCropState]        = useState<{ src: string; name: string } | null>(null);
  const [addrModalOpen,    setAddrModalOpen]    = useState(false);
  const [editingAddr,      setEditingAddr]      = useState<AddressData | null>(null);
  const [deletingAddrId,   setDeletingAddrId]   = useState<string | null>(null);
  const [editGender,       setEditGender]       = useState<'man' | 'woman' | ''>('');
  const [editActivityDate, setEditActivityDate] = useState('');
  const [repModalOpen,     setRepModalOpen]     = useState(false);
  const [selRepId,         setSelRepId]         = useState<number | null>(null);

  const isAdmin = primaryRole === 'admin';

  // ── Queries ──

  const { data, isLoading, isError } = useQuery({
    queryKey: ['doctor-detail', uuid, primaryRole],
    queryFn: () =>
      primaryRole === 'clinic'
        ? api.get<ApiResponse<any>>(`/api/v1/clinic/my-doctor/${uuid}`)
        : api.get<ApiResponse<any>>(`/api/v1/doctor/${uuid}`),
    enabled: !!uuid,
  });
  const specialtiesQ = useQuery({
    queryKey: ['specialties-all'],
    queryFn: () => api.get<ApiResponse<any>>('/api/v1/specialties'),
    staleTime: 600_000,
  });
  const servicesQ = useQuery({
    queryKey: ['doctor-services-all'],
    queryFn: () => api.get<ApiResponse<any>>('/api/v1/doctor-services'),
    staleTime: 600_000,
  });

  const doctor: DoctorDetail | undefined = useMemo(() => {
    const raw = data?.data;
    return (raw as any)?.data ?? raw;
  }, [data]);

  const specialties: SpecialtyOpt[] = useMemo(
    () => specialtiesQ.data?.data?.data ?? specialtiesQ.data?.data ?? [], [specialtiesQ.data]);
  const services: ServiceOpt[] = useMemo(
    () => servicesQ.data?.data?.data ?? servicesQ.data?.data ?? [], [servicesQ.data]);

  // ── Edit form ──

  const { register, handleSubmit, reset, watch, setValue, control: editControl } = useForm<EditForm>({
    resolver: zodResolver(editSchema),
  });
  const watchedSpecialties = (watch('specialties') ?? []) as number[];
  const watchedServices    = (watch('services')    ?? []) as number[];

  useEffect(() => {
    if (doctor) {
      reset({
        name:                doctor.name,
        gender:              (doctor.gender as any) ?? '',
        degree:              doctor.degree ?? '',
        medical_system_code: doctor.medical_system_code ?? '',
        mobile_number:       '',
        info:                doctor.detail ?? '',
        specialties:         doctor.specialties.map(s => Number(s.id)),
        services:            doctor.expertise.map(s => Number(s.id)),
        social_instagram:    doctor.social_media?.instagram ?? '',
        social_telegram:     doctor.social_media?.telegram ?? '',
        social_aparat:       doctor.social_media?.aparat ?? '',
        social_youtube:      doctor.social_media?.youtube ?? '',
        social_linkedin:     doctor.social_media?.linkedin ?? '',
      });
      setEditGender((doctor.gender as any) ?? '');
      setEditActivityDate(
        doctor.activity_time ? toGregorianDate(toDate(Number(doctor.activity_time))!) : ''
      );
    }
  }, [doctor, reset]);

  // Close menu on outside click
  useEffect(() => {
    if (!menuOpen) return;
    const close = (e: MouseEvent) => {
      if ((e.target as HTMLElement).closest('[data-dmenu]') === null) setMenuOpen(false);
    };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [menuOpen]);

  // ── Mutations ──

  const updateMut = useMutation({
    mutationFn: (body: EditForm) => api.patch<ApiResponse<any>>(`/api/v1/doctor/${uuid}`, {
      title:               body.name,
      gender:              editGender || undefined,
      degree:              body.degree || undefined,
      medical_system_code: body.medical_system_code || undefined,
      mobile_number:       body.mobile_number || undefined,
      info:                body.info || undefined,
      ...(editActivityDate
        ? { activity_time: Math.floor(new Date(`${editActivityDate}T12:00:00`).getTime() / 1000) }
        : {}),
      specialties:         body.specialties ?? [],
      doctor_services:     body.services ?? [],
      social_media: {
        instagram: body.social_instagram || null,
        telegram:  body.social_telegram || null,
        aparat:    body.social_aparat || null,
        youtube:   body.social_youtube || null,
        linkedin:  body.social_linkedin || null,
      },
    }),
    onSuccess: () => {
      toast.success('اطلاعات پزشک بروزرسانی شد');
      setEditOpen(false);
      qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] });
      qc.invalidateQueries({ queryKey: ['admin-doctors'] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const toggleMut = useMutation({
    mutationFn: () => api.post<ApiResponse<any>>(`/api/v1/admin/doctors/${uuid}/status`, {}),
    onSuccess: (res) => {
      const isActive = (res?.data as any)?.is_active;
      toast.success(isActive ? 'پزشک فعال شد' : 'پزشک غیرفعال شد');
      qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] });
      qc.invalidateQueries({ queryKey: ['admin-doctors'] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const deleteMut = useMutation({
    mutationFn: () => api.delete<ApiResponse<null>>(`/api/v1/doctor/${uuid}`),
    onSuccess: () => { toast.success('پزشک حذف شد'); navigate('/admin/doctors'); },
    onError:   (e: Error) => toast.error(e.message),
  });

  const repsQuery = useQuery({
    queryKey: ['representations-select'],
    queryFn: () => api.get<ApiResponse<any>>('/api/v1/admin/representations?limit=200'),
    enabled: isAdmin && repModalOpen,
    staleTime: 60_000,
  });
  const repOptions: { value: number; label: string }[] = useMemo(() => {
    const rows = repsQuery.data?.data ?? [];
    return (rows as any[]).map((r) => ({ value: r.id as number, label: `${r.full_name}${r.city ? ` — ${r.city}` : ''}` }));
  }, [repsQuery.data]);

  const setRepMut = useMutation({
    mutationFn: (representationId: number | null) =>
      api.put<ApiResponse<any>>(`/api/v1/admin/doctors/${uuid}/representation`, { representation_id: representationId }),
    onSuccess: () => {
      toast.success('نماینده‌ی پزشک به‌روزرسانی شد');
      setRepModalOpen(false);
      qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] });
      qc.invalidateQueries({ queryKey: ['admin-doctors'] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const deleteAddrMut = useMutation({
    mutationFn: (id: string) => api.delete<ApiResponse<null>>(`/api/v1/clinic-pro/doctor-address/${id}`),
    onSuccess: () => {
      toast.success('آدرس حذف شد'); setDeletingAddrId(null);
      qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] });
      qc.invalidateQueries({ queryKey: ['available-locations', uuid] });
    },
    onError: (e: Error) => toast.error(e.message),
  });

  // ── Image upload ──

  const openCrop = useCallback((file: File) => {
    setCropState({ src: URL.createObjectURL(file), name: file.name });
  }, []);

  const closeCrop = useCallback(() => {
    setCropState(prev => {
      if (prev) URL.revokeObjectURL(prev.src);
      return null;
    });
  }, []);

  const handleImageUpload = useCallback(async (file: File) => {
    setUploadingImg(true);
    try {
      const fileData = await uploadDoctorImage(file);
      await api.patch(`/api/v1/doctor/${uuid}`, { images: [fileData] });
      toast.success('تصویر با موفقیت آپلود شد');
      qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] });
      qc.invalidateQueries({ queryKey: ['admin-doctors'] });
    } catch (e: unknown) {
      toast.error(e instanceof Error ? e.message : 'خطا در آپلود تصویر');
    } finally {
      setUploadingImg(false);
    }
  }, [uuid, qc]);

  // ── Loading / Error ──

  if (isLoading) {
    return (
      <div className="animate-slide-up space-y-5">
        <div className="flex items-center gap-3">
          <div className="w-8 h-8 rounded-lg skeleton" /><div className="h-5 w-32 rounded skeleton" />
        </div>
        <div className="cp-card p-6">
          <div className="flex items-start gap-5 mb-8">
            <div className="w-24 h-24 rounded-2xl skeleton shrink-0" />
            <div className="flex-1 space-y-2 pt-2">
              <div className="h-7 w-48 rounded skeleton" />
              <div className="flex gap-2 mt-2"><div className="h-5 w-16 rounded-full skeleton" /><div className="h-5 w-14 rounded-full skeleton" /></div>
            </div>
          </div>
          <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
            {Array.from({ length: 4 }).map((_, i) => <div key={i} className="h-16 rounded-xl skeleton" />)}
          </div>
        </div>
      </div>
    );
  }

  if (isError || !doctor) {
    return (
      <div className="animate-slide-up cp-card p-16 flex flex-col items-center gap-4 text-center">
        <div className="w-16 h-16 rounded-2xl bg-red-100 dark:bg-red-400/10 flex items-center justify-center">
          <XCircleIcon className="w-8 h-8 text-red-500" />
        </div>
        <p className="font-semibold text-slate-800 dark:text-slate-200">پزشک یافت نشد</p>
        <button onClick={() => navigate('/admin/doctors')} className="cp-btn-secondary mt-2">
          بازگشت به لیست پزشکان
        </button>
      </div>
    );
  }

  const mainImage   = doctor.img?.[0]?.url ?? null;
  const idNum       = parseInt(doctor.id ?? '0', 10);
  const degreeLabel = DEGREE_LABELS[doctor.degree ?? ''] ?? doctor.degree ?? '—';
  const rateNum     = parseFloat(doctor.point ?? '3.5');
  const addresses   = doctor.address ?? [];

  return (
    <div className="animate-slide-up space-y-5">

      {/* Breadcrumb */}
      <div className="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400">
        {isOwnProfile ? (
          <>
            <button onClick={() => navigate('/admin/dashboard')}
              className="flex items-center gap-1.5 hover:text-slate-800 dark:hover:text-slate-100 transition-colors">
              <ArrowRightIcon className="w-4 h-4" />داشبورد
            </button>
            <span>/</span>
            <span className="text-slate-700 dark:text-slate-300 font-medium">پروفایل من</span>
          </>
        ) : (
          <>
            <button onClick={() => navigate(primaryRole === 'clinic' ? '/admin/dashboard' : '/admin/doctors')}
              className="flex items-center gap-1.5 hover:text-slate-800 dark:hover:text-slate-100 transition-colors">
              <ArrowRightIcon className="w-4 h-4" />{primaryRole === 'clinic' ? 'داشبورد' : 'پزشکان'}
            </button>
            <span>/</span>
            <span className="text-slate-700 dark:text-slate-300 font-medium">پروفایل پزشک</span>
          </>
        )}
      </div>

      {/* Profile Card */}
      <div className="cp-card overflow-hidden">
        <div className="h-28 bg-gradient-to-l from-blue-600/20 via-blue-500/10 to-transparent dark:from-blue-500/20" />
        <div className="px-6 pb-6 -mt-12 flex flex-col sm:flex-row sm:items-end gap-4">

          <div className="flex flex-col items-center gap-1.5">
            <DoctorAvatar name={doctor.name} img={mainImage} idx={idNum}
              onUpload={primaryRole !== 'clinic' && !isReadOnly ? openCrop : undefined} uploading={uploadingImg} />
            {cropState && (
              <ImageCropModal
                src={cropState.src}
                fileName={cropState.name}
                onCancel={closeCrop}
                onConfirm={(file) => { closeCrop(); handleImageUpload(file); }}
              />
            )}
            {primaryRole !== 'clinic' && !isReadOnly && (
              <span className="text-[10px] text-slate-400 dark:text-slate-500">کلیک برای تغییر عکس</span>
            )}
          </div>

          <div className="flex-1 min-w-0 sm:mb-1">
            <h1 className="text-xl font-bold text-slate-900 dark:text-slate-50">دکتر {doctor.name}</h1>
            <div className="flex items-center gap-2 mt-1.5 flex-wrap">
              {/* Active status badge */}
              {doctor.active
                ? <span className="inline-flex items-center gap-1 text-xs font-medium px-2.5 py-0.5 rounded-full bg-emerald-100 dark:bg-emerald-400/10 text-emerald-700 dark:text-emerald-300">
                    <CheckCircleIcon className="w-3 h-3" />فعال
                  </span>
                : <span className="inline-flex items-center gap-1 text-xs font-medium px-2.5 py-0.5 rounded-full bg-slate-100 dark:bg-gray-700 text-slate-500 dark:text-slate-400">
                    <XCircleIcon className="w-3 h-3" />غیرفعال
                  </span>
              }
              {doctor.degree && (
                <span className="inline-flex text-xs font-medium px-2.5 py-0.5 rounded-full bg-blue-100 dark:bg-blue-400/10 text-blue-700 dark:text-blue-300">{degreeLabel}</span>
              )}
              {doctor.gender && (
                <span className="text-xs text-slate-500 dark:text-slate-400">{doctor.gender === 'man' ? '♂ مرد' : '♀ زن'}</span>
              )}
            </div>
            {doctor.specialties.length > 0 && (
              <div className="flex flex-wrap gap-1 mt-2">
                {doctor.specialties.map((s, i) => (
                  <span key={s.id} className={`text-[11px] font-medium px-2 py-0.5 rounded-full ${SPECIALTY_COLORS[i % SPECIALTY_COLORS.length]}`}>{s.name}</span>
                ))}
              </div>
            )}
            {doctor.expertise.length > 0 && (
              <div className="flex flex-wrap gap-1 mt-1">
                {doctor.expertise.map(e => (
                  <span key={e.id} className="text-[11px] px-2 py-0.5 rounded-full bg-slate-100 dark:bg-gray-700 text-slate-600 dark:text-slate-400 border border-slate-200 dark:border-gray-600">{e.name}</span>
                ))}
              </div>
            )}
          </div>

          <div className="flex items-center gap-2 sm:mb-1 flex-wrap">
            {primaryRole !== 'clinic' && !isReadOnly && (
              <button onClick={() => setEditOpen(true)} className="cp-btn-primary text-sm">
                <PencilIcon className="w-4 h-4" />ویرایش
              </button>
            )}

            {/* Toggle active / Delete — فقط ادمین */}
            {!isOwnProfile && primaryRole !== 'clinic' && !isReadOnly && (
              <>
                <button
                  onClick={() => setToggleConfirm(true)}
                  disabled={toggleMut.isPending}
                  className={`inline-flex items-center gap-1.5 text-sm font-medium px-3 h-10 rounded-xl border transition-colors disabled:opacity-50 ${
                    doctor.active
                      ? 'border-orange-300 dark:border-orange-500/40 text-orange-600 dark:text-orange-400 bg-orange-50 dark:bg-orange-500/10 hover:bg-orange-100 dark:hover:bg-orange-500/20'
                      : 'border-emerald-300 dark:border-emerald-500/40 text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10 hover:bg-emerald-100 dark:hover:bg-emerald-500/20'
                  }`}
                >
                  {toggleMut.isPending
                    ? <span className="w-3.5 h-3.5 border-2 border-current/30 border-t-current rounded-full animate-spin" />
                    : doctor.active
                      ? <XCircleIcon className="w-4 h-4" />
                      : <CheckCircleIcon className="w-4 h-4" />
                  }
                  {doctor.active ? 'غیرفعال کردن' : 'فعال کردن'}
                </button>

                <div className="relative" data-dmenu>
                  <button data-dmenu onClick={() => setMenuOpen(!menuOpen)}
                    className="cp-btn-secondary h-10 w-10 flex items-center justify-center px-0">
                    <EllipsisVerticalIcon className="w-4 h-4" />
                  </button>
                  {menuOpen && (
                    <div className="absolute left-0 top-12 z-30 w-52 cp-card shadow-2xl border border-slate-200 dark:border-gray-700 py-1.5 animate-scale-in" data-dmenu>
                      <button onClick={() => { setDeleteOpen(true); setMenuOpen(false); }}
                        className="w-full flex items-center gap-2.5 px-3 py-2 text-sm text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-500/10">
                        <TrashIcon className="w-4 h-4 shrink-0" />حذف این پزشک
                      </button>
                    </div>
                  )}
                </div>
              </>
            )}
          </div>
        </div>
      </div>

      {/* Info Grid */}
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-5">

        <div className="lg:col-span-2 space-y-5">

          <div className="cp-card p-6 space-y-4">
            <h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300">اطلاعات پایه</h2>
            <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
              <InfoCard icon={UserIcon} label="نام کامل" value={doctor.name} />
              <InfoCard icon={AcademicCapIcon} label="کد نظام پزشکی" value={doctor.medical_system_code} mono copyable />
              <InfoCard icon={CalendarIcon} label="سابقه (سال)" value={doctor.experience > 0 ? String(doctor.experience) : null} />
              <InfoCard icon={StarIcon} label="امتیاز" value={<div dir="ltr"><StarRating rate={rateNum} /></div>} />
              <InfoCard
                icon={UserIcon}
                label="نماینده"
                value={
                  <span className="flex items-center gap-2">
                    {doctor.representation
                      ? <button type="button" style={{ color: '#2563eb', textDecoration: 'underline', background: 'none', border: 0, padding: 0, cursor: 'pointer', font: 'inherit' }} onClick={() => navigate(`/admin/representations/${doctor.representation!.uuid}`)}>{doctor.representation.full_name ?? 'نماینده'}</button>
                      : <span className="text-slate-400">بدون نماینده</span>}
                    {isAdmin && (
                      <button type="button" className="btn ghost sm"
                        onClick={() => { setSelRepId(doctor.representation?.id ?? null); setRepModalOpen(true); }}>
                        {doctor.representation ? 'تغییر' : 'افزودن نماینده'}
                      </button>
                    )}
                  </span>
                }
              />
            </div>
          </div>

          {doctor.detail && (
            <div className="cp-card p-6">
              <h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300 mb-3">بیوگرافی</h2>
              <p className="text-sm text-slate-600 dark:text-slate-400 leading-relaxed">{doctor.detail}</p>
            </div>
          )}

          {/* Addresses */}
          <div className="cp-card p-6">
            <div className="flex items-center justify-between mb-4">
              <h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300">
                آدرس‌های مطب ({formatNumber(addresses.length)})
              </h2>
              {!isReadOnly && (
                <button onClick={() => { setEditingAddr(null); setAddrModalOpen(true); }}
                  className="flex items-center gap-1.5 text-xs font-medium text-primary-600 dark:text-primary-400 hover:underline">
                  <PlusIcon className="w-4 h-4" />افزودن آدرس
                </button>
              )}
            </div>
            {addresses.length > 0 ? (
              <div className="space-y-2">
                {addresses.map(addr => (
                  <AddressCard key={addr.id} addr={addr} readOnly={isReadOnly}
                    onEdit={() => { setEditingAddr(addr); setAddrModalOpen(true); }}
                    onDelete={() => setDeletingAddrId(addr.id)}
                  />
                ))}
              </div>
            ) : (
              <div className="text-center py-8 text-slate-400 dark:text-slate-500">
                <MapPinIcon className="w-10 h-10 mx-auto mb-2 opacity-30" />
                <p className="text-sm">هنوز آدرسی ثبت نشده است</p>
                {!isReadOnly && (
                  <button onClick={() => { setEditingAddr(null); setAddrModalOpen(true); }}
                    className="mt-3 text-xs text-primary-600 dark:text-primary-400 hover:underline">
                    اولین آدرس را اضافه کنید
                  </button>
                )}
              </div>
            )}
          </div>

          {uuid && <ScheduleSection doctorUuid={uuid} readOnly={isReadOnly} />}

          {isOwnProfile && <ClinicInvitationsSection />}

          {doctor.clinics && doctor.clinics.length > 0 && (
            <div className="cp-card p-6">
              <h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300 mb-3">
                کلینیک‌های مرتبط ({formatNumber(doctor.clinics.length)})
              </h2>
              <div className="space-y-2">
                {doctor.clinics.map(c => (
                  <div key={c.uuid} className="flex items-start gap-3 p-3 rounded-xl bg-slate-50 dark:bg-gray-800/60">
                    <div className="w-8 h-8 rounded-lg bg-white dark:bg-gray-700 shadow-sm flex items-center justify-center shrink-0">
                      <BuildingOfficeIcon className="w-4 h-4 text-slate-400" />
                    </div>
                    <div>
                      <p className="text-sm font-medium text-slate-800 dark:text-slate-200">{c.name}</p>
                      {c.address && <p className="text-xs text-slate-500 mt-0.5 flex items-center gap-1"><MapPinIcon className="w-3 h-3" />{c.address}</p>}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>

        {/* Sidebar */}
        <div className="space-y-5">
          <div className="cp-card p-5">
            <h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300 mb-3">
              تخصص‌ها ({formatNumber(doctor.specialties.length)})
            </h2>
            {doctor.specialties.length > 0
              ? <div className="flex flex-wrap gap-1.5">
                  {doctor.specialties.map((s, i) => (
                    <span key={s.id} className={`text-xs font-medium px-2.5 py-1 rounded-lg ${SPECIALTY_COLORS[i % SPECIALTY_COLORS.length]}`}>{s.name}</span>
                  ))}
                </div>
              : <p className="text-sm text-slate-400">تخصصی ثبت نشده</p>
            }
            {!isReadOnly && (
              <button onClick={() => setEditOpen(true)} className="w-full mt-3 text-xs text-primary-600 dark:text-primary-400 hover:underline text-right">
                ویرایش تخصص‌ها ←
              </button>
            )}
          </div>

          <div className="cp-card p-5">
            <h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300 mb-3">
              خدمات ({formatNumber(doctor.expertise.length)})
            </h2>
            {doctor.expertise.length > 0
              ? <div className="flex flex-wrap gap-1.5">
                  {doctor.expertise.map(e => (
                    <span key={e.id} className="text-xs px-2.5 py-1 rounded-lg bg-slate-100 dark:bg-gray-800 text-slate-600 dark:text-slate-400 border border-slate-200 dark:border-gray-700">{e.name}</span>
                  ))}
                </div>
              : <p className="text-sm text-slate-400">خدمتی ثبت نشده</p>
            }
            {!isReadOnly && (
              <button onClick={() => setEditOpen(true)} className="w-full mt-3 text-xs text-primary-600 dark:text-primary-400 hover:underline text-right">
                ویرایش خدمات ←
              </button>
            )}
          </div>

          <div className="cp-card p-5">
            <h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300 mb-3">امتیاز و رضایت</h2>
            <div className="space-y-3">
              <div>
                <p className="text-xs text-slate-500 dark:text-slate-400 mb-1">امتیاز کلی</p>
                <div dir="ltr"><StarRating rate={rateNum} /></div>
              </div>
              <div>
                <p className="text-xs text-slate-500 dark:text-slate-400 mb-1">درصد رضایت</p>
                <div className="flex items-center gap-2">
                  <div className="flex-1 h-2 rounded-full bg-slate-200 dark:bg-gray-700">
                    <div className="h-full rounded-full bg-amber-400 transition-all" style={{ width: `${doctor.satisfaction}%` }} />
                  </div>
                  <span className="text-sm font-medium text-slate-700 dark:text-slate-300">{doctor.satisfaction}%</span>
                </div>
              </div>
            </div>
          </div>

          <div className="cp-card p-5">
            <h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300 mb-2">UUID</h2>
            <div className="flex items-center gap-2">
              <p className="text-[11px] font-mono text-slate-500 dark:text-slate-400 break-all flex-1" dir="ltr">{doctor.uuid}</p>
              <button onClick={() => { navigator.clipboard.writeText(doctor.uuid); toast.success('UUID کپی شد'); }}
                className="w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-slate-600 hover:bg-slate-100 transition-colors shrink-0">
                <ClipboardDocumentIcon className="w-3.5 h-3.5" />
              </button>
            </div>
          </div>

          {primaryRole === 'doctor' && (
            <NotificationMobileCard target="doctor" />
          )}
        </div>
      </div>

      {/* ── Edit Modal ─────────────────────────────────────────── */}
      <Modal open={editOpen} title="ویرایش اطلاعات پزشک" size="lg" onClose={() => setEditOpen(false)}
        footer={
          <>
            <button onClick={() => setEditOpen(false)} className="cp-btn-secondary">لغو</button>
            <button form="edit-doctor-form" type="submit" disabled={updateMut.isPending} className="cp-btn-primary" style={{ minWidth: 130 }}>
              {updateMut.isPending ? 'در حال ذخیره...' : 'ذخیره تغییرات'}
            </button>
          </>
        }
      >
        <form id="edit-doctor-form" onSubmit={handleSubmit(v => updateMut.mutate(v))}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>

            {/* ── Section: اطلاعات حساب ── */}
            <div className="cp-card" style={{ padding: 'var(--card-pad)' }}>
              <EditSectionHeader icon={<PhoneIcon style={{ width: 16, height: 16 }} />} title="اطلاعات حساب" />
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
                <EditField label="نام کامل" required error={(register('name') as any)?.formState?.errors?.name?.message}>
                  <input type="text" className="cp-input" placeholder="دکتر ..." {...register('name')} />
                </EditField>
                <EditField label="شماره موبایل مطب">
                  <MobileInput className="cp-input" {...register('mobile_number')} />
                </EditField>
              </div>
            </div>

            {/* ── Section: اطلاعات حرفه‌ای ── */}
            <div className="cp-card" style={{ padding: 'var(--card-pad)' }}>
              <EditSectionHeader icon={<IdentificationIcon style={{ width: 16, height: 16 }} />} title="اطلاعات حرفه‌ای" />

              {/* Gender toggle */}
              <div style={{ marginBottom: 14 }}>
                <label className="cp-label">جنسیت</label>
                <div style={{ display: 'flex', gap: 8 }}>
                  {EDIT_GENDER_OPTIONS.map(o => (
                    <button key={o.value} type="button"
                      onClick={() => setEditGender(editGender === o.value ? '' : o.value as 'man' | 'woman')}
                      style={{
                        flex: 1, height: 42, borderRadius: 'var(--r-sm)',
                        border: `1.5px solid ${editGender === o.value ? 'var(--primary)' : 'var(--border)'}`,
                        background: editGender === o.value ? 'var(--primary-soft)' : 'var(--surface)',
                        color: editGender === o.value ? 'var(--primary-700)' : 'var(--text-2)',
                        fontWeight: editGender === o.value ? 700 : 400, fontSize: 14, cursor: 'pointer', transition: 'all .14s',
                        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
                      }}>
                      <span style={{ fontSize: 16 }}>{o.icon}</span>{o.label}
                    </button>
                  ))}
                </div>
              </div>

              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
                <EditField label="درجه تحصیلی">
                  <Controller name="degree" control={editControl} render={({ field }) => (
                    <GlobalSearchableSelect
                      options={EDIT_DEGREE_OPTIONS}
                      value={field.value ?? null}
                      onChange={(v) => field.onChange(v ?? '')}
                      placeholder="انتخاب کنید"
                      isClearable
                    />
                  )} />
                </EditField>
                <EditField label="کد نظام پزشکی">
                  <input type="text" dir="ltr" className="cp-input" placeholder="123456" {...register('medical_system_code')} />
                </EditField>
              </div>

              <div style={{ marginTop: 14 }}>
                <EditField label="تاریخ شروع فعالیت" hint="مبنای محاسبهٔ سال تجربه">
                  <PersianDatePicker
                    value={editActivityDate}
                    onChange={setEditActivityDate}
                    placeholder="انتخاب تاریخ"
                    enableYearPicker
                    minWidth={200}
                  />
                </EditField>
              </div>

              <div style={{ marginTop: 14 }}>
                <EditField label="بیوگرافی">
                  <textarea rows={3} className="cp-textarea" placeholder="معرفی کوتاهی از پزشک..." {...register('info')} style={{ resize: 'none' }} />
                </EditField>
              </div>
            </div>

            {/* ── Section: شبکه‌های اجتماعی ── */}
            <div className="cp-card" style={{ padding: 'var(--card-pad)' }}>
              <EditSectionHeader icon={<DocumentTextIcon style={{ width: 16, height: 16 }} />} title="شبکه‌های اجتماعی" />
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
                <EditField label="اینستاگرام">
                  <input type="text" dir="ltr" className="cp-input" placeholder="https://instagram.com/..." {...register('social_instagram')} />
                </EditField>
                <EditField label="تلگرام">
                  <input type="text" dir="ltr" className="cp-input" placeholder="https://t.me/..." {...register('social_telegram')} />
                </EditField>
                <EditField label="آپارات">
                  <input type="text" dir="ltr" className="cp-input" placeholder="https://aparat.com/..." {...register('social_aparat')} />
                </EditField>
                <EditField label="یوتیوب">
                  <input type="text" dir="ltr" className="cp-input" placeholder="https://youtube.com/..." {...register('social_youtube')} />
                </EditField>
                <EditField label="لینکدین">
                  <input type="text" dir="ltr" className="cp-input" placeholder="https://linkedin.com/in/..." {...register('social_linkedin')} />
                </EditField>
              </div>
            </div>

            {/* ── Section: تخصص‌ها ── */}
            <div className="cp-card" style={{ padding: 'var(--card-pad)' }}>
              <EditSectionHeader
                icon={<AcademicCapIcon style={{ width: 16, height: 16 }} />}
                title="تخصص‌ها"
                badge={watchedSpecialties.length > 0 ? `${watchedSpecialties.length} انتخاب شده` : undefined}
              />
              {specialtiesQ.isLoading ? (
                <div style={{ height: 200, borderRadius: 'var(--r)', overflow: 'hidden' }} className="skeleton" />
              ) : (
                <EditSpecialtyPicker
                  selected={watchedSpecialties}
                  onChange={ids => setValue('specialties', ids)}
                  specialties={specialties}
                />
              )}
            </div>

            {/* ── Section: خدمات ── */}
            <div className="cp-card" style={{ padding: 'var(--card-pad)' }}>
              <EditSectionHeader
                icon={<DocumentTextIcon style={{ width: 16, height: 16 }} />}
                title="خدمات"
                badge={watchedServices.length > 0 ? `${watchedServices.length} انتخاب شده` : undefined}
              />
              <ServicesPicker
                selected={watchedServices}
                onChange={ids => setValue('services', ids)}
                services={services}
                specialties={specialties}
                selectedSpecialtyIds={watchedSpecialties}
              />
            </div>

          </div>
        </form>
      </Modal>

      {uuid && (
        <AddressModal
          open={addrModalOpen}
          onClose={() => { setAddrModalOpen(false); setEditingAddr(null); }}
          existing={editingAddr}
          doctorUuid={uuid}
          onSaved={() => {
            qc.invalidateQueries({ queryKey: ['doctor-detail', uuid] });
            qc.invalidateQueries({ queryKey: ['available-locations', uuid] });
          }}
        />
      )}

      <ConfirmDialog
        open={!!deletingAddrId} title="حذف آدرس" message="آیا از حذف این آدرس اطمینان دارید؟"
        confirmLabel="بله، حذف کن" danger loading={deleteAddrMut.isPending}
        onConfirm={() => deletingAddrId && deleteAddrMut.mutate(deletingAddrId)}
        onCancel={() => setDeletingAddrId(null)}
      />
      <ConfirmDialog
        open={toggleConfirm}
        title={doctor.active ? 'غیرفعال کردن پزشک' : 'فعال کردن پزشک'}
        message={
          doctor.active
            ? `آیا مطمئن هستید که می‌خواهید دکتر "${doctor.name}" را غیرفعال کنید؟ پزشک دیگر قادر به دریافت نوبت نخواهد بود.`
            : `آیا می‌خواهید دکتر "${doctor.name}" را فعال کنید؟ پزشک مجدداً قادر به دریافت نوبت خواهد بود.`
        }
        confirmLabel={doctor.active ? 'بله، غیرفعال کن' : 'بله، فعال کن'}
        danger={doctor.active}
        loading={toggleMut.isPending}
        onConfirm={() => { toggleMut.mutate(); setToggleConfirm(false); }}
        onCancel={() => setToggleConfirm(false)}
      />
      <ConfirmDialog
        open={deleteOpen} title="حذف پزشک"
        message={`آیا از حذف دکتر "${doctor.name}" اطمینان دارید؟ این عمل قابل بازگشت نیست.`}
        confirmLabel="بله، حذف کن" danger loading={deleteMut.isPending}
        onConfirm={() => deleteMut.mutate()} onCancel={() => setDeleteOpen(false)}
      />

      <Modal open={repModalOpen} title="نماینده‌ی پزشک" size="md" onClose={() => setRepModalOpen(false)}
        footer={
          <>
            <button className="btn ghost sm" onClick={() => setRepModalOpen(false)}>لغو</button>
            {doctor.representation && (
              <button className="btn danger sm" disabled={setRepMut.isPending}
                onClick={() => setRepMut.mutate(null)}>حذف نماینده</button>
            )}
            <button className="btn primary sm" disabled={setRepMut.isPending || !selRepId}
              onClick={() => setRepMut.mutate(selRepId)}>ذخیره</button>
          </>
        }
      >
        <label className="block text-sm text-slate-600 mb-2">انتخاب نماینده</label>
        <GlobalSearchableSelect
          options={repOptions}
          value={selRepId}
          onChange={(v) => setSelRepId(v === null || v === '' ? null : Number(v))}
          placeholder={repsQuery.isLoading ? 'در حال بارگذاری…' : 'جستجوی نماینده…'}
        />
      </Modal>
    </div>
  );
}
