/**
 * Tests d'intégration CRUD pour les playlists
 * T0463: Create Playlist CRUD Integration Tests Frontend
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter, Routes, Route } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { PlaylistListPage } from '../pages/PlaylistListPage';
import { PlaylistDetailPage } from '../pages/PlaylistDetailPage';
import { CreatePlaylistDialog } from '../components/CreatePlaylistDialog';
import { PlaylistForm } from '../components/PlaylistForm';
import * as playlistService from '../services/playlistService';
import type { Playlist, PlaylistListResponse } from '../types';

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

// Mock playlistService
vi.mock('../services/playlistService', () => ({
  listPlaylists: vi.fn(),
  getPlaylist: vi.fn(),
  createPlaylist: vi.fn(),
  updatePlaylist: vi.fn(),
  deletePlaylist: vi.fn(),
}));

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

// Mock useAuthStore
vi.mock('@/features/auth/store/authStore', () => ({
  useAuthStore: (selector: any) => {
    const state = {
      user: { id: 1, username: 'testuser' },
      isAuthenticated: true,
      isLoading: false,
    };
    return selector ? selector(state) : state;
  },
}));

// Mock TrackListContainer
vi.mock('@/features/tracks/components/TrackListContainer', () => ({
  TrackListContainer: ({ initialTracks }: { initialTracks: any[] }) => (
    <div data-testid="track-list-container">{initialTracks.length} tracks</div>
  ),
}));

// Helper pour créer un QueryClient pour chaque test
function createQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
        cacheTime: 0,
      },
      mutations: {
        retry: false,
      },
    },
  });
}

// Helper pour wrapper les composants avec les providers nécessaires
function renderWithProviders(
  component: React.ReactElement,
  initialEntries: string[] = ['/playlists'],
) {
  const queryClient = createQueryClient();

  return render(
    <QueryClientProvider client={queryClient}>
      <MemoryRouter initialEntries={initialEntries}>
        <Routes>
          <Route path="/playlists" element={component} />
          <Route path="/playlists/new" element={component} />
          <Route path="/playlists/:id" element={component} />
          <Route path="/playlists/:id/edit" element={component} />
        </Routes>
      </MemoryRouter>
    </QueryClientProvider>,
  );
}

describe('Playlist CRUD Integration Tests', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

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

  describe('Create Playlist', () => {
    it('should create a new playlist successfully', async () => {
      const user = userEvent.setup();
      const mockCreatePlaylist = vi.mocked(playlistService.createPlaylist);

      const newPlaylist: Playlist = {
        id: '1',
        user_id: '1',
        title: 'New Playlist',
        description: 'A new playlist',
        is_public: true,
        track_count: 0,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      mockCreatePlaylist.mockResolvedValue(newPlaylist);

      render(
        <QueryClientProvider client={createQueryClient()}>
          <CreatePlaylistDialog open={true} onOpenChange={vi.fn()} />
        </QueryClientProvider>,
      );

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

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

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

      // Vérifier que createPlaylist a été appelé
      await waitFor(() => {
        expect(mockCreatePlaylist).toHaveBeenCalledWith({
          title: 'New Playlist',
          description: 'A new playlist',
          is_public: true,
        });
      });
    });

    it('should show validation errors when creating playlist with invalid data', async () => {
      const user = userEvent.setup();

      render(
        <QueryClientProvider client={createQueryClient()}>
          <CreatePlaylistDialog open={true} onOpenChange={vi.fn()} />
        </QueryClientProvider>,
      );

      // Essayer de soumettre sans titre
      const submitButton = screen.getByRole('button', { name: /Créer/i });
      await user.click(submitButton);

      // Vérifier que l'erreur de validation est affichée
      await waitFor(() => {
        expect(screen.getByText('Le titre est requis')).toBeInTheDocument();
      });
    });
  });

  describe('Read Playlist', () => {
    it('should display playlist list', async () => {
      const mockListPlaylists = vi.mocked(playlistService.listPlaylists);

      const mockPlaylists: PlaylistListResponse = {
        playlists: [
          {
            id: '1',
            user_id: '1',
            title: 'Playlist 1',
            description: 'Description 1',
            is_public: true,
            track_count: 5,
            created_at: '2024-01-01T00:00:00Z',
            updated_at: '2024-01-01T00:00:00Z',
          },
          {
            id: '2',
            user_id: '1',
            title: 'Playlist 2',
            description: 'Description 2',
            is_public: false,
            track_count: 3,
            created_at: '2024-01-02T00:00:00Z',
            updated_at: '2024-01-02T00:00:00Z',
          },
        ],
        total: 2,
        page: 1,
        limit: 20,
      };

      mockListPlaylists.mockResolvedValue(mockPlaylists);

      renderWithProviders(<PlaylistListPage />, ['/playlists']);

      // Vérifier que la liste est affichée
      await waitFor(() => {
        expect(screen.getByText('Playlist 1')).toBeInTheDocument();
        expect(screen.getByText('Playlist 2')).toBeInTheDocument();
      });
    });

    it('should display playlist details', async () => {
      const mockGetPlaylist = vi.mocked(playlistService.getPlaylist);

      const mockPlaylist: Playlist = {
        id: '1',
        user_id: '1',
        title: 'My Playlist',
        description: 'A test playlist',
        is_public: true,
        cover_url: 'https://example.com/cover.jpg',
        track_count: 2,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
        tracks: [
          {
            id: '1',
            playlist_id: '1',
            track_id: '1',
            position: 1,
            added_at: '2024-01-01T00:00:00Z',
            track: {
              id: '1',
              title: 'Track 1',
              artist: 'Artist 1',
              duration: 180,
              file_path: '/path/to/track1.mp3',
            },
          },
        ],
      };

      mockGetPlaylist.mockResolvedValue(mockPlaylist);

      renderWithProviders(<PlaylistDetailPage />, ['/playlists/1']);

      // Vérifier que les détails sont affichés
      await waitFor(() => {
        expect(screen.getByText('My Playlist')).toBeInTheDocument();
        expect(screen.getByText('A test playlist')).toBeInTheDocument();
        expect(screen.getByText('Tracks (2)')).toBeInTheDocument();
      });
    });
  });

  describe('Update Playlist', () => {
    it('should update playlist successfully', async () => {
      const user = userEvent.setup();
      const mockUpdatePlaylist = vi.mocked(playlistService.updatePlaylist);

      const existingPlaylist: Playlist = {
        id: '1',
        user_id: '1',
        title: 'Original Title',
        description: 'Original description',
        is_public: true,
        track_count: 0,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      const updatedPlaylist: Playlist = {
        ...existingPlaylist,
        title: 'Updated Title',
        description: 'Updated description',
        updated_at: '2024-01-02T00:00:00Z',
      };

      mockUpdatePlaylist.mockResolvedValue(updatedPlaylist);

      render(
        <QueryClientProvider client={createQueryClient()}>
          <PlaylistForm playlist={existingPlaylist} />
        </QueryClientProvider>,
      );

      // Attendre que le formulaire soit chargé
      await waitFor(() => {
        expect(screen.getByDisplayValue('Original Title')).toBeInTheDocument();
      });

      // Modifier le titre
      const titleInput = screen.getByDisplayValue('Original Title');
      await user.clear(titleInput);
      await user.type(titleInput, 'Updated Title');

      // Modifier la description
      const descriptionInput = screen.getByDisplayValue('Original description');
      await user.clear(descriptionInput);
      await user.type(descriptionInput, 'Updated description');

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

      // Vérifier que updatePlaylist a été appelé
      await waitFor(() => {
        expect(mockUpdatePlaylist).toHaveBeenCalledWith('1', {
          title: 'Updated Title',
          description: 'Updated description',
          is_public: true,
          cover_url: undefined,
        });
      });
    });
  });

  describe('Delete Playlist', () => {
    it('should delete playlist successfully', async () => {
      const user = userEvent.setup();
      const mockGetPlaylist = vi.mocked(playlistService.getPlaylist);
      const mockDeletePlaylist = vi.mocked(playlistService.deletePlaylist);

      const mockPlaylist: Playlist = {
        id: '1',
        user_id: '1',
        title: 'Playlist to Delete',
        description: 'This will be deleted',
        is_public: true,
        track_count: 0,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      mockGetPlaylist.mockResolvedValue(mockPlaylist);
      mockDeletePlaylist.mockResolvedValue(undefined);

      renderWithProviders(<PlaylistDetailPage />, ['/playlists/1']);

      // Attendre que la page soit chargée
      await waitFor(() => {
        expect(screen.getByText('Playlist to Delete')).toBeInTheDocument();
      });

      // Cliquer sur le bouton de suppression
      const deleteButtons = screen.getAllByRole('button', {
        name: /Supprimer/,
      });
      const deleteButton = deleteButtons[0]; // Premier bouton (celui dans PlaylistActions)
      await user.click(deleteButton);

      // Confirmer la suppression dans le dialog
      await waitFor(() => {
        expect(screen.getByText(/Supprimer la playlist/i)).toBeInTheDocument();
      });

      // Trouver le bouton de confirmation dans le dialog (le dernier bouton "Supprimer")
      const confirmButtons = screen.getAllByRole('button', {
        name: /Supprimer/,
      });
      const confirmButton = confirmButtons[confirmButtons.length - 1]; // Dernier bouton (celui dans le dialog)
      await user.click(confirmButton);

      // Vérifier que deletePlaylist a été appelé
      await waitFor(() => {
        expect(mockDeletePlaylist).toHaveBeenCalledWith('1');
      });
    });
  });

  describe('Complete Playlist Management Flow', () => {
    it('should complete full playlist creation flow', async () => {
      const user = userEvent.setup();
      const mockCreatePlaylist = vi.mocked(playlistService.createPlaylist);

      const newPlaylist: Playlist = {
        id: '1',
        user_id: '1',
        title: 'My New Playlist',
        description: 'A complete test playlist',
        is_public: false,
        track_count: 0,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      mockCreatePlaylist.mockResolvedValue(newPlaylist);

      render(
        <QueryClientProvider client={createQueryClient()}>
          <CreatePlaylistDialog open={true} onOpenChange={vi.fn()} />
        </QueryClientProvider>,
      );

      // Fill form completely
      const titleInput = screen.getByLabelText(/Titre/i);
      await user.type(titleInput, 'My New Playlist');

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

      // Toggle public/private
      const publicCheckbox = screen.getByLabelText(/Publique/i);
      if (
        publicCheckbox instanceof HTMLInputElement &&
        publicCheckbox.checked
      ) {
        await user.click(publicCheckbox);
      }

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

      // Verify creation
      await waitFor(() => {
        expect(mockCreatePlaylist).toHaveBeenCalledWith({
          title: 'My New Playlist',
          description: 'A complete test playlist',
          is_public: false,
        });
      });
    });

    it('should complete full playlist editing flow', async () => {
      const user = userEvent.setup();
      const mockUpdatePlaylist = vi.mocked(playlistService.updatePlaylist);

      const existingPlaylist: Playlist = {
        id: '1',
        user_id: '1',
        title: 'Original Playlist',
        description: 'Original description',
        is_public: true,
        track_count: 5,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      const updatedPlaylist: Playlist = {
        ...existingPlaylist,
        title: 'Updated Playlist Title',
        description: 'Updated description with more details',
        is_public: false,
        updated_at: '2024-01-02T00:00:00Z',
      };

      mockUpdatePlaylist.mockResolvedValue(updatedPlaylist);

      render(
        <QueryClientProvider client={createQueryClient()}>
          <PlaylistForm playlist={existingPlaylist} />
        </QueryClientProvider>,
      );

      // Wait for form to load
      await waitFor(() => {
        expect(
          screen.getByDisplayValue('Original Playlist'),
        ).toBeInTheDocument();
      });

      // Update title
      const titleInput = screen.getByDisplayValue('Original Playlist');
      await user.clear(titleInput);
      await user.type(titleInput, 'Updated Playlist Title');

      // Update description
      const descriptionInput = screen.getByDisplayValue('Original description');
      await user.clear(descriptionInput);
      await user.type(
        descriptionInput,
        'Updated description with more details',
      );

      // Toggle public/private
      const publicCheckbox = screen.getByLabelText(/Publique/i);
      if (
        publicCheckbox instanceof HTMLInputElement &&
        publicCheckbox.checked
      ) {
        await user.click(publicCheckbox);
      }

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

      // Verify update
      await waitFor(() => {
        expect(mockUpdatePlaylist).toHaveBeenCalledWith('1', {
          title: 'Updated Playlist Title',
          description: 'Updated description with more details',
          is_public: false,
          cover_url: undefined,
        });
      });
    });

    it('should handle playlist creation errors', async () => {
      const user = userEvent.setup();
      const mockCreatePlaylist = vi.mocked(playlistService.createPlaylist);

      mockCreatePlaylist.mockRejectedValue(
        new Error('Failed to create playlist'),
      );

      render(
        <QueryClientProvider client={createQueryClient()}>
          <CreatePlaylistDialog open={true} onOpenChange={vi.fn()} />
        </QueryClientProvider>,
      );

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

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

      // Error should be handled
      await waitFor(() => {
        expect(mockCreatePlaylist).toHaveBeenCalled();
      });
    });

    it('should handle playlist update errors', async () => {
      const user = userEvent.setup();
      const mockUpdatePlaylist = vi.mocked(playlistService.updatePlaylist);

      const existingPlaylist: Playlist = {
        id: 1,
        user_id: '1',
        title: 'Test Playlist',
        description: 'Test description',
        is_public: true,
        track_count: 0,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      mockUpdatePlaylist.mockRejectedValue(
        new Error('Failed to update playlist'),
      );

      render(
        <QueryClientProvider client={createQueryClient()}>
          <PlaylistForm playlist={existingPlaylist} />
        </QueryClientProvider>,
      );

      await waitFor(() => {
        expect(screen.getByDisplayValue('Test Playlist')).toBeInTheDocument();
      });

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

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

      // Error should be handled
      await waitFor(() => {
        expect(mockUpdatePlaylist).toHaveBeenCalled();
      });
    });

    it('should render playlist list page', async () => {
      const mockListPlaylists = vi.mocked(playlistService.listPlaylists);

      mockListPlaylists.mockResolvedValue({
        playlists: [],
        total: 0,
        page: 1,
        limit: 20,
      });

      renderWithProviders(<PlaylistListPage />, ['/playlists']);

      // Verify the page renders
      await waitFor(() => {
        expect(mockListPlaylists).toHaveBeenCalled();
      });
    });

    it('should show edit form when playlist is provided', async () => {
      const existingPlaylist: Playlist = {
        id: '1',
        user_id: '1',
        title: 'Test Playlist',
        description: 'Test description',
        is_public: true,
        track_count: 0,
        created_at: '2024-01-01T00:00:00Z',
        updated_at: '2024-01-01T00:00:00Z',
      };

      render(
        <QueryClientProvider client={createQueryClient()}>
          <PlaylistForm playlist={existingPlaylist} />
        </QueryClientProvider>,
      );

      await waitFor(() => {
        expect(screen.getByDisplayValue('Test Playlist')).toBeInTheDocument();
        expect(
          screen.getByDisplayValue('Test description'),
        ).toBeInTheDocument();
      });
    });
  });
});
