import { renderHook, waitFor, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, afterEach, Mock } from 'vitest';
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useCustomConfig } from '../useCustomConfig';
import { utilsService } from '../../services/utilsService';
import { useToastStore } from '../../../../store/toastStore';
import { useAuthStore } from '../../../../store/authStore';
import { UserConfig } from '../../../../types';

vi.mock('../../services/utilsService');
vi.mock('../../../../store/toastStore');
vi.mock('../../../../store/authStore');

const makeWrapper = () => {
  const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={qc}>{children}</QueryClientProvider>
  );
};

const addToast = vi.fn();

const mockConfig = (): UserConfig => ({
  difficulty: 'Normal',
  theme: 'dark',
  notifications_enabled: true,
  ai_personality: 'friendly',
});

// useAuthStore is used as a selector: useAuthStore((s) => s.isAuthenticated)
const setAuthenticated = (value: boolean) => {
  (useAuthStore as unknown as Mock).mockImplementation(
    (selector: (state: { isAuthenticated: boolean }) => unknown) =>
      selector({ isAuthenticated: value })
  );
};

describe('useCustomConfig', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    (useToastStore as unknown as Mock).mockReturnValue({ addToast });
    setAuthenticated(true);
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('fetches config when authenticated', async () => {
    const config = mockConfig();
    (utilsService.getConfig as Mock).mockResolvedValue(config);

    const { result } = renderHook(() => useCustomConfig(), { wrapper: makeWrapper() });

    await waitFor(() => expect(result.current.isLoading).toBe(false));
    expect(result.current.config).toEqual(config);
    expect(utilsService.getConfig).toHaveBeenCalled();
  });

  it('does not fetch config when not authenticated', async () => {
    setAuthenticated(false);
    (utilsService.getConfig as Mock).mockResolvedValue(mockConfig());

    renderHook(() => useCustomConfig(), { wrapper: makeWrapper() });

    // query disabled -> service never invoked
    await waitFor(() => expect(utilsService.getConfig).not.toHaveBeenCalled());
  });

  it('posts new config through utilsService and shows a success toast on save', async () => {
    (utilsService.getConfig as Mock).mockResolvedValue(mockConfig());
    (utilsService.updateConfig as Mock).mockResolvedValue({});

    const { result } = renderHook(() => useCustomConfig(), { wrapper: makeWrapper() });
    await waitFor(() => expect(result.current.isLoading).toBe(false));

    await act(async () => {
      result.current.saveConfig({ theme: 'light' });
    });

    await waitFor(() => expect(addToast).toHaveBeenCalledWith('Paramètres enregistrés !', 'success'));
    expect(utilsService.updateConfig).toHaveBeenCalledWith({ theme: 'light' });
  });
});
