import { describe, it, expect, beforeEach, vi } from 'vitest';
import { screen, waitFor } from '@testing-library/react';
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 {},
}));

vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }));

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

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

const resource = (key: string, label: string, actions: [string, string][]) => ({
  key,
  label,
  clinic_only: false,
  actions: actions.map(([k, l]) => ({ key: k, label: l })),
});

const BASE_CATALOG = [
  resource('appointments', 'مدیریت نوبت‌ها', [['view', 'مشاهده'], ['create', 'ایجاد']]),
  resource('patients', 'پرونده بیماران', [['view', 'مشاهده'], ['delete', 'حذف']]),
  resource('services', 'خدمات و تعرفه‌ها', [['view', 'مشاهده']]),
];

const permissionPayload = {
  uuid: 'p1',
  clinic_uuid: 'c1',
  doctor_uuid: 'd1',
  doctor_name: 'دکتر تست',
  active: true,
  permissions: { version: 1, resources: { appointments: { view: true, create: false } } },
};

function mockApi(catalog: unknown[]) {
  get.mockImplementation((url: string) => {
    if (url.includes('permission-catalog')) {
      return Promise.resolve({ success: true, data: { version: 1, resources: catalog } });
    }
    return Promise.resolve({ success: true, data: permissionPayload });
  });
}

function render(catalog: unknown[] = BASE_CATALOG) {
  mockApi(catalog);
  return renderWithProviders(
    <DoctorPermissionsModal clinicUuid="c1" doctorUuid="d1" doctorName="دکتر تست" onClose={() => {}} />,
  );
}

describe('DoctorPermissionsModal', () => {
  beforeEach(() => vi.clearAllMocks());

  it('renders one row per catalog resource', async () => {
    render();

    expect(await screen.findByText('مدیریت نوبت‌ها')).toBeInTheDocument();
    expect(screen.getByText('پرونده بیماران')).toBeInTheDocument();
    expect(screen.getByText('خدمات و تعرفه‌ها')).toBeInTheDocument();
  });

  /**
   * قلبِ «داینامیک بودن»: منبع تازه فقط به mock اضافه می‌شود و هیچ خطی از کد
   * کامپوننت عوض نمی‌شود. اگر این تست بشکند یعنی فهرست دوباره هاردکد شده.
   */
  it('shows a resource added to the catalog with no code change', async () => {
    render([...BASE_CATALOG, resource('brand_new_page', 'صفحهٔ کاملاً تازه', [['view', 'مشاهده']])]);

    expect(await screen.findByText('صفحهٔ کاملاً تازه')).toBeInTheDocument();
  });

  it('renders a switch only for actions the resource actually has', async () => {
    render();

    // patients در کاتالوگ view و delete دارد، ولی create ندارد.
    expect(await screen.findByLabelText('پرونده بیماران — حذف')).toBeInTheDocument();
    expect(screen.queryByLabelText('پرونده بیماران — ایجاد')).not.toBeInTheDocument();
  });

  it('reflects stored values and defaults missing ones to off', async () => {
    render();

    await waitFor(() => expect(screen.getByLabelText('مدیریت نوبت‌ها — مشاهده')).toBeChecked());
    expect(screen.getByLabelText('مدیریت نوبت‌ها — ایجاد')).not.toBeChecked();
    // services اصلاً در JSONِ ذخیره‌شده نیست
    expect(screen.getByLabelText('خدمات و تعرفه‌ها — مشاهده')).not.toBeChecked();
  });

  it('shows loading rather than an empty table while the catalog is in flight', () => {
    get.mockImplementation(() => new Promise(() => {}));

    renderWithProviders(
      <DoctorPermissionsModal clinicUuid="c1" doctorUuid="d1" doctorName="دکتر تست" onClose={() => {}} />,
    );

    expect(screen.getByText('در حال بارگذاری...')).toBeInTheDocument();
  });
});
