import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { TrackHistory } from './TrackHistory';
import {
  getTrackHistory,
  TrackHistoryError,
  TrackHistoryItem,
} from '../services/trackHistoryService';
import { useToast } from '@/hooks/useToast';

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

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

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

  const mockHistoryItems: TrackHistoryItem[] = [
    {
      id: 1,
      track_id: 123,
      user_id: 1,
      action: 'created',
      old_value: undefined,
      new_value: '{"title": "Test Track"}',
      created_at: '2024-01-01T00:00:00Z',
    },
    {
      id: 2,
      track_id: 123,
      user_id: 1,
      action: 'updated',
      old_value: '{"title": "Test Track"}',
      new_value: '{"title": "Updated Track"}',
      created_at: '2024-01-02T00:00:00Z',
    },
    {
      id: 3,
      track_id: 123,
      user_id: 1,
      action: 'published',
      old_value: undefined,
      new_value: undefined,
      created_at: '2024-01-03T00:00:00Z',
    },
  ];

  it('should render loading state initially', async () => {
    vi.mocked(getTrackHistory).mockImplementation(
      () =>
        new Promise((resolve) =>
          setTimeout(
            () =>
              resolve({
                history: mockHistoryItems,
                total: 3,
                limit: 50,
                offset: 0,
              }),
            100,
          ),
        ),
    );

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

    expect(screen.getByRole('status')).toBeInTheDocument();
    await waitFor(() => {
      expect(getTrackHistory).toHaveBeenCalledWith(123, {
        limit: 50,
        offset: 0,
      });
    });
  });

  it('should display history timeline', async () => {
    vi.mocked(getTrackHistory).mockResolvedValue({
      history: mockHistoryItems,
      total: 3,
      limit: 50,
      offset: 0,
    });

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

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

    expect(screen.getByText('Créé')).toBeInTheDocument();
    expect(screen.getByText('Modifié')).toBeInTheDocument();
    expect(screen.getByText('Publié')).toBeInTheDocument();
  });

  it('should display empty state when no history', async () => {
    vi.mocked(getTrackHistory).mockResolvedValue({
      history: [],
      total: 0,
      limit: 50,
      offset: 0,
    });

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

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

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

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

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

  it('should display old and new values when available', async () => {
    vi.mocked(getTrackHistory).mockResolvedValue({
      history: [mockHistoryItems[1]], // Updated action with values
      total: 1,
      limit: 50,
      offset: 0,
    });

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

    await waitFor(() => {
      expect(screen.getByText('Ancienne valeur:')).toBeInTheDocument();
      expect(screen.getByText('Nouvelle valeur:')).toBeInTheDocument();
    });
  });

  it('should handle pagination', async () => {
    const largeHistory: TrackHistoryItem[] = Array.from(
      { length: 10 },
      (_, i) => ({
        id: i + 1,
        track_id: 123,
        user_id: 1,
        action: 'updated' as const,
        created_at: `2024-01-0${i + 1}T00:00:00Z`,
      }),
    );

    vi.mocked(getTrackHistory).mockResolvedValue({
      history: largeHistory.slice(0, 5),
      total: 10,
      limit: 5,
      offset: 0,
    });

    render(<TrackHistory trackId={123} limit={5} />);

    await waitFor(() => {
      expect(screen.getByText(/Affichage 1 - 5 sur 10/)).toBeInTheDocument();
    });

    const nextButton = screen.getByText('Suivant');
    expect(nextButton).toBeInTheDocument();
    expect(nextButton).not.toBeDisabled();

    // Mock next page response
    vi.mocked(getTrackHistory).mockResolvedValue({
      history: largeHistory.slice(5, 10),
      total: 10,
      limit: 5,
      offset: 5,
    });

    fireEvent.click(nextButton);

    await waitFor(() => {
      expect(getTrackHistory).toHaveBeenCalledWith(123, {
        limit: 5,
        offset: 5,
      });
    });
  });

  it('should disable previous button on first page', async () => {
    vi.mocked(getTrackHistory).mockResolvedValue({
      history: mockHistoryItems.slice(0, 2),
      total: 10,
      limit: 2,
      offset: 0,
    });

    render(<TrackHistory trackId={123} limit={2} />);

    await waitFor(() => {
      const previousButton = screen.getByText('Précédent');
      expect(previousButton).toBeDisabled();
    });
  });

  it('should disable next button on last page', async () => {
    vi.mocked(getTrackHistory).mockResolvedValue({
      history: mockHistoryItems.slice(0, 2),
      total: 2,
      limit: 2,
      offset: 0,
    });

    render(<TrackHistory trackId={123} limit={2} />);

    await waitFor(() => {
      const nextButton = screen.getByText('Suivant');
      expect(nextButton).toBeDisabled();
    });
  });

  it('should display all action types correctly', async () => {
    const allActions: TrackHistoryItem[] = [
      {
        id: 1,
        track_id: 123,
        user_id: 1,
        action: 'created',
        created_at: '2024-01-01T00:00:00Z',
      },
      {
        id: 2,
        track_id: 123,
        user_id: 1,
        action: 'updated',
        created_at: '2024-01-02T00:00:00Z',
      },
      {
        id: 3,
        track_id: 123,
        user_id: 1,
        action: 'deleted',
        created_at: '2024-01-03T00:00:00Z',
      },
      {
        id: 4,
        track_id: 123,
        user_id: 1,
        action: 'published',
        created_at: '2024-01-04T00:00:00Z',
      },
      {
        id: 5,
        track_id: 123,
        user_id: 1,
        action: 'unpublished',
        created_at: '2024-01-05T00:00:00Z',
      },
      {
        id: 6,
        track_id: 123,
        user_id: 1,
        action: 'restored',
        created_at: '2024-01-06T00:00:00Z',
      },
    ];

    vi.mocked(getTrackHistory).mockResolvedValue({
      history: allActions,
      total: 6,
      limit: 50,
      offset: 0,
    });

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

    await waitFor(() => {
      expect(screen.getByText('Créé')).toBeInTheDocument();
      expect(screen.getByText('Modifié')).toBeInTheDocument();
      expect(screen.getByText('Supprimé')).toBeInTheDocument();
      expect(screen.getByText('Publié')).toBeInTheDocument();
      expect(screen.getByText('Dépublié')).toBeInTheDocument();
      expect(screen.getByText('Restauré')).toBeInTheDocument();
    });
  });

  it('should format dates correctly', async () => {
    vi.mocked(getTrackHistory).mockResolvedValue({
      history: [mockHistoryItems[0]],
      total: 1,
      limit: 50,
      offset: 0,
    });

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

    await waitFor(() => {
      // Check that date is formatted (should contain month name in French)
      const dateElement = screen.getByText(/janvier/i);
      expect(dateElement).toBeInTheDocument();
    });
  });
});
