import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';
import { PlaylistForm } from './PlaylistForm';
import { useCreatePlaylist, useUpdatePlaylist } from '../hooks/usePlaylist';
import type { Playlist } from '../types';

// Mock ResizeObserver
global.ResizeObserver = vi.fn().mockImplementation(() => ({
  observe: vi.fn(),
  unobserve: vi.fn(),
  disconnect: vi.fn(),
}));

// Mock hooks
vi.mock('../hooks/usePlaylist', () => ({
  useCreatePlaylist: vi.fn(),
  useUpdatePlaylist: vi.fn(),
}));

// Mock useToast
vi.mock('@/hooks/useToast', () => ({
  useToast: () => ({
    toast: vi.fn(),
  }),
}));

// Helper pour créer un QueryClient pour chaque test
function createWrapper() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
      mutations: {
        retry: false,
      },
    },
  });

  return ({ children }: { children: React.ReactNode }) => (
    <BrowserRouter>
      <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
    </BrowserRouter>
  );
}

describe('PlaylistForm', () => {
  const mockCreateMutation = {
    mutateAsync: vi.fn(),
    isPending: false,
  };

  const mockUpdateMutation = {
    mutateAsync: vi.fn(),
    isPending: false,
  };

  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(useCreatePlaylist).mockReturnValue(mockCreateMutation as any);
    vi.mocked(useUpdatePlaylist).mockReturnValue(mockUpdateMutation as any);
  });

  it('should render form with default values for create mode', () => {
    render(<PlaylistForm />, { wrapper: createWrapper() });

    expect(screen.getByLabelText(/Titre/)).toBeInTheDocument();
    expect(screen.getByLabelText(/Description/)).toBeInTheDocument();
    expect(screen.getByLabelText(/URL de la couverture/)).toBeInTheDocument();
    expect(screen.getByLabelText(/Playlist publique/)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /Créer/ })).toBeInTheDocument();
  });

  it('should render form with playlist data for edit mode', () => {
    const playlist: Playlist = {
      id: 1,
      user_id: 1,
      title: 'My Playlist',
      description: 'A test playlist',
      is_public: false,
      cover_url: 'https://example.com/cover.jpg',
      track_count: 5,
      created_at: '2024-01-01T00:00:00Z',
      updated_at: '2024-01-01T00:00:00Z',
    };

    render(<PlaylistForm playlist={playlist} />, { wrapper: createWrapper() });

    expect(screen.getByDisplayValue('My Playlist')).toBeInTheDocument();
    expect(screen.getByDisplayValue('A test playlist')).toBeInTheDocument();
    expect(
      screen.getByDisplayValue('https://example.com/cover.jpg'),
    ).toBeInTheDocument();
    expect(
      screen.getByRole('button', { name: /Enregistrer/ }),
    ).toBeInTheDocument();
  });

  it('should validate required title field', async () => {
    const user = userEvent.setup();
    render(<PlaylistForm />, { wrapper: createWrapper() });

    const submitButton = screen.getByRole('button', { name: /Créer/ });
    await user.click(submitButton);

    await waitFor(() => {
      expect(screen.getByText('Le titre est requis')).toBeInTheDocument();
    });
  });

  it('should validate title max length', async () => {
    const user = userEvent.setup();
    render(<PlaylistForm />, { wrapper: createWrapper() });

    const titleInput = screen.getByLabelText(/Titre/);
    await user.type(titleInput, 'a'.repeat(201));

    const submitButton = screen.getByRole('button', { name: /Créer/ });
    await user.click(submitButton);

    await waitFor(() => {
      expect(
        screen.getByText('Le titre ne peut pas dépasser 200 caractères'),
      ).toBeInTheDocument();
    });
  });

  it('should validate description max length', async () => {
    const user = userEvent.setup();
    render(<PlaylistForm />, { wrapper: createWrapper() });

    const descriptionInput = screen.getByLabelText(/Description/);
    await user.type(descriptionInput, 'a'.repeat(2001));

    const submitButton = screen.getByRole('button', { name: /Créer/ });
    await user.click(submitButton);

    await waitFor(() => {
      expect(
        screen.getByText('La description ne peut pas dépasser 2000 caractères'),
      ).toBeInTheDocument();
    });
  });

  it('should validate cover URL format', async () => {
    const user = userEvent.setup();
    render(<PlaylistForm />, { wrapper: createWrapper() });

    const titleInput = screen.getByLabelText(/Titre/);
    await user.type(titleInput, 'Valid Title');

    const coverUrlInput = screen.getByLabelText(/URL de la couverture/);
    await user.clear(coverUrlInput);
    await user.type(coverUrlInput, 'invalid-url');

    const submitButton = screen.getByRole('button', { name: /Créer/ });
    await user.click(submitButton);

    await waitFor(
      () => {
        const errorMessage = screen.queryByText(
          /URL de la couverture doit être valide/i,
        );
        expect(errorMessage).toBeInTheDocument();
      },
      { timeout: 3000 },
    );
  });

  it('should create playlist on submit in create mode', async () => {
    const user = userEvent.setup();
    mockCreateMutation.mutateAsync.mockResolvedValue({});

    render(<PlaylistForm />, { wrapper: createWrapper() });

    const titleInput = screen.getByLabelText(/Titre/);
    await user.type(titleInput, 'New Playlist');

    const descriptionInput = screen.getByLabelText(/Description/);
    await user.type(descriptionInput, 'A new playlist');

    const submitButton = screen.getByRole('button', { name: /Créer/ });
    await user.click(submitButton);

    await waitFor(() => {
      expect(mockCreateMutation.mutateAsync).toHaveBeenCalledWith({
        title: 'New Playlist',
        description: 'A new playlist',
        is_public: true,
        cover_url: undefined,
      });
    });
  });

  it('should update playlist on submit in edit mode', async () => {
    const user = userEvent.setup();
    const playlist: Playlist = {
      id: 1,
      user_id: 1,
      title: 'My Playlist',
      description: 'A test playlist',
      is_public: true,
      track_count: 5,
      created_at: '2024-01-01T00:00:00Z',
      updated_at: '2024-01-01T00:00:00Z',
    };

    mockUpdateMutation.mutateAsync.mockResolvedValue(playlist);

    render(<PlaylistForm playlist={playlist} />, { wrapper: createWrapper() });

    const titleInput = screen.getByDisplayValue('My Playlist');
    await user.clear(titleInput);
    await user.type(titleInput, 'Updated Playlist');

    const submitButton = screen.getByRole('button', { name: /Enregistrer/ });
    await user.click(submitButton);

    await waitFor(() => {
      expect(mockUpdateMutation.mutateAsync).toHaveBeenCalledWith({
        id: 1,
        data: {
          title: 'Updated Playlist',
          description: 'A test playlist',
          is_public: true,
          cover_url: undefined,
        },
      });
    });
  });

  it('should call custom onSubmit if provided', async () => {
    const user = userEvent.setup();
    const customOnSubmit = vi.fn().mockResolvedValue(undefined);

    render(<PlaylistForm onSubmit={customOnSubmit} />, {
      wrapper: createWrapper(),
    });

    const titleInput = screen.getByLabelText(/Titre/);
    await user.type(titleInput, 'New Playlist');

    const submitButton = screen.getByRole('button', { name: /Créer/ });
    await user.click(submitButton);

    await waitFor(() => {
      expect(customOnSubmit).toHaveBeenCalledWith({
        title: 'New Playlist',
        description: undefined,
        is_public: true,
        cover_url: undefined,
      });
      expect(mockCreateMutation.mutateAsync).not.toHaveBeenCalled();
    });
  });

  it('should handle cancel callback', async () => {
    const user = userEvent.setup();
    const onCancel = vi.fn();

    render(<PlaylistForm onCancel={onCancel} />, { wrapper: createWrapper() });

    const cancelButton = screen.getByRole('button', { name: /Annuler/ });
    await user.click(cancelButton);

    expect(onCancel).toHaveBeenCalled();
  });

  it('should show loading state during submission', async () => {
    mockCreateMutation.isPending = true;
    vi.mocked(useCreatePlaylist).mockReturnValue(mockCreateMutation as any);

    render(<PlaylistForm />, { wrapper: createWrapper() });

    const submitButton = screen.getByRole('button', { name: /Créer/ });
    expect(submitButton).toBeDisabled();
  });

  it('should use custom submit label', () => {
    render(<PlaylistForm submitLabel="Sauvegarder" />, {
      wrapper: createWrapper(),
    });

    expect(
      screen.getByRole('button', { name: /Sauvegarder/ }),
    ).toBeInTheDocument();
  });
});
