import React from 'react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ThemeProvider } from 'styled-components';
import { MemoryRouter } from 'react-router-dom';
import { I18nextProvider } from 'react-i18next';
import { AxiosError } from 'axios';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import '@testing-library/jest-dom';
import SettingsPage from '@/features/settings/pages/SettingsPage';
import * as settingsService from '@/features/settings/services/settingsService';
import type { SettingItem } from '@/features/settings/types/settings.types';
import i18n from '@/locales/i18n';
import { LocaleProvider } from '@/app/providers/LocaleProvider';
import { lightTheme } from '@/styles/theme';
import { useAuthStore } from '@/features/auth/store/authStore';

vi.mock('@/features/settings/services/settingsService');

vi.mock('@/shared/components/layout/AppLayout', () => ({
  AppLayout: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));

const mockSetting: SettingItem = {
  key: 'ticketing.auto_assign',
  value: true,
  group: 'general',
  label: 'Auto assign tickets',
  description: 'Automatically assign new tickets',
  type: 'boolean',
  default_value: false,
  is_public: false,
  is_encrypted: false,
  source: 'department',
  source_scope_id: 'dept-1',
};

function renderSettingsPage() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

  return render(
    <QueryClientProvider client={queryClient}>
      <I18nextProvider i18n={i18n}>
        <LocaleProvider>
          <ThemeProvider theme={lightTheme}>
            <MemoryRouter>
              <SettingsPage />
            </MemoryRouter>
          </ThemeProvider>
        </LocaleProvider>
      </I18nextProvider>
    </QueryClientProvider>,
  );
}

describe('SettingsPage', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    void i18n.changeLanguage('en');
    useAuthStore.setState({
      user: {
        id: 'user-1',
        name: 'Admin User',
        email: 'admin@test.com',
        locale: 'en',
        company_id: 'co-1',
        company: {
          id: 'co-1',
          name: 'ACME',
          domain: 'acme.test',
          is_active: true,
          created_at: '',
          updated_at: '',
        },
        departments: [
          {
            id: 'dept-1',
            company_id: 'co-1',
            name: 'IT',
            is_active: true,
            created_at: '',
            updated_at: '',
          },
        ],
        roles: [
          {
            id: 1,
            name: 'Admin',
            display_name: 'Admin',
            guard_name: 'api',
            scope: 'company',
            created_at: '',
            updated_at: '',
          },
        ],
        permissions: [],
        is_active: true,
        created_at: '',
        updated_at: '',
      },
      token: 'token',
      isAuthenticated: true,
      isLoading: false,
    });
    vi.mocked(settingsService.fetchSettingGroups).mockResolvedValue(['general']);
  });

  it('shows loading state while fetching settings', async () => {
    vi.mocked(settingsService.fetchSettings).mockReturnValue(new Promise(() => {}));

    renderSettingsPage();

    expect(await screen.findByText(/loading settings/i)).toBeInTheDocument();
    expect(screen.getByRole('status')).toBeInTheDocument();
  });

  it('renders settings on success', async () => {
    vi.mocked(settingsService.fetchSettings).mockResolvedValue({
      [mockSetting.key]: mockSetting,
    });

    renderSettingsPage();

    await waitFor(() => {
      expect(screen.getByText('Auto assign tickets')).toBeInTheDocument();
    });
    expect(screen.getByText(/department override/i)).toBeInTheDocument();
  });

  it('updates a setting successfully', async () => {
    vi.mocked(settingsService.fetchSettings).mockResolvedValue({
      [mockSetting.key]: mockSetting,
    });
    vi.mocked(settingsService.updateSetting).mockResolvedValue();

    renderSettingsPage();

    await waitFor(() => {
      expect(screen.getByText('Auto assign tickets')).toBeInTheDocument();
    });

    fireEvent.click(screen.getByRole('button', { name: /save/i }));

    await waitFor(() => {
      expect(settingsService.updateSetting).toHaveBeenCalledWith(
        mockSetting.key,
        true,
        'department',
        { scope: 'department', departmentId: 'dept-1' },
      );
    });
  });

  it('resets a setting to inherited value', async () => {
    vi.mocked(settingsService.fetchSettings).mockResolvedValue({
      [mockSetting.key]: mockSetting,
    });
    vi.mocked(settingsService.resetSetting).mockResolvedValue();

    renderSettingsPage();

    await waitFor(() => {
      expect(screen.getByText('Auto assign tickets')).toBeInTheDocument();
    });

    fireEvent.click(screen.getByRole('button', { name: /reset to inherited/i }));

    await waitFor(() => {
      expect(settingsService.resetSetting).toHaveBeenCalledWith(
        mockSetting.key,
        'department',
        { scope: 'department', departmentId: 'dept-1' },
      );
    });
  });

  it('shows forbidden state on 403', async () => {
    const forbidden = new AxiosError('Forbidden');
    forbidden.response = {
      status: 403,
      statusText: 'Forbidden',
      data: {},
      headers: {},
      config: {} as never,
    };
    vi.mocked(settingsService.fetchSettings).mockRejectedValue(forbidden);

    renderSettingsPage();

    await waitFor(() => {
      expect(
        screen.getByText(/you do not have permission to view or change these settings/i),
      ).toBeInTheDocument();
    });
  });
});
