import React, { useEffect, useRef, useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useNavigate, useSearchParams } from 'react-router-dom';
import {
  PlusIcon, ChevronRightIcon, ChevronLeftIcon, ChevronDownIcon, CalendarDaysIcon,
  AdjustmentsHorizontalIcon, ClockIcon, MagnifyingGlassIcon, CheckCircleIcon,
} from '@heroicons/react/24/outline';
import { toast } from 'sonner';
import { api } from '../lib/api';
import type { PaginatedResponse, ApiResponse } from '../lib/api';
import type { Appointment } from '../types';
import { formatDate, toGregorianDate, formatTime, digitsOnly, sanitizeMobileInput, rialToToman, tomanToRial, formatRial } from '../lib/utils';
import PriceInput from '../components/ui/PriceInput';
import Modal from '../components/ui/Modal';
import { useAuthStore } from '../stores/authStore';
import { useClinicContext } from '../hooks/useClinicContext';
import Pagination from '../components/ui/Pagination';
import AppointmentFiltersModal, { applyAppointmentFilters, EMPTY_FILTERS } from '../components/AppointmentFiltersModal';
import type { AppointmentFilters } from '../components/AppointmentFiltersModal';
import PersianCalendar from '../components/ui/PersianCalendar';
import SearchableSelect from '../components/ui/SearchableSelect';
// اجزای طرح نوبت‌های tauri
import TurnsStatInfo from '../components/appointments/TurnsStatInfo';
import TurnsViewToggle from '../components/appointments/TurnsViewToggle';
import type { TurnsViewMode } from '../components/appointments/TurnsViewToggle';
import DoctorTabs from '../components/appointments/DoctorTabs';
import TurnsTimeline from '../components/appointments/TurnsTimeline';
import TurnsTable from '../components/appointments/TurnsTable';
import ServiceSlotPicker from '../components/appointments/ServiceSlotPicker';
import { useDoctorBookingServices } from '../hooks/useDoctorBookingServices';
import { CANCELLED_STATUSES } from '../components/appointments/turnStatus';
import type { TimelineSlot } from '../components/appointments/types';

const EMPTY_ARR: Appointment[] = [];

// ─────────────────────────────────────────────────────────────────────────────
// Date Navigator (طرح tauri — ناوبری روزانه)
// ─────────────────────────────────────────────────────────────────────────────

const WEEK_DAYS_FA = ['یک‌شنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنج‌شنبه', 'جمعه', 'شنبه'];

function getPersianWeekDay(gregorianDate: string): string {
  return WEEK_DAYS_FA[new Date(gregorianDate + 'T12:00:00').getDay()];
}

const navBtnSx: React.CSSProperties = {
  background: 'var(--surface)', border: '1px solid var(--border)',
  borderRadius: 'var(--r-sm)', height: 36, width: 36,
  display: 'flex', alignItems: 'center', justifyContent: 'center',
  cursor: 'pointer', color: 'var(--text-2)',
};

function DateNavigator({ date, onChange }: { date: string; onChange: (d: string) => void }) {
  const [showCal, setShowCal] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  const weekDay = getPersianWeekDay(date);
  const isFriday = new Date(date + 'T12:00:00').getDay() === 5;

  function addDays(n: number) {
    const d = new Date(date + 'T12:00:00');
    d.setDate(d.getDate() + n);
    onChange(toGregorianDate(d));
  }

  return (
    <div ref={ref} style={{ display: 'flex', alignItems: 'center', gap: 2, position: 'relative' }}>
      <button className="btn sm" style={navBtnSx} onClick={() => addDays(1)}>
        <ChevronRightIcon style={{ width: 15, height: 15 }} />
      </button>
      <div style={{
        padding: '0 14px', height: 44,
        display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
        background: 'var(--surface)', border: '1px solid var(--border)',
        borderRadius: 'var(--r-sm)', minWidth: 130, gap: 1,
      }}>
        <span style={{ fontSize: 13, fontWeight: 700, lineHeight: 1.2, color: isFriday ? '#ef4444' : 'var(--text)' }}>
          {weekDay}
        </span>
        <span style={{ fontSize: 11, color: 'var(--text-2)', lineHeight: 1.2 }}>
          {formatDate(date)}
        </span>
      </div>
      <button className="btn sm" style={navBtnSx} onClick={() => addDays(-1)}>
        <ChevronLeftIcon style={{ width: 15, height: 15 }} />
      </button>
      <button className="btn sm" style={navBtnSx} onClick={() => setShowCal(c => !c)}>
        <CalendarDaysIcon style={{ width: 15, height: 15 }} />
      </button>
      {showCal && (
        <PersianCalendar value={date} onChange={v => { onChange(v); setShowCal(false); }} onClose={() => setShowCal(false)} />
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Quick booking modal (کلیک روی اسلات خالیِ زمانبندی)
// ─────────────────────────────────────────────────────────────────────────────

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

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

export function NewAppointmentModal({
  slot, onClose, onSuccess, serviceMode = false, services = [], date, clinicUuid = null,
}: {
  slot: BookingSlot;
  onClose: () => void;
  onSuccess: () => void;
  serviceMode?: boolean;
  services?: import('../hooks/useDoctorBookingServices').BookingService[];
  date?: string;
  /** محل نوبت — بدون آن backend نوبت را به مطب شخصی نسبت می‌دهد. */
  clinicUuid?: string | null;
}) {
  const [mobile, setMobile] = useState('');
  const [lookup, setLookup] = useState<PatientLookup | null>(null);
  const [patientName, setPatientName] = useState('');
  const [nationalCode, setNationalCode] = useState('');
  // معیار جستجوی بیمار: موبایل یا کد ملی.
  const [searchBy, setSearchBy] = useState<'mobile' | 'national'>('mobile');
  const [pick, setPick] = useState<{ serviceUuids: string[]; slot: { start: number; end: number } | null }>({ serviceUuids: [], slot: null });

  const role = useAuthStore(s => s.primaryRole);
  const createEndpoint = role === 'admin' ? '/api/v1/admin/appointment' : '/api/v1/my/appointment';

  // هزینه ویزیت — الزامی بودن از تنظیمات «الزامی کردن هزینه ویزیت». فیلد UI تومان،
  // API ریالی (visit_price_rials). بدون این مقدار، وقتی فلگ فعال است backend خطای ۴۲۲ می‌دهد.
  //
  // قیمت باید از تنظیمات نوبت‌دهیِ *پزشکِ همین اسلات* بیاید، نه از entity کاربر جاری؛
  // منشی/کلینیک قیمت خودشان را ندارند و فیلد صفر می‌ماند. اگر دسترسی به تنظیمات آن
  // پزشک نبود (۴۰۳)، به تنظیمات خودِ کاربر برمی‌گردیم تا فلگ الزامی‌بودن از دست نرود.
  type Pricing = { data: { free_visit_price_rials: number; require_visit_price: boolean } };
  const doctorPricingQ = useQuery<Pricing>({
    queryKey: ['insurance-pricing', slot.doctor_uuid],
    queryFn: () => api.get(`/api/v1/insurance-pricing?doctor_uuid=${encodeURIComponent(slot.doctor_uuid)}`),
    retry: false,
  });
  const selfPricingQ = useQuery<Pricing>({
    queryKey: ['insurance-pricing'],
    queryFn: () => api.get('/api/v1/insurance-pricing'),
    enabled: doctorPricingQ.isError,
  });
  const pricing = (doctorPricingQ.data ?? selfPricingQ.data) as any;
  const freeVisit = pricing?.data?.free_visit_price_rials ?? 0;
  const requireVisit = pricing?.data?.require_visit_price ?? false;
  const pricingLoading = doctorPricingQ.isLoading || selfPricingQ.isLoading;
  const [visitPriceToman, setVisitPriceToman] = useState(0);
  const [visitPriceTouched, setVisitPriceTouched] = useState(false);
  useEffect(() => {
    if (!visitPriceTouched && freeVisit > 0) setVisitPriceToman(rialToToman(freeVisit));
  }, [freeVisit, visitPriceTouched]);
  // هزینه ویزیت اختیاری داخل کلپسِ بسته می‌نشیند؛ وقتی الزامی است کلپس همیشه باز است.
  const [visitPriceOpen, setVisitPriceOpen] = useState(false);
  const visitPriceExpanded = requireVisit || visitPriceOpen;

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

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

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

  const mutation = useMutation({
    mutationFn: () => api.post(createEndpoint, {
      doctor_uuid: slot.doctor_uuid,
      slot_start: serviceMode ? pick.slot!.start : slot.start,
      slot_end: serviceMode ? pick.slot!.end : slot.end,
      patient_mobile: mobile,
      patient_name: effectiveName,
      patient_national_code: effectiveNationalCode,
      ...(clinicUuid ? { clinic_uuid: clinicUuid } : {}),
      ...(serviceMode ? { service_item_uuids: pick.serviceUuids } : {}),
      ...(visitPriceToman > 0 ? { visit_price_rials: tomanToRial(visitPriceToman) } : {}),
    }),
    onSuccess: () => {
      toast.success('نوبت با موفقیت ثبت شد');
      onSuccess();
      onClose();
    },
    onError: (e: any) => {
      const msg = e?.response?.data?.errors?.[0]?.message ?? e?.message ?? 'خطا در ثبت نوبت';
      toast.error(msg);
    },
  });

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

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

  return (
    <Modal
      open
      title="ثبت نوبت"
      size="sm"
      onClose={onClose}
      footer={
        <>
          <button className="btn ghost" onClick={onClose}>انصراف</button>
          <button
            className="btn primary"
            onClick={() => mutation.mutate()}
            disabled={!isValid || mutation.isPending}
          >
            {mutation.isPending ? 'در حال ثبت…' : 'ثبت نوبت'}
          </button>
        </>
      }
    >
      {/* اسلات انتخاب‌شده */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 10, marginBottom: 18,
        padding: '10px 14px', borderRadius: 'var(--r-sm)', background: 'var(--primary-soft)',
      }}>
        <ClockIcon style={{ width: 18, height: 18, color: 'var(--primary-700)', flexShrink: 0 }} />
        <span style={{ fontSize: 13.5, color: 'var(--text)' }}>
          {serviceMode ? slot.doctor_name : `${slot.start_time} تا ${slot.end_time}`}
        </span>
        {!serviceMode && (
          <span style={{ fontSize: 12.5, color: 'var(--text-2)', marginInlineStart: 'auto' }}>
            {slot.doctor_name}
          </span>
        )}
      </div>

      {serviceMode && date && (
        <div style={{ marginBottom: 18 }}>
          <ServiceSlotPicker
            doctorUuid={slot.doctor_uuid}
            date={date}
            services={services}
            onSelect={setPick}
            clinicUuidOverride={clinicUuid}
          />
        </div>
      )}

      <div className="field-block" style={{ marginBottom: 14 }}>
        <label>جستجوی بیمار <span className="req">*</span></label>
        {/* انتخاب معیار جستجو: موبایل یا کد ملی */}
        <div style={{ display: 'flex', gap: 6, marginBottom: 8 }}>
          {(['mobile', 'national'] as const).map(mode => (
            <button
              key={mode}
              type="button"
              className={`btn sm ${searchBy === mode ? 'primary' : 'ghost'}`}
              onClick={() => onSwitchSearchBy(mode)}
              style={{ flex: 1 }}
            >
              {mode === 'mobile' ? 'شماره موبایل' : 'کد ملی'}
            </button>
          ))}
        </div>
        <div style={{ display: 'flex', gap: 8 }}>
          <div className="field" style={{ flex: 1 }}>
            {searchBy === 'mobile' ? (
              <input
                type="tel"
                inputMode="numeric"
                maxLength={11}
                value={mobile}
                onChange={e => onMobileChange(e.target.value)}
                onKeyDown={e => { if (e.key === 'Enter' && searchValid && !search.isPending) search.mutate(); }}
                placeholder="مثال: 09123456789"
                style={{ direction: 'ltr' }}
                autoFocus
              />
            ) : (
              <input
                type="text"
                inputMode="numeric"
                lang="en"
                maxLength={10}
                value={nationalCode}
                onChange={e => onNationalSearchChange(e.target.value)}
                onKeyDown={e => { if (e.key === 'Enter' && searchValid && !search.isPending) search.mutate(); }}
                placeholder="کد ملی ۱۰ رقمی"
                style={{ direction: 'ltr' }}
                autoFocus
              />
            )}
          </div>
          <button
            className="btn soft"
            onClick={() => search.mutate()}
            disabled={!searchValid || search.isPending}
            style={{ whiteSpace: 'nowrap' }}
          >
            <MagnifyingGlassIcon style={{ width: 16, height: 16 }} />
            {search.isPending ? '...' : 'جستجو'}
          </button>
        </div>
      </div>

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

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

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

// ─────────────────────────────────────────────────────────────────────────────
// Main Page — «نوبت‌ ها» (طرح tauri)
// ─────────────────────────────────────────────────────────────────────────────

interface TodayStats { total: number; completed: number; waiting: number; cancelled: number; }

export default function AppointmentsPage() {
  const navigate = useNavigate();
  const primaryRole = useAuthStore(s => s.primaryRole);
  const dbUuid = useAuthStore(s => s.dbUuid);
  // در محیط کلینیک، dbUuid شناسهٔ کلینیک است نه پزشک؛ uuid پزشک فقط از doctorUuid می‌آید.
  const doctorUuid = useAuthStore(s => s.doctorUuid);
  const clinicUuid = useClinicContext();
  const isAdmin = primaryRole === 'admin';
  const isClinic = primaryRole === 'clinic';
  const isDoctor = primaryRole === 'doctor';
  const isRepresentation = primaryRole === 'representation';

  const [params] = useSearchParams();
  const today = new Date().toISOString().slice(0, 10);
  // پس از ویرایش/ثبت، صفحه با ?date=... باز می‌شود تا همان روز نمایش داده شود.
  const [selectedDate, setSelectedDate] = useState(params.get('date') || today);
  const [viewMode, setViewMode] = useState<TurnsViewMode>('timeline');
  const [selectedDoctorUuid, setSelectedDoctorUuid] = useState<string>(isDoctor ? (doctorUuid ?? '') : '');
  const [bookingSlot, setBookingSlot] = useState<BookingSlot | null>(null);
  const [filtersOpen, setFiltersOpen] = useState(false);
  const [filters, setFilters] = useState<AppointmentFilters>(EMPTY_FILTERS);
  const qc = useQueryClient();

  // ── Today stats
  const statsQuery = useQuery<ApiResponse<TodayStats>>({
    queryKey: ['appt-today-stats', selectedDate, isAdmin],
    queryFn: () => api.get(
      isAdmin
        ? `/api/v1/admin/appointments/today-stats?date=${selectedDate}`
        : `/api/v1/my/appointments/today-stats?date=${selectedDate}`
    ),
  });
  const stats = statsQuery.data?.data ?? { total: 0, completed: 0, waiting: 0, cancelled: 0 };

  // ── Appointments query
  const apptEndpoint = isAdmin
    ? '/api/v1/admin/appointments'
    : isRepresentation
      ? '/api/v1/representation/appointments'
      : '/api/v1/my/appointments';
  const apptQueryKey = ['appointments', apptEndpoint, selectedDate, selectedDoctorUuid];
  const apptParams = new URLSearchParams({ date: selectedDate, limit: '500' });
  if (selectedDoctorUuid) apptParams.set('doctor_uuid', selectedDoctorUuid);

  const apptQuery = useQuery<PaginatedResponse<Appointment>>({
    queryKey: apptQueryKey,
    queryFn: () => api.get(`${apptEndpoint}?${apptParams}`),
  });
  const appointments: Appointment[] = apptQuery.data?.data ?? EMPTY_ARR;
  const filteredAppointments = applyAppointmentFilters(appointments, filters);
  const filtersActive = filters !== EMPTY_FILTERS && JSON.stringify(filters) !== JSON.stringify(EMPTY_FILTERS);

  // Table pagination — client-side (full day stays loaded for timeline + doctor tabs).
  const TABLE_PAGE_SIZE = 20;
  const [tablePage, setTablePage] = useState(1);
  useEffect(() => { setTablePage(1); }, [selectedDate, selectedDoctorUuid, filters]);
  const pagedAppointments = filteredAppointments.slice((tablePage - 1) * TABLE_PAGE_SIZE, tablePage * TABLE_PAGE_SIZE);

  // ── Clinic doctors (authoritative list for tabs)
  const clinicDoctorsQuery = useQuery<ApiResponse<{ data: { uuid: string; name: string }[] }>>({
    queryKey: ['clinic-doctors', dbUuid],
    queryFn: () => api.get(`/api/v1/clinic/doctor-list/${dbUuid}`),
    enabled: isClinic && !!dbUuid,
  });
  const clinicDoctorsList = clinicDoctorsQuery.data?.data?.data ?? [];

  const doctors = React.useMemo(() => {
    const map = new Map<string, string>();
    clinicDoctorsList.forEach(d => map.set(d.uuid, d.name));
    appointments.forEach(a => {
      if (a.doctor_uuid && a.doctor_name && !map.has(a.doctor_uuid)) {
        map.set(a.doctor_uuid, a.doctor_name);
      }
    });
    return Array.from(map.entries()).map(([uuid, name]) => ({ uuid, name }));
  }, [appointments, clinicDoctorsList]);

  // سرویس‌های موجود در نوبت‌های امروز (برای فیلتر «سرویس مورد نظر...»).
  const serviceOptions = React.useMemo(() => {
    const map = new Map<string, string>();
    appointments.forEach(a => {
      if (a.service_item?.uuid && a.service_item?.name) map.set(a.service_item.uuid, a.service_item.name);
    });
    return Array.from(map.entries()).map(([uuid, name]) => ({ uuid, name }));
  }, [appointments]);

  // نوع پروفایل: کلینیک چندپزشکه (تب دکترها + مدیریت چند پزشک) در برابر پزشک مستقل.
  // نقش clinic/admin = چندپزشکه؛ نقش doctor (حتی مهمانِ کلینیک) = مستقل، فقط برنامهٔ خودش.
  const isMultiDoctorClinic = isClinic || isAdmin;
  const showDoctorTabs = isMultiDoctorClinic && doctors.length >= 1;
  const showDoctorCol = isAdmin && !selectedDoctorUuid;

  // در کلینیک، اولین دکتر به‌صورت پیش‌فرض انتخاب می‌شود تا زمانبندی مثل طرح پر باشد.
  useEffect(() => {
    if (isClinic && !selectedDoctorUuid && doctors.length > 0) {
      setSelectedDoctorUuid(doctors[0].uuid);
    }
  }, [isClinic, selectedDoctorUuid, doctors]);

  // ── محل نوبت‌دهی برای ادمین
  // ادمین context کلینیکی ندارد (useClinicContext → null)؛ بدون clinic_uuid فقط برنامهٔ
  // مطب شخصی خوانده می‌شود. محل از booking-locations همان پزشک انتخاب می‌شود.
  const adminLocationsQuery = useQuery<ApiResponse<any>>({
    queryKey: ['booking-locations', selectedDoctorUuid],
    queryFn: () => api.get(`/api/v1/appointment-booking-locations/${selectedDoctorUuid}?management=1`),
    enabled: isAdmin && !!selectedDoctorUuid,
  });
  const adminLocations: any[] = (adminLocationsQuery.data?.data as any)?.booking_locations ?? EMPTY_ARR;
  const [adminLocKey, setAdminLocKey] = useState<string | null>(null);
  useEffect(() => { setAdminLocKey(null); }, [selectedDoctorUuid]);
  const locKey = (l: any) => l.clinic_uuid ?? 'personal';
  // پیش‌فرض = اولین آیتم؛ backend بر اساس زودترین نوبت آزاد مرتب کرده است.
  const adminLocation = adminLocations.find(l => locKey(l) === adminLocKey) ?? adminLocations[0] ?? null;
  const effectiveClinicUuid: string | null = isAdmin ? (adminLocation?.clinic_uuid ?? null) : clinicUuid;

  // ── Slots query (timeline)
  // effectiveClinicUuid باید در کلید کش باشد، وگرنه برنامهٔ یک محیط برای محیط دیگر نشان داده می‌شود.
  const slotsQueryKey = ['appt-slots', selectedDoctorUuid, selectedDate, effectiveClinicUuid];
  const slotsQuery = useQuery<ApiResponse<any>>({
    queryKey: slotsQueryKey,
    queryFn: () => api.get(
      `/api/v1/appointment-slots?doctor_uuid=${selectedDoctorUuid}&date=${selectedDate}&management=1` +
      (effectiveClinicUuid ? `&clinic_uuid=${encodeURIComponent(effectiveClinicUuid)}` : '')
    ),
    enabled: viewMode === 'timeline' && !!selectedDoctorUuid,
  });

  // ── روش نوبت‌دهی پزشکِ انتخاب‌شده (سرویسی/اسلاتی)
  const { bookingMode, services } = useDoctorBookingServices(
    selectedDoctorUuid,
    isAdmin ? (adminLocation?.clinic_uuid ?? null) : undefined,
  );
  const serviceMode = bookingMode === 'service';

  // بازهٔ کاری پزشک در این روز (برای هدرِ تایم‌لاینِ سرویسی).
  const workingRange = React.useMemo(() => {
    const rawSessions: any[] = (slotsQuery.data?.data as any)?.sessions ?? [];
    if (!rawSessions.length) return null;
    const starts = rawSessions.map(s => s.start_time).filter(Boolean).sort();
    const ends = rawSessions.map(s => s.end_time).filter(Boolean).sort();
    if (!starts.length || !ends.length) return null;
    return { start: starts[0], end: ends[ends.length - 1] };
  }, [slotsQuery.data]);

  // ── Merge sessions + appointments → flat timeline slots
  const timelineSlots: TimelineSlot[] = React.useMemo(() => {
    if (viewMode !== 'timeline') return [];

    const activeByStart = new Map<number, Appointment>();
    const cancelledByStart = new Map<number, Appointment>();
    appointments.forEach(a => {
      const key = typeof a.slot_start === 'number' ? a.slot_start : parseInt(String(a.slot_start), 10);
      if (CANCELLED_STATUSES.has(a.status)) {
        const existing = cancelledByStart.get(key);
        if (!existing || a.created_at > existing.created_at) cancelledByStart.set(key, a);
      } else {
        activeByStart.set(key, a);
      }
    });

    // حالت سرویسی: اسلات ثابت وجود ندارد — برای هر شیفتِ کاری، نوبت‌های رزروشده
    // نمایش داده می‌شوند و باقیِ زمان به‌صورت بازه‌(های) خالیِ قابل‌رزرو بین آن‌ها.
    if (serviceMode) {
      const fmt = (ts: number) => formatTime(ts);
      const parseHM = (t: string) => { const [h, m] = (t ?? '00:00').split(':').map(Number); return (h * 3600) + (m * 60); };
      const rawSessions: any[] = (slotsQuery.data?.data as any)?.sessions ?? [];
      const booked = [...activeByStart.values()].sort((a, b) => Number(a.slot_start) - Number(b.slot_start));
      const now = Math.floor(Date.now() / 1000);

      const out: TimelineSlot[] = [];
      // بازهٔ خالیِ قابل‌رزرو؛ اگر روی «اکنون» بیفتد به اکنون بریده می‌شود و بازه‌های
      // کاملاً گذشته حذف می‌شوند تا فقط زمان قابل‌رزرو باقی بماند.
      const pushFree = (start: number, end: number) => {
        const s = start < now ? now : start;
        if (end <= s) return;
        out.push({ start: s, end, start_time: fmt(s), end_time: fmt(end), is_available: true, appointment: null, cancelled_appointment: null });
      };

      rawSessions.forEach((session: any) => {
        const slots = (session.slots as any[]) ?? [];
        if (!slots.length) return;
        const dayStart = Number(slots[0].start) - parseHM(session.start_time);
        const winStart = dayStart + parseHM(session.start_time);
        const winEnd   = dayStart + parseHM(session.end_time);

        const inWin = booked.filter(a => Number(a.slot_start) >= winStart && Number(a.slot_start) < winEnd);
        let cursor = winStart;
        inWin.forEach(a => {
          const s = Number(a.slot_start), e = Number(a.slot_end);
          if (s > cursor) pushFree(cursor, s);
          out.push({
            start: s, end: e, start_time: fmt(s), end_time: fmt(e),
            is_available: false, appointment: a, cancelled_appointment: null,
          });
          cursor = Math.max(cursor, e);
        });
        if (cursor < winEnd) pushFree(cursor, winEnd);
      });
      return out;
    }

    const rawSessions: any[] = (slotsQuery.data?.data as any)?.sessions ?? [];
    const out: TimelineSlot[] = [];
    rawSessions.forEach((session: any) => {
      (session.slots as any[]).forEach((s: any) => {
        const slotStart = typeof s.start === 'number' ? s.start : parseInt(String(s.start), 10);
        const slotEnd = typeof s.end === 'number' ? s.end : parseInt(String(s.end), 10);
        out.push({
          start: slotStart,
          end: slotEnd,
          start_time: s.start_time ?? formatTime(slotStart),
          end_time: s.end_time ?? formatTime(slotEnd),
          is_available: s.is_available as boolean,
          appointment: activeByStart.get(slotStart) ?? null,
          cancelled_appointment: cancelledByStart.get(slotStart) ?? null,
        });
      });
    });
    return out;
  }, [viewMode, slotsQuery.data, appointments, serviceMode]);

  // ── Slot click → quick booking modal
  function handleSlotClick(slot: TimelineSlot) {
    if (isRepresentation) return;
    const doctorName = doctors.find(d => d.uuid === selectedDoctorUuid)?.name ?? '';
    setBookingSlot({
      start: slot.start,
      end: slot.end,
      start_time: slot.start_time,
      end_time: slot.end_time,
      doctor_uuid: selectedDoctorUuid,
      doctor_name: doctorName,
    });
  }

  function openDetail(a: Appointment) {
    navigate(`/admin/appointments/${a.uuid}`);
  }

  return (
    <div style={{ padding: '20px 24px' }}>
      <div style={{ maxWidth: 1050, margin: '0 auto' }}>
        {/* عنوان */}
        <h1 style={{ fontSize: 20, fontWeight: 700, color: 'var(--text)', marginBottom: 16 }}>نوبت‌ ها</h1>

        {/* نوار آمار */}
        <TurnsStatInfo stats={stats} />

        {/* نوار ابزار (بیرونِ کارت، مطابق طرح) */}
        <div style={{
          display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', marginBottom: 16,
        }}>
          {/* سمت راست: تاریخ + سرویس + سوییچ نما (مطابق طرح) */}
          <DateNavigator date={selectedDate} onChange={setSelectedDate} />

          <ServiceFilterSelect
            value={filters.itemUuid}
            options={serviceOptions}
            onChange={(v) => setFilters(f => ({ ...f, itemUuid: v }))}
          />

          {isAdmin && adminLocations.length > 1 && (
            <div style={{ minWidth: 220 }}>
              <SearchableSelect
                options={adminLocations.map((l: any) => ({
                  value: locKey(l),
                  label: l.type === 'personal' ? `مطب شخصی${l.title ? ` — ${l.title}` : ''}` : l.title,
                }))}
                value={adminLocation ? locKey(adminLocation) : null}
                onChange={v => setAdminLocKey(v ? String(v) : null)}
                placeholder="محل نوبت‌دهی..."
                height={44}
              />
            </div>
          )}

          <TurnsViewToggle viewMode={viewMode} onChange={setViewMode} />

          <div style={{ flex: 1 }} />

          {/* سمت چپ: فیلتر + افزودن نوبت */}
          <button
            aria-label="فیلترها"
            className="btn sm"
            onClick={() => setFiltersOpen(true)}
            style={{
              border: `1px solid ${filtersActive ? 'var(--primary)' : 'var(--border)'}`,
              color: filtersActive ? 'var(--primary)' : 'var(--text-2)',
              background: 'var(--surface)',
            }}
          >
            <AdjustmentsHorizontalIcon style={{ width: 16 }} />
          </button>

          {!isRepresentation && (
            <button
              className="btn primary sm"
              onClick={() => {
                const q = selectedDoctorUuid ? `?doctor=${selectedDoctorUuid}&date=${selectedDate}` : `?date=${selectedDate}`;
                navigate(`/admin/appointments/new${q}`);
              }}
              style={{ display: 'flex', alignItems: 'center', gap: 5 }}
            >
              <PlusIcon style={{ width: 15, height: 15 }} />
              افزودن نوبت
            </button>
          )}
        </div>

        {/* کارت اصلی — تب دکترها (هدر) + محتوا */}
        <div style={{
          background: 'var(--surface)', border: '1px solid var(--border)',
          borderRadius: 'var(--r)', overflow: 'hidden',
        }}>
          {showDoctorTabs && (
            <DoctorTabs doctors={doctors} selected={selectedDoctorUuid} onSelect={setSelectedDoctorUuid} showAll={isAdmin} />
          )}
          <div style={{ padding: 16 }}>
            {viewMode === 'table' ? (
              <>
                <TurnsTable
                  items={pagedAppointments}
                  loading={apptQuery.isLoading}
                  queryKey={apptQueryKey}
                  showDoctor={showDoctorCol}
                />
                {filteredAppointments.length > TABLE_PAGE_SIZE && (
                  <div style={{ marginTop: 14 }}>
                    <Pagination page={tablePage} total={filteredAppointments.length} limit={TABLE_PAGE_SIZE} onPageChange={setTablePage} />
                  </div>
                )}
              </>
            ) : (
              <>
                {!selectedDoctorUuid ? (
                  <div style={{ padding: 40, textAlign: 'center', color: 'var(--text-3)' }}>
                    برای نمایش زمانبندی، ابتدا یک پزشک انتخاب کنید
                  </div>
                ) : (
                  <>
                    {serviceMode && (
                      <div style={{
                        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8,
                        padding: '8px 12px', marginBottom: 12, borderRadius: 'var(--r-sm)',
                        background: 'var(--surface-2)', border: '1px solid var(--border)', fontSize: 12.5, color: 'var(--text-2)',
                      }}>
                        <span>نوبت‌دهی سرویسی — نوبت‌ها بر اساس مدت سرویس چیده می‌شوند.</span>
                        {workingRange && (
                          <span dir="ltr" style={{ color: 'var(--text-3)' }}>ساعت کاری: {workingRange.start} - {workingRange.end}</span>
                        )}
                      </div>
                    )}
                    <TurnsTimeline
                      slots={timelineSlots}
                      loading={apptQuery.isLoading || slotsQuery.isLoading}
                      queryKey={apptQueryKey}
                      onView={openDetail}
                      onBook={handleSlotClick}
                      emptyReason={(slotsQuery.data?.data as any)?.empty_reason ?? null}
                      errorMessage={slotsQuery.isError ? ((slotsQuery.error as Error)?.message || 'خطای نامشخص') : null}
                    />
                  </>
                )}
              </>
            )}
          </div>
        </div>
      </div>

      {/* مودال ثبت سریع نوبت */}
      {bookingSlot && (
        <NewAppointmentModal
          slot={bookingSlot}
          serviceMode={serviceMode}
          services={services}
          date={selectedDate}
          clinicUuid={effectiveClinicUuid}
          onClose={() => setBookingSlot(null)}
          onSuccess={() => {
            qc.invalidateQueries({ queryKey: apptQueryKey });
            qc.invalidateQueries({ queryKey: slotsQueryKey });
            qc.invalidateQueries({ queryKey: ['appt-today-stats', selectedDate] });
          }}
        />
      )}

      {/* مودال فیلترها */}
      {filtersOpen && (
        <AppointmentFiltersModal value={filters} onApply={setFilters} onClose={() => setFiltersOpen(false)} />
      )}
    </div>
  );
}

/** فیلتر سرویس نوار ابزار — ردیف‌های بارگذاری‌شدهٔ روز را بر اساس سرویس فیلتر می‌کند. */
function ServiceFilterSelect({ value, options, onChange }: {
  value: string; options: { uuid: string; name: string }[]; onChange: (v: string) => void;
}) {
  return (
    <div style={{ minWidth: 280 }}>
      <SearchableSelect
        options={options.map(s => ({ value: s.uuid, label: s.name }))}
        value={value || null}
        onChange={v => onChange(v ? String(v) : '')}
        placeholder="سرویس مورد نظر را انتخاب کنید..."
        isClearable
        height={44}
      />
    </div>
  );
}
