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

vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }));
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 { toast } from 'sonner';
import PatientTreatmentTab from './PatientTreatmentTab';

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

const CASE_UUID = 'case-1';

const summary = (over: Record<string, unknown> = {}) => ({
  uuid: CASE_UUID,
  status: 'active',
  total_sessions: 5,
  completed_sessions: 3,
  service: { uuid: 'svc-1', name: 'لیزر ناحیه ۱' },
  ...over,
});

/** فهرست دوره‌ها، تقویم دوره، و بقیهٔ GETها. */
function mockApi(caseOver: Record<string, unknown> = {}) {
  get.mockImplementation((url: string) => {
    if (url.startsWith('/api/v1/treatment-cases')) {
      return Promise.resolve({ success: true, data: [summary(caseOver)] });
    }
    if (url.includes('/plan')) {
      return Promise.resolve({ success: true, data: {
        case: { ...summary(caseOver), patient_national_code: null },
        resource: null,
        sessions: [],
      } });
    }
    return Promise.resolve({ success: true, data: [] });
  });
}

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

/** دورهٔ انتخاب‌شده در URL می‌نشیند؛ تست هم از همان مسیر شروع می‌کند. */
const render = () => renderWithProviders(
  <PatientTreatmentTab recordUuid="rec-1" />,
  { route: `/admin/patients/p-1?case=${CASE_UUID}` },
);

const plusBtn = () => screen.getByRole('button', { name: 'افزودن یک جلسه' });
const minusBtn = () => screen.getByRole('button', { name: 'کم کردن یک جلسه' });

/**
 * تعداد جلسات سرِ پروندهٔ بیمار تصمیم گرفته می‌شود، ولی تا این تغییر فقط از صفحهٔ
 * «دوره‌های درمان» و پشت یک مودال قابل تغییر بود.
 */
describe('PatientTreatmentTab — تعداد جلسات', () => {
  it('یک جلسه اضافه می‌کند', async () => {
    render();

    fireEvent.click(await screen.findByRole('button', { name: 'افزودن یک جلسه' }));

    await waitFor(() => expect(patch).toHaveBeenCalledWith(
      `/api/v1/treatment-case/${CASE_UUID}`, { total_sessions: 6 },
    ));
  });

  it('یک جلسه کم می‌کند', async () => {
    render();

    fireEvent.click(await screen.findByRole('button', { name: 'کم کردن یک جلسه' }));

    await waitFor(() => expect(patch).toHaveBeenCalledWith(
      `/api/v1/treatment-case/${CASE_UUID}`, { total_sessions: 4 },
    ));
  });

  it('وقتی همهٔ جلسه‌ها انجام شده، کم کردن قفل است', async () => {
    mockApi({ total_sessions: 4, completed_sessions: 4 });
    render();

    await waitFor(() => expect(minusBtn()).toBeDisabled());
    expect(screen.getByText(/همهٔ جلسه‌ها انجام شده/)).toBeInTheDocument();
    // افزودن همچنان آزاد است.
    expect(plusBtn()).not.toBeDisabled();
  });

  it('کف دو جلسه رعایت می‌شود', async () => {
    mockApi({ total_sessions: 2, completed_sessions: 0 });
    render();

    await waitFor(() => expect(minusBtn()).toBeDisabled());
    expect(screen.getByText(/کمتر از ۲ جلسه ممکن نیست/)).toBeInTheDocument();
  });

  it('سقف شصت جلسه رعایت می‌شود', async () => {
    mockApi({ total_sessions: 60, completed_sessions: 0 });
    render();

    await waitFor(() => expect(plusBtn()).toBeDisabled());
    expect(minusBtn()).not.toBeDisabled();
  });

  it('خطای سرور را همان‌طور که آمده نشان می‌دهد', async () => {
    // سرور دقیق‌تر از هر متن ثابتی می‌گوید چند جلسه قفل است.
    patch.mockRejectedValue(new Error('۳ جلسه انجام شده یا نوبت دارد؛ تعداد کمتر از آن ممکن نیست'));
    render();

    fireEvent.click(await screen.findByRole('button', { name: 'کم کردن یک جلسه' }));

    await waitFor(() => expect(toast.error).toHaveBeenCalledWith(
      '۳ جلسه انجام شده یا نوبت دارد؛ تعداد کمتر از آن ممکن نیست',
    ));
  });

  it('پیشرفت دوره با نسبت واقعی اعلام می‌شود', async () => {
    render();

    const bar = await screen.findByRole('progressbar', { name: /پیشرفت دوره/ });

    expect(bar).toHaveAttribute('aria-valuenow', '3');
    expect(bar).toHaveAttribute('aria-valuemax', '5');
  });

  it('دکمهٔ ویرایش دوره برای بقیهٔ فیلدها هست', async () => {
    render();

    expect(await screen.findByRole('button', { name: /ویرایش دوره/ })).toBeInTheDocument();
  });
});
