/**
 * Tests pour RemoveTrackButton
 * T0472: Create Remove Track from Playlist Component
 */

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 { RemoveTrackButton } from './RemoveTrackButton';
import * as playlistHooks from '../hooks/usePlaylist';

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

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

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

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

describe('RemoveTrackButton', () => {
  const mockMutateAsync = vi.fn();
  const mockOnRemoved = vi.fn();

  beforeEach(() => {
    vi.clearAllMocks();

    vi.mocked(playlistHooks.useRemoveTrackFromPlaylist).mockReturnValue({
      mutateAsync: mockMutateAsync,
      isPending: false,
      isSuccess: false,
      isError: false,
      error: null,
      data: undefined,
      reset: vi.fn(),
      mutate: vi.fn(),
      status: 'idle',
    } as any);
  });

  it('should render button with default trigger', () => {
    render(<RemoveTrackButton playlistId={1} trackId={10} />, {
      wrapper: createWrapper(),
    });

    expect(screen.getByText('Retirer')).toBeInTheDocument();
  });

  it('should render custom trigger', () => {
    render(
      <RemoveTrackButton
        playlistId={1}
        trackId={10}
        trigger={<button>Custom Remove</button>}
      />,
      { wrapper: createWrapper() },
    );

    expect(screen.getByText('Custom Remove')).toBeInTheDocument();
  });

  it('should open confirmation dialog when button is clicked', async () => {
    const user = userEvent.setup();
    render(<RemoveTrackButton playlistId={1} trackId={10} />, {
      wrapper: createWrapper(),
    });

    const button = screen.getByText('Retirer');
    await user.click(button);

    expect(
      screen.getByText('Retirer le track de la playlist ?'),
    ).toBeInTheDocument();
  });

  it('should display track title in dialog when provided', async () => {
    const user = userEvent.setup();
    render(
      <RemoveTrackButton playlistId={1} trackId={10} trackTitle="Test Track" />,
      { wrapper: createWrapper() },
    );

    const button = screen.getByText('Retirer');
    await user.click(button);

    expect(screen.getByText('Test Track')).toBeInTheDocument();
  });

  it('should remove track when confirmed', async () => {
    const user = userEvent.setup();
    mockMutateAsync.mockResolvedValue(undefined);

    render(
      <RemoveTrackButton
        playlistId={1}
        trackId={10}
        onRemoved={mockOnRemoved}
      />,
      { wrapper: createWrapper() },
    );

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

    await waitFor(() => {
      expect(
        screen.getByText('Retirer le track de la playlist ?'),
      ).toBeInTheDocument();
    });

    const confirmButtons = screen.getAllByRole('button', { name: /retirer/i });
    // Le deuxième bouton est celui de confirmation dans le dialog
    await user.click(confirmButtons[1]);

    await waitFor(() => {
      expect(mockMutateAsync).toHaveBeenCalledWith({
        playlistId: 1,
        trackId: 10,
      });
    });

    expect(mockOnRemoved).toHaveBeenCalled();
  });

  it('should close dialog when cancel is clicked', async () => {
    const user = userEvent.setup();
    render(<RemoveTrackButton playlistId={1} trackId={10} />, {
      wrapper: createWrapper(),
    });

    const button = screen.getByText('Retirer');
    await user.click(button);

    const cancelButton = screen.getByText('Annuler');
    await user.click(cancelButton);

    await waitFor(() => {
      expect(
        screen.queryByText('Retirer le track de la playlist ?'),
      ).not.toBeInTheDocument();
    });
  });

  it('should show loading state when removing', async () => {
    const user = userEvent.setup();
    let resolvePromise: () => void;
    const promise = new Promise<void>((resolve) => {
      resolvePromise = resolve;
    });
    mockMutateAsync.mockImplementation(() => promise);

    // Initial state: not pending
    vi.mocked(playlistHooks.useRemoveTrackFromPlaylist).mockReturnValue({
      mutateAsync: mockMutateAsync,
      isPending: false,
      isSuccess: false,
      isError: false,
      error: null,
      data: undefined,
      reset: vi.fn(),
      mutate: vi.fn(),
      status: 'idle',
    } as any);

    const { rerender } = render(
      <RemoveTrackButton playlistId={1} trackId={10} />,
      { wrapper: createWrapper() },
    );

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

    await waitFor(() => {
      expect(
        screen.getByText('Retirer le track de la playlist ?'),
      ).toBeInTheDocument();
    });

    const confirmButtons = screen.getAllByRole('button', { name: /retirer/i });
    // Le deuxième bouton est celui de confirmation dans le dialog
    await user.click(confirmButtons[1]);

    // Update to pending state
    vi.mocked(playlistHooks.useRemoveTrackFromPlaylist).mockReturnValue({
      mutateAsync: mockMutateAsync,
      isPending: true,
      isSuccess: false,
      isError: false,
      error: null,
      data: undefined,
      reset: vi.fn(),
      mutate: vi.fn(),
      status: 'pending',
    } as any);

    rerender(<RemoveTrackButton playlistId={1} trackId={10} />);

    await waitFor(() => {
      expect(screen.getByText('Retrait en cours...')).toBeInTheDocument();
    });

    // Cleanup
    resolvePromise!();
  });

  it('should handle error when removal fails', async () => {
    const user = userEvent.setup();
    const error = new Error('Failed to remove track');
    mockMutateAsync.mockRejectedValue(error);

    render(<RemoveTrackButton playlistId={1} trackId={10} />, {
      wrapper: createWrapper(),
    });

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

    await waitFor(() => {
      expect(
        screen.getByText('Retirer le track de la playlist ?'),
      ).toBeInTheDocument();
    });

    const confirmButtons = screen.getAllByRole('button', { name: /retirer/i });
    // Le deuxième bouton est celui de confirmation dans le dialog
    await user.click(confirmButtons[1]);

    await waitFor(
      () => {
        expect(mockMutateAsync).toHaveBeenCalled();
      },
      { timeout: 2000 },
    );

    // Le dialog peut se fermer ou rester ouvert selon l'implémentation
    // On vérifie juste que la mutation a été appelée
    expect(mockMutateAsync).toHaveBeenCalledWith({
      playlistId: 1,
      trackId: 10,
    });
  });
});
