import React, { useCallback, useMemo, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import PageHeader from '../components/ui/PageHeader';
import SearchableSelect from '../components/ui/SearchableSelect';
import HoldCountdown from '../components/HoldCountdown';
import { formatDate, formatNumber } from '../lib/utils';
import { useBranches } from '../hooks/useBranches';
import { useAllServiceItems } from '../hooks/useServiceCatalog';
import {
  REASON_LABELS,
  useAvailabilitySearch,
  useHold,
  type AvailableSlot,
  type HoldResult,
} from '../hooks/useResourceBooking';
import { api, ApiError, type ApiResponse } from '../lib/api';
import { useQuery } from '@tanstack/react-query';

const DAY = 86400;

function timeOf(ts: number): string {
  return new Date(ts * 1000).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' });
}

/**
 * رزرو نوبت در حالت منبع‌محور.
 *
 * سه مرحلهٔ جدا و عمداً جدا: جستجو → گرفتن موقت → ثبت نهایی. بین دومی و سومی یک مهلت
 * هست و صفحه آن را با شمارش معکوس نشان می‌دهد؛ رزروی که در سکوت منقضی شود، اپراتور را
 * با یک ۴۰۹ بی‌توضیح تنها می‌گذارد.
 */
export default function ResourceBookingPage() {
  const navigate = useNavigate();
  const [params] = useSearchParams();
  const { branches } = useBranches();
  const { items: services } = useAllServiceItems();
  const { create, release, confirm, rebook } = useHold();

  /**
   * حالت جابه‌جایی — همان سه مرحله، با یک تفاوت در گام آخر.
   *
   * صفحهٔ جدا نساختیم چون جستجو و رزرو موقت دقیقاً همان‌اند؛ چیزی که فرق می‌کند فقط
   * این است که در پایان به‌جای «ثبت نوبت تازه»، نوبت موجود جابه‌جا می‌شود و زمان قدیم
   * در همان درخواست آزاد می‌شود.
   */
  const rebookUuid = params.get('rebook');

  const [serviceUuid, setServiceUuid] = useState('');
  const [branchUuid, setBranchUuid] = useState('');
  const [days, setDays] = useState('7');
  const [searching, setSearching] = useState(false);

  const [doctorUuid, setDoctorUuid] = useState('');
  const [hold, setHold] = useState<HoldResult | null>(null);
  const [expired, setExpired] = useState(false);

  /** جایگزینی منبع per نقش، فقط برای همان زمانِ انتخاب‌شده. */
  const [picked, setPicked] = useState<Record<string, string[]> | null>(null);
  const [pickedSlot, setPickedSlot] = useState<AvailableSlot | null>(null);

  /**
   * پزشکِ نوبت — ثبت نهایی بدونش ممکن نیست.
   *
   * منشی از اندپوینت احرازشده می‌گیرد تا فقط پزشکان تخصیص‌یافته‌اش بیایند؛ همان
   * قاعده‌ای که صفحهٔ نوبت‌ها از قبل دارد.
   */
  const doctorsQuery = useQuery<ApiResponse<{ data: { uuid: string; name: string }[] }>>({
    queryKey: ['booking-doctors'],
    queryFn: () => api.get('/api/v1/my/clinic-doctors'),
    staleTime: 60_000,
  });

  const doctors = doctorsQuery.data?.data?.data ?? [];

  const range = useMemo(() => {
    const from = Math.floor(Date.now() / 1000);
    return { from, to: from + Number(days) * DAY };
  }, [days]);

  const { result, loading, error, refetch } = useAvailabilitySearch(
    { serviceUuid, branchUuid, from: range.from, to: range.to },
    searching,
  );

  const onExpired = useCallback(() => setExpired(true), []);

  const chooseSlot = (slot: AvailableSlot) => {
    setPickedSlot(slot);
    setPicked(
      Object.fromEntries(
        Object.entries(slot.assignment).map(([role, resources]) => [role, resources.map((r) => r.uuid)]),
      ),
    );
  };

  /**
   * گزینه‌های جایگزین یک نقش: منابعی که در **همین زمان** پیشنهاد شده‌اند.
   *
   * فهرست کاملِ منابع شعبه اینجا غلط است — منبعی که موتور برای این زمان نداده، آزاد
   * نبوده، و نشان دادنش یعنی اپراتور چیزی انتخاب کند که ۴۰۹ می‌گیرد.
   */
  const optionsFor = (role: string): { value: string; label: string }[] =>
    (pickedSlot?.assignment[role] ?? []).map((r) => ({ value: r.uuid, label: r.name }));

  const takeHold = async () => {
    if (!pickedSlot || !picked) return;

    try {
      const created = await create.mutateAsync({
        service_uuid: serviceUuid,
        branch_uuid: branchUuid,
        start: pickedSlot.start,
        assignment: picked,
      });

      setHold(created.data);
      setExpired(false);
    } catch (e) {
      // ۴۰۹ یعنی همین لحظه کس دیگری گرفت. گفتنش کافی نیست: کاربر باید بلافاصله
      // جایگزین ببیند، وگرنه باید دستی دوباره جستجو بزند و بختش را از نو امتحان کند.
      if (e instanceof ApiError && e.status === 409) {
        setPickedSlot(null);
        setPicked(null);
        await refetch();
      }
    }
  };

  const reasonText = result?.reason ? REASON_LABELS[result.reason] ?? result.reason : null;

  return (
    <div className="fade-in">
      <PageHeader
        title={rebookUuid ? 'جابه‌جایی نوبت' : 'رزرو نوبت منبع‌محور'}
        description={
          rebookUuid
            ? 'زمان تازه را انتخاب و نگه دارید؛ زمان قبلی در همان لحظهٔ جابه‌جایی آزاد می‌شود.'
            : 'وقت آزاد از تقاطع تقویم منابع می‌آید؛ هر وقت با منابع پیشنهادی خودش نمایش داده می‌شود.'
        }
        backTo={rebookUuid ? `/admin/appointments/${rebookUuid}` : '/admin/appointments'}
      />

      <div className="card card-pad" style={{ marginBottom: 16, display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'flex-end' }}>
        <div className="field-block" style={{ minWidth: 220, margin: 0 }}>
          <label>خدمت</label>
          <SearchableSelect
            value={serviceUuid}
            onChange={(v) => {
              setServiceUuid(String(v ?? ''));
              setPickedSlot(null);
            }}
            options={services.map((s) => ({ value: s.uuid, label: s.name }))}
            placeholder="انتخاب خدمت"
          />
        </div>

        <div className="field-block" style={{ minWidth: 200, margin: 0 }}>
          <label>شعبه</label>
          <SearchableSelect
            value={branchUuid}
            onChange={(v) => {
              setBranchUuid(String(v ?? ''));
              setPickedSlot(null);
            }}
            options={branches.map((b) => ({ value: b.uuid, label: b.name || 'بدون نام' }))}
            placeholder="انتخاب شعبه"
          />
        </div>

        <div className="field-block" style={{ minWidth: 160, margin: 0 }}>
          <label>بازه</label>
          <SearchableSelect
            value={days}
            onChange={(v) => setDays(String(v ?? '7'))}
            options={[
              { value: '7', label: 'یک هفته' },
              { value: '30', label: 'یک ماه' },
              { value: '90', label: 'سه ماه' },
            ]}
          />
        </div>

        <button
          type="button"
          className="btn primary sm"
          disabled={serviceUuid === '' || branchUuid === '' || loading}
          onClick={() => setSearching(true)}
        >
          جستجوی وقت
        </button>
      </div>

      {error && (
        <div className="card card-pad" style={{ marginBottom: 16, fontSize: 13, color: 'var(--danger)' }}>
          {error instanceof ApiError ? error.message : 'جستجوی وقت ناموفق بود'}
        </div>
      )}

      {result && (
        <div className="card card-pad" style={{ marginBottom: 16, display: 'flex', flexDirection: 'column', gap: 10 }}>
          <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', fontSize: 14 }}>
            <span>
              مدت نوبت: <strong>{formatNumber(result.plan.total_minutes)}</strong> دقیقه
            </span>
            <span style={{ color: 'var(--text-2)' }}>
              {formatNumber(result.plan.segments.length)} بخش · {formatNumber(result.slots.length)} وقت پیدا شد
            </span>
          </div>

          {/* فهرست خالی خطا نیست؛ دلیلش را می‌گوییم تا کاربر حدس نزند. */}
          {result.slots.length === 0 && reasonText && (
            <span style={{ fontSize: 13, color: 'var(--warning)' }}>{reasonText}</span>
          )}
        </div>
      )}

      {result && result.slots.length > 0 && (
        <div style={{ overflowX: 'auto' }} className="card card-pad">
          <table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse', minWidth: 520 }}>
            <thead>
              <tr style={{ color: 'var(--text-3)', textAlign: 'right' }}>
                <th style={{ padding: '8px 6px', fontWeight: 500 }}>تاریخ</th>
                <th style={{ padding: '8px 6px', fontWeight: 500 }}>ساعت</th>
                <th style={{ padding: '8px 6px', fontWeight: 500 }}>منابع پیشنهادی</th>
                <th style={{ padding: '8px 6px', fontWeight: 500 }} />
              </tr>
            </thead>
            <tbody>
              {result.slots.slice(0, 100).map((slot) => (
                <tr
                  key={slot.start}
                  style={{
                    borderTop: '1px solid var(--border)',
                    background: pickedSlot?.start === slot.start ? 'var(--primary-soft)' : undefined,
                  }}
                >
                  <td style={{ padding: '8px 6px' }}>{formatDate(slot.start)}</td>
                  <td style={{ padding: '8px 6px' }} dir="ltr">
                    {timeOf(slot.start)} – {timeOf(slot.end)}
                  </td>
                  <td style={{ padding: '8px 6px', color: 'var(--text-2)' }}>
                    {Object.values(slot.assignment)
                      .flat()
                      .map((r) => r.name)
                      .join('، ')}
                  </td>
                  <td style={{ padding: '8px 6px' }}>
                    <button
                      type="button"
                      className="btn secondary sm"
                      disabled={hold !== null}
                      onClick={() => chooseSlot(slot)}
                    >
                      انتخاب
                    </button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {pickedSlot && (
        <div className="card card-pad" style={{ marginTop: 16, display: 'flex', flexDirection: 'column', gap: 12 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
            <h3 style={{ fontSize: 15, margin: 0 }}>
              {formatDate(pickedSlot.start)} · <span dir="ltr">{timeOf(pickedSlot.start)}</span>
            </h3>
            {hold && !expired && <HoldCountdown expiresAt={hold.expires_at} onExpired={onExpired} />}
            {expired && (
              <span className="badge red">
                <span className="bdot" />
                مهلت تمام شد — دوباره جستجو کنید
              </span>
            )}
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {Object.entries(pickedSlot.assignment).map(([role, resources]) => (
              <div key={role} style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
                <span style={{ fontSize: 13, minWidth: 90, color: 'var(--text-2)' }}>{role}</span>
                <div style={{ minWidth: 220 }}>
                  <SearchableSelect
                    value={picked?.[role]?.[0] ?? resources[0]?.uuid ?? ''}
                    onChange={(v) =>
                      setPicked((prev) => ({ ...(prev ?? {}), [role]: [String(v ?? '')] }))
                    }
                    options={optionsFor(role)}
                    isDisabled={hold !== null}
                  />
                </div>
              </div>
            ))}
          </div>

          <span style={{ fontSize: 12, color: 'var(--text-3)' }}>
            فهرست هر نقش فقط منابعی است که در همین زمان آزادند؛ تخصیص منابع به بیمار
            نمایش داده نمی‌شود.
          </span>

          {/* در جابه‌جایی پزشک عوض نمی‌شود؛ پرسیدنش یعنی دعوت به تغییری که خواسته نشده. */}
          {!rebookUuid && (
            <div className="field-block" style={{ maxWidth: 280, margin: 0 }}>
              <label>پزشک نوبت</label>
              <SearchableSelect
                value={doctorUuid}
                onChange={(v) => setDoctorUuid(String(v ?? ''))}
                options={doctors.map((d) => ({ value: d.uuid, label: d.name }))}
                placeholder="انتخاب پزشک"
              />
            </div>
          )}

          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            {hold === null ? (
              <button
                type="button"
                className="btn primary"
                disabled={create.isPending}
                onClick={takeHold}
              >
                نگه‌داشتن این زمان
              </button>
            ) : (
              <>
                {rebookUuid ? (
                  <button
                    type="button"
                    className="btn primary"
                    disabled={expired || rebook.isPending}
                    onClick={async () => {
                      await rebook.mutateAsync({
                        appointmentUuid: rebookUuid,
                        holdUuid: hold.hold_uuid,
                      });
                      navigate(`/admin/appointments/${rebookUuid}`);
                    }}
                  >
                    جابه‌جایی به این زمان
                  </button>
                ) : (
                  <button
                    type="button"
                    className="btn primary"
                    disabled={expired || doctorUuid === '' || confirm.isPending}
                    onClick={async () => {
                      await confirm.mutateAsync({ hold_uuid: hold.hold_uuid, doctor_uuid: doctorUuid });
                      navigate('/admin/appointments');
                    }}
                  >
                    ثبت نهایی نوبت
                  </button>
                )}

                <button
                  type="button"
                  className="btn secondary"
                  disabled={release.isPending}
                  onClick={async () => {
                    await release.mutateAsync(hold.hold_uuid);
                    setHold(null);
                    setPickedSlot(null);
                  }}
                >
                  آزادکردن
                </button>
              </>
            )}
          </div>
        </div>
      )}
    </div>
  );
}
