import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { TrackVersionHistory } from './TrackVersionHistory';
import {
  listVersions,
  createVersion,
  restoreVersion,
  TrackVersionError,
} from '../services/trackVersionService';
import { useToast } from '@/hooks/useToast';

// Mock services
vi.mock('../services/trackVersionService');
vi.mock('@/hooks/useToast');

describe('TrackVersionHistory', () => {
  const mockToast = {
    success: vi.fn(),
    error: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(useToast).mockReturnValue(mockToast);
  });

  const mockVersions = [
    {
      id: 1,
      track_id: 123,
      version_number: 1,
      file_path: '/path/to/v1.mp3',
      file_size: 1024,
      changelog: 'Initial version',
      created_at: '2024-01-01T00:00:00Z',
      updated_at: '2024-01-01T00:00:00Z',
    },
    {
      id: 2,
      track_id: 123,
      version_number: 2,
      file_path: '/path/to/v2.mp3',
      file_size: 2048,
      changelog: 'Updated version',
      created_at: '2024-01-02T00:00:00Z',
      updated_at: '2024-01-02T00:00:00Z',
    },
  ];

  it('should render loading state initially', async () => {
    vi.mocked(listVersions).mockImplementation(
      () =>
        new Promise((resolve) => setTimeout(() => resolve(mockVersions), 100)),
    );

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    expect(screen.getByRole('status')).toBeInTheDocument();
    await waitFor(() => {
      expect(listVersions).toHaveBeenCalledWith(123);
    });
  });

  it('should display versions list', async () => {
    vi.mocked(listVersions).mockResolvedValue(mockVersions);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('Historique des versions')).toBeInTheDocument();
    });

    expect(screen.getByText('Version 1')).toBeInTheDocument();
    expect(screen.getByText('Version 2')).toBeInTheDocument();
    expect(screen.getByText('Initial version')).toBeInTheDocument();
    expect(screen.getByText('Updated version')).toBeInTheDocument();
  });

  it('should display empty state when no versions', async () => {
    vi.mocked(listVersions).mockResolvedValue([]);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(
        screen.getByText('Aucune version enregistrée'),
      ).toBeInTheDocument();
    });
  });

  it('should display error message on load error', async () => {
    const error = new TrackVersionError('Failed to load', 'SERVER', true);
    vi.mocked(listVersions).mockRejectedValue(error);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('Failed to load')).toBeInTheDocument();
    });
  });

  it('should open create version dialog', async () => {
    vi.mocked(listVersions).mockResolvedValue(mockVersions);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('Créer une version')).toBeInTheDocument();
    });

    const createButton = screen.getByText('Créer une version');
    fireEvent.click(createButton);

    await waitFor(() => {
      expect(
        screen.getByText('Créer une nouvelle version'),
      ).toBeInTheDocument();
    });
  });

  it('should create version successfully', async () => {
    vi.mocked(listVersions).mockResolvedValue(mockVersions);
    vi.mocked(createVersion).mockResolvedValue({
      id: 3,
      track_id: 123,
      version_number: 3,
      file_path: '/path/to/v3.mp3',
      file_size: 3072,
      changelog: 'New version',
      created_at: '2024-01-03T00:00:00Z',
      updated_at: '2024-01-03T00:00:00Z',
    });

    // Mock window.confirm
    window.confirm = vi.fn(() => true);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('Créer une version')).toBeInTheDocument();
    });

    const createButton = screen.getByText('Créer une version');
    fireEvent.click(createButton);

    await waitFor(() => {
      expect(
        screen.getByText('Créer une nouvelle version'),
      ).toBeInTheDocument();
    });

    const changelogInput = screen.getByPlaceholderText(
      "Ex: Correction du mixage, ajout d'effets...",
    );
    fireEvent.change(changelogInput, { target: { value: 'New version' } });

    const submitButton = screen.getByText('Créer la version');
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(createVersion).toHaveBeenCalledWith(123, {
        file_path: '/path/to/track.mp3',
        file_size: 1024,
        changelog: 'New version',
      });
    });

    await waitFor(() => {
      expect(mockToast.success).toHaveBeenCalledWith(
        'Version créée avec succès',
      );
    });
  });

  it('should handle create version error', async () => {
    vi.mocked(listVersions).mockResolvedValue(mockVersions);
    const error = new TrackVersionError('Failed to create', 'SERVER', true);
    vi.mocked(createVersion).mockRejectedValue(error);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('Créer une version')).toBeInTheDocument();
    });

    const createButton = screen.getByText('Créer une version');
    fireEvent.click(createButton);

    await waitFor(() => {
      expect(
        screen.getByText('Créer une nouvelle version'),
      ).toBeInTheDocument();
    });

    const submitButton = screen.getByText('Créer la version');
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(mockToast.error).toHaveBeenCalledWith('Failed to create');
    });
  });

  it('should restore version successfully', async () => {
    vi.mocked(listVersions).mockResolvedValue(mockVersions);
    vi.mocked(restoreVersion).mockResolvedValue();

    // Mock window.confirm
    window.confirm = vi.fn(() => true);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('Version 1')).toBeInTheDocument();
    });

    const restoreButtons = screen.getAllByText('Restaurer');
    fireEvent.click(restoreButtons[0]); // Click first restore button

    await waitFor(() => {
      expect(restoreVersion).toHaveBeenCalledWith(123, 1);
    });

    await waitFor(() => {
      expect(mockToast.success).toHaveBeenCalledWith(
        'Version restaurée avec succès',
      );
    });
  });

  it('should not restore version if user cancels', async () => {
    vi.mocked(listVersions).mockResolvedValue(mockVersions);

    // Mock window.confirm to return false
    window.confirm = vi.fn(() => false);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('Version 1')).toBeInTheDocument();
    });

    const restoreButtons = screen.getAllByText('Restaurer');
    fireEvent.click(restoreButtons[0]);

    await waitFor(() => {
      expect(window.confirm).toHaveBeenCalled();
    });

    expect(restoreVersion).not.toHaveBeenCalled();
  });

  it('should handle restore version error', async () => {
    vi.mocked(listVersions).mockResolvedValue(mockVersions);
    const error = new TrackVersionError('Failed to restore', 'SERVER', true);
    vi.mocked(restoreVersion).mockRejectedValue(error);

    // Mock window.confirm
    window.confirm = vi.fn(() => true);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText('Version 1')).toBeInTheDocument();
    });

    const restoreButtons = screen.getAllByText('Restaurer');
    fireEvent.click(restoreButtons[0]);

    await waitFor(() => {
      expect(mockToast.error).toHaveBeenCalledWith('Failed to restore');
    });
  });

  it('should not show create button when track info is missing', async () => {
    vi.mocked(listVersions).mockResolvedValue(mockVersions);

    render(<TrackVersionHistory trackId={123} />);

    await waitFor(() => {
      expect(screen.getByText('Historique des versions')).toBeInTheDocument();
    });

    expect(screen.queryByText('Créer une version')).not.toBeInTheDocument();
  });

  it('should format file sizes correctly', async () => {
    const versionsWithLargeSize = [
      {
        ...mockVersions[0],
        file_size: 1024 * 1024 * 1024, // 1 GB
      },
    ];

    vi.mocked(listVersions).mockResolvedValue(versionsWithLargeSize);

    render(
      <TrackVersionHistory
        trackId={123}
        trackFilePath="/path/to/track.mp3"
        trackFileSize={1024}
      />,
    );

    await waitFor(() => {
      expect(screen.getByText(/GB/)).toBeInTheDocument();
    });
  });
});
