import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { CommentSection } from './CommentSection';
import {
  getComments,
  createComment,
  CommentError,
} from '../services/commentService';
import { useAuthStore } from '@/features/auth/store/authStore';
import { useToast } from '@/hooks/useToast';
import type { TrackComment } from '../services/commentService';

// Mock dependencies
vi.mock('../services/commentService');
vi.mock('@/features/auth/store/authStore');
vi.mock('@/hooks/useToast');

describe('CommentSection', () => {
  const mockToast = {
    success: vi.fn(),
    error: vi.fn(),
    warning: vi.fn(),
    info: vi.fn(),
    toast: vi.fn(),
  };

  const mockUser = {
    id: 123,
    username: 'testuser',
    email: 'test@example.com',
  };

  const mockComments: TrackComment[] = [
    {
      id: 1,
      track_id: 1,
      creator_id: 123,
      content: 'Great track!',
      is_edited: false,
      created_at: '2024-01-01T00:00:00Z',
      updated_at: '2024-01-01T00:00:00Z',
      user: {
        id: 123,
        username: 'testuser',
      },
    },
    {
      id: 2,
      track_id: 1,
      creator_id: 456,
      content: 'Amazing!',
      is_edited: false,
      created_at: '2024-01-02T00:00:00Z',
      updated_at: '2024-01-02T00:00:00Z',
      user: {
        id: 456,
        username: 'otheruser',
      },
    },
  ];

  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(useAuthStore).mockReturnValue({
      user: mockUser,
      isAuthenticated: true,
      isLoading: false,
      error: null,
      login: vi.fn(),
      register: vi.fn(),
      logout: vi.fn(),
      refreshUser: vi.fn(),
      clearError: vi.fn(),
      setLoading: vi.fn(),
      checkAuthStatus: vi.fn(),
    });
    vi.mocked(useToast).mockReturnValue(mockToast);
  });

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

  it('should render comment section', async () => {
    vi.mocked(getComments).mockResolvedValue({
      comments: mockComments,
      total: 2,
      page: 1,
      limit: 20,
    });

    render(<CommentSection trackId={1} />);

    await waitFor(() => {
      expect(screen.getByText('Commentaires (2)')).toBeInTheDocument();
    });
  });

  it('should display loading state', () => {
    vi.mocked(getComments).mockImplementation(
      () =>
        new Promise((resolve) => {
          setTimeout(() => {
            resolve({
              comments: [],
              total: 0,
              page: 1,
              limit: 20,
            });
          }, 100);
        }),
    );

    render(<CommentSection trackId={1} />);

    expect(screen.getByRole('status')).toBeInTheDocument();
  });

  it('should display empty state when no comments', async () => {
    vi.mocked(getComments).mockResolvedValue({
      comments: [],
      total: 0,
      page: 1,
      limit: 20,
    });

    render(<CommentSection trackId={1} />);

    await waitFor(() => {
      expect(
        screen.getByText(/aucun commentaire pour le moment/i),
      ).toBeInTheDocument();
    });
  });

  it('should display comments', async () => {
    vi.mocked(getComments).mockResolvedValue({
      comments: mockComments,
      total: 2,
      page: 1,
      limit: 20,
    });

    render(<CommentSection trackId={1} />);

    await waitFor(() => {
      expect(screen.getByText('Great track!')).toBeInTheDocument();
      expect(screen.getByText('Amazing!')).toBeInTheDocument();
    });
  });

  it('should create a new comment', async () => {
    const user = userEvent.setup();
    const newComment: TrackComment = {
      id: 3,
      track_id: 1,
      creator_id: 123,
      content: 'New comment',
      is_edited: false,
      created_at: '2024-01-03T00:00:00Z',
      updated_at: '2024-01-03T00:00:00Z',
      user: {
        id: 123,
        username: 'testuser',
      },
    };

    vi.mocked(getComments).mockResolvedValue({
      comments: mockComments,
      total: 2,
      page: 1,
      limit: 20,
    });
    vi.mocked(createComment).mockResolvedValue(newComment);

    render(<CommentSection trackId={1} />);

    await waitFor(() => {
      expect(
        screen.getByPlaceholderText('Écrivez un commentaire...'),
      ).toBeInTheDocument();
    });

    const textarea = screen.getByPlaceholderText('Écrivez un commentaire...');
    await user.type(textarea, 'New comment');

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

    await waitFor(() => {
      expect(createComment).toHaveBeenCalledWith(1, 'New comment', undefined);
      expect(mockToast.success).toHaveBeenCalledWith('Commentaire ajouté');
    });
  });

  it('should load more comments', async () => {
    const user = userEvent.setup();
    const moreComments: TrackComment[] = [
      {
        id: 3,
        track_id: 1,
        creator_id: 123,
        content: 'More comments',
        is_edited: false,
        created_at: '2024-01-03T00:00:00Z',
        updated_at: '2024-01-03T00:00:00Z',
        user: {
          id: 123,
          username: 'testuser',
        },
      },
    ];

    vi.mocked(getComments)
      .mockResolvedValueOnce({
        comments: mockComments,
        total: 3,
        page: 1,
        limit: 20,
      })
      .mockResolvedValueOnce({
        comments: moreComments,
        total: 3,
        page: 2,
        limit: 20,
      });

    render(<CommentSection trackId={1} />);

    await waitFor(() => {
      expect(screen.getByText('Great track!')).toBeInTheDocument();
    });

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

    await waitFor(() => {
      expect(getComments).toHaveBeenCalledWith(1, 2, 20);
    });
  });

  it('should handle error when loading comments', async () => {
    const error = new CommentError('Failed to load comments', 'NETWORK', true);
    vi.mocked(getComments).mockRejectedValue(error);

    render(<CommentSection trackId={1} />);

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

  it('should refresh comments', async () => {
    const user = userEvent.setup();
    vi.mocked(getComments).mockResolvedValue({
      comments: mockComments,
      total: 2,
      page: 1,
      limit: 20,
    });

    render(<CommentSection trackId={1} />);

    await waitFor(() => {
      expect(screen.getByText('Great track!')).toBeInTheDocument();
    });

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

    await waitFor(() => {
      expect(getComments).toHaveBeenCalledWith(1, 1, 20);
    });
  });
});
