import { describe, it, expect, beforeEach, vi } from 'vitest';
import { screen, fireEvent, waitFor } from '@testing-library/react';
import { Routes, Route } from 'react-router-dom';
import { renderWithProviders } from '../test/utils';

vi.mock('../lib/api', () => ({
  api: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), put: vi.fn(), delete: vi.fn() },
  ApiError: class extends Error {},
}));

import { api } from '../lib/api';
import AppointmentEditPage from './AppointmentEditPage';

const get = api.get as ReturnType<typeof vi.fn>;
const post = api.post as ReturnType<typeof vi.fn>;
const patch = api.patch as ReturnType<typeof vi.fn>;

const slotStart = Math.floor(new Date('2026-08-01T15:00').getTime() / 1000);
const slotEnd = Math.floor(new Date('2026-08-01T15:30').getTime() / 1000);
const newStart = Math.floor(new Date('2026-08-01T16:00').getTime() / 1000);

/**
 * `bookingMode` روی محلِ خودِ نوبت است، پس پاسخ `appointment-booking-services` را با
 * همان `doctor_uuid` و بدون `clinic_uuid` (مطب شخصی) می‌دهیم.
 */
function mockApi(opts: { mode: 'slot' | 'service'; isReserve?: boolean; serviceItems?: any[] }) {
  get.mockImplementation((url: string) => {
    if (url === '/api/v1/appointment/ap1') return Promise.resolve({ success: true, data: { data: {
      uuid: 'ap1', slot_start: slotStart, slot_end: slotEnd, status: 'confirmed', version: 4,
      doctor: { uuid: 'doc1', name: 'دکتر تست' },
      clinic_uuid: null,
      is_reserve: opts.isReserve ?? false,
      service_total_minutes: opts.mode === 'service' ? 30 : null,
      service_buffer_minutes: opts.mode === 'service' ? 10 : null,
      service_section: { uuid: 'sec1', name: 'زیبایی' },
      service_item: { uuid: 'it1', name: 'لیزر' },
      service_items: opts.serviceItems ?? [{ uuid: 'it1', name: 'لیزر' }],
      staff: { uuid: 'st1', full_name: 'سحر ایمانی' },
    } } });
    if (url.startsWith('/api/v1/appointment-booking-services/')) {
      return Promise.resolve({ success: true, data: {
        booking_mode: opts.mode,
        buffer_minutes: 10,
        services: [
          { uuid: 'it1', name: 'لیزر', duration_minutes: 30, price_rials: 0, service_section: { uuid: 'sec1', name: 'زیبایی' } },
          { uuid: 'it2', name: 'تزریق', duration_minutes: 15, price_rials: 0, service_section: { uuid: 'sec1', name: 'زیبایی' } },
        ],
      } });
    }
    if (url.startsWith('/api/v1/appointment-service-slots')) {
      return Promise.resolve({ success: true, data: {
        total_duration_minutes: 30,
        buffer_minutes: 10,
        start_times: [{ start: newStart, end: newStart + 1800, start_time: '16:00' }],
      } });
    }
    if (url === '/api/v1/service-sections') return Promise.resolve({ success: true, data: [{ uuid: 'sec1', name: 'زیبایی' }] });
    if (url.startsWith('/api/v1/service-items/')) return Promise.resolve({ success: true, data: [{ uuid: 'it1', name: 'لیزر' }] });
    if (url === '/api/v1/staff') return Promise.resolve({ success: true, data: [{ uuid: 'st1', full_name: 'سحر ایمانی' }] });
    return Promise.resolve({ success: true, data: [] });
  });
}

function renderEdit() {
  return renderWithProviders(
    <Routes><Route path="/admin/appointments/:uuid/edit" element={<AppointmentEditPage />} /></Routes>,
    { route: '/admin/appointments/ap1/edit' },
  );
}

beforeEach(() => {
  get.mockReset(); post.mockReset(); patch.mockReset();
  post.mockResolvedValue({ success: true, data: {} });
  patch.mockResolvedValue({ success: true, data: {} });
});

describe('AppointmentEditPage — حالت نوبت‌دهی سرویسی', () => {
  it('ورودی دستی ساعت را پنهان می‌کند و انتخابگر سرویس را نشان می‌دهد', async () => {
    mockApi({ mode: 'service' });
    renderEdit();

    await screen.findByText('مشخصات سرویس:');
    // انتخابگر سرویس هم برچسب «بخش» دارد؛ ظاهر شدن دو تا نشانهٔ mount شدنش است.
    await waitFor(() => expect(screen.getAllByText('بخش')).toHaveLength(2));

    expect(screen.queryByLabelText('ساعت شروع')).not.toBeInTheDocument();
    expect(screen.queryByLabelText('ساعت پایان')).not.toBeInTheDocument();
  });

  it('دکمهٔ ثبت تا انتخاب زمان غیرفعال است و راهنمای فارسی دارد', async () => {
    mockApi({ mode: 'service' });
    renderEdit();
    await screen.findByText('مشخصات سرویس:');

    expect(screen.getByRole('button', { name: 'ثبت اطلاعات' })).toBeDisabled();
    expect(screen.getByText('برای ثبت، سرویس و سپس یکی از زمان‌های خالی را انتخاب کنید.')).toBeInTheDocument();
  });

  it('exclude_appointment_uuid را به درخواست اسلات‌ها می‌دهد', async () => {
    mockApi({ mode: 'service' });
    renderEdit();
    await screen.findByText('مشخصات سرویس:');

    await waitFor(() => {
      const calls = get.mock.calls.map((c) => String(c[0]));
      expect(calls.some((u) => u.includes('appointment-service-slots') && u.includes('exclude_appointment_uuid=ap1'))).toBe(true);
    });
  });

  it('ثبت، service-reschedule را بدون هیچ مدتی صدا می‌زند', async () => {
    mockApi({ mode: 'service' });
    renderEdit();
    await screen.findByText('مشخصات سرویس:');

    fireEvent.click(await screen.findByRole('button', { name: '16:00' }));
    fireEvent.click(screen.getByRole('button', { name: 'ثبت اطلاعات' }));

    await waitFor(() => expect(post).toHaveBeenCalledWith(
      '/api/v1/appointment/ap1/service-reschedule',
      expect.objectContaining({ start: newStart, service_item_uuids: ['it1'] }),
    ));

    const body = post.mock.calls[0][1] as Record<string, unknown>;
    expect(body).not.toHaveProperty('slot_end');
    expect(body).not.toHaveProperty('total_duration_minutes');
  });

  it('PATCH بعدی زمان و نسخه را نمی‌فرستد (نسخه یک قدم جلو رفته)', async () => {
    mockApi({ mode: 'service' });
    renderEdit();
    await screen.findByText('مشخصات سرویس:');

    fireEvent.click(await screen.findByRole('button', { name: '16:00' }));
    fireEvent.click(screen.getByRole('button', { name: 'ثبت اطلاعات' }));

    await waitFor(() => expect(patch).toHaveBeenCalled());
    const body = patch.mock.calls[0][1] as Record<string, unknown>;
    expect(body).not.toHaveProperty('slot_start');
    expect(body).not.toHaveProperty('slot_end');
    expect(body).not.toHaveProperty('version');
    expect(body).toHaveProperty('deposit_required');
  });

  it('سرویسِ غیرفعالِ نوبت را با هشدار فارسی گزارش می‌کند', async () => {
    mockApi({ mode: 'service', serviceItems: [{ uuid: 'it1', name: 'لیزر' }, { uuid: 'gone', name: 'حذف‌شده' }] });
    renderEdit();
    await screen.findByText('مشخصات سرویس:');

    expect(await screen.findByText(/۱ سرویس این نوبت دیگر برای نوبت‌دهی فعال نیست|1 سرویس این نوبت دیگر برای نوبت‌دهی فعال نیست/))
      .toBeInTheDocument();
  });
});

describe('AppointmentEditPage — خط سرخ: حالت اسلاتی و رزرو', () => {
  it('حالت اسلاتی سه فیلد ساعت را حفظ می‌کند و انتخابگر ندارد', async () => {
    mockApi({ mode: 'slot' });
    renderEdit();
    await screen.findByText('مشخصات سرویس:');

    expect((screen.getByLabelText('ساعت شروع') as HTMLInputElement).value).toBe('15:00');
    expect(screen.getByLabelText('ساعت پایان')).toBeInTheDocument();
    expect(screen.queryByText('زمان‌های خالی پیشنهادی')).not.toBeInTheDocument();
  });

  it('حالت اسلاتی همان PATCH قبلی با version را می‌فرستد', async () => {
    mockApi({ mode: 'slot' });
    renderEdit();
    await screen.findByText('مشخصات سرویس:');
    // پیش از کلیک باید هیدریت شده باشد، وگرنه toEpoch('','') مقدار NaN می‌سازد.
    await waitFor(() => expect((screen.getByLabelText('ساعت شروع') as HTMLInputElement).value).toBe('15:00'));

    fireEvent.click(screen.getByRole('button', { name: 'ثبت اطلاعات' }));

    await waitFor(() => expect(patch).toHaveBeenCalledWith('/api/v1/appointment/ap1', expect.objectContaining({
      slot_start: slotStart,
      slot_end: slotEnd,
      version: 4,
    })));
    expect(post).not.toHaveBeenCalled();
  });

  it('نوبت رزرو در محل سرویسی هم انتخابگر زمان نمی‌گیرد', async () => {
    mockApi({ mode: 'service', isReserve: true });
    renderEdit();
    await screen.findByText('مشخصات سرویس:');

    expect(screen.getByLabelText('ساعت شروع')).toBeInTheDocument();
    expect(screen.queryByText('زمان‌های خالی پیشنهادی')).not.toBeInTheDocument();
  });
});
