import { render, screen, waitFor, act } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { ToastComponent, Toast } from './Toast';
import { ToastProvider, useToastContext } from './ToastProvider';
import { useToast } from '@/hooks/useToast';

describe('Toast Components', () => {
  describe('ToastComponent', () => {
    const mockToast: Toast = {
      id: '1',
      message: 'Test message',
      type: 'success',
    };

    it('renders toast with message', async () => {
      const onDismiss = vi.fn();
      render(<ToastComponent toast={mockToast} onDismiss={onDismiss} />);

      await waitFor(() => {
        expect(screen.getByText('Test message')).toBeInTheDocument();
      });
    });

    it('renders with correct type styling', async () => {
      const onDismiss = vi.fn();
      const { container } = render(
        <ToastComponent
          toast={{ ...mockToast, type: 'error' }}
          onDismiss={onDismiss}
        />,
      );

      await waitFor(() => {
        const toast = container.querySelector(
          '.bg-red-50, .dark\\:bg-red-900\\/20',
        );
        expect(toast).toBeInTheDocument();
      });
    });

    it('auto-dismisses after duration', async () => {
      vi.useFakeTimers();
      const onDismiss = vi.fn();
      render(
        <ToastComponent
          toast={{ ...mockToast, duration: 1000 }}
          onDismiss={onDismiss}
        />,
      );

      await waitFor(() => {
        expect(screen.getByText('Test message')).toBeInTheDocument();
      });

      act(() => {
        vi.advanceTimersByTime(1100);
      });

      await waitFor(() => {
        expect(onDismiss).toHaveBeenCalledWith('1');
      });

      vi.useRealTimers();
    });

    it('does not auto-dismiss when duration is 0', async () => {
      vi.useFakeTimers();
      const onDismiss = vi.fn();
      render(
        <ToastComponent
          toast={{ ...mockToast, duration: 0 }}
          onDismiss={onDismiss}
        />,
      );

      await waitFor(() => {
        expect(screen.getByText('Test message')).toBeInTheDocument();
      });

      act(() => {
        vi.advanceTimersByTime(6000);
      });

      await waitFor(() => {
        expect(onDismiss).not.toHaveBeenCalled();
      });

      vi.useRealTimers();
    });

    it('calls onDismiss when close button is clicked', async () => {
      const onDismiss = vi.fn();
      render(<ToastComponent toast={mockToast} onDismiss={onDismiss} />);

      await waitFor(() => {
        expect(screen.getByText('Test message')).toBeInTheDocument();
      });

      const closeButton = screen.getByLabelText('Fermer');
      closeButton.click();

      await waitFor(
        () => {
          expect(onDismiss).toHaveBeenCalledWith('1');
        },
        { timeout: 500 },
      );
    });

    it('renders correct icon for each type', async () => {
      const onDismiss = vi.fn();
      const { container, rerender } = render(
        <ToastComponent
          toast={{ ...mockToast, type: 'success' }}
          onDismiss={onDismiss}
        />,
      );

      await waitFor(() => {
        const icon = container.querySelector('svg');
        expect(icon).toBeInTheDocument();
      });

      rerender(
        <ToastComponent
          toast={{ ...mockToast, type: 'error' }}
          onDismiss={onDismiss}
        />,
      );
      rerender(
        <ToastComponent
          toast={{ ...mockToast, type: 'warning' }}
          onDismiss={onDismiss}
        />,
      );
      rerender(
        <ToastComponent
          toast={{ ...mockToast, type: 'info' }}
          onDismiss={onDismiss}
        />,
      );
    });

    it('renders with default type when type is not provided', async () => {
      const onDismiss = vi.fn();
      const { container } = render(
        <ToastComponent
          toast={{ id: '1', message: 'Test' }}
          onDismiss={onDismiss}
        />,
      );

      await waitFor(() => {
        expect(screen.getByText('Test')).toBeInTheDocument();
        const toast = container.querySelector(
          '.bg-blue-50, .dark\\:bg-blue-900\\/20',
        );
        expect(toast).toBeInTheDocument();
      });
    });
  });

  describe('ToastProvider', () => {
    it('provides toast context', () => {
      const TestComponent = () => {
        const context = useToastContext();
        expect(context).toBeDefined();
        expect(context.toasts).toEqual([]);
        expect(typeof context.addToast).toBe('function');
        expect(typeof context.removeToast).toBe('function');
        return <div>Test</div>;
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );
    });

    it('throws error when used outside provider', () => {
      const TestComponent = () => {
        try {
          useToastContext();
          return <div>Should not render</div>;
        } catch (error: any) {
          expect(error.message).toContain(
            'useToastContext must be used within ToastProvider',
          );
          return <div>Error caught</div>;
        }
      };

      render(<TestComponent />);
    });

    it('adds toast to queue', async () => {
      const TestComponent = () => {
        const { addToast, toasts } = useToastContext();
        return (
          <div>
            <button
              onClick={() => addToast({ message: 'Test', type: 'success' })}
            >
              Add Toast
            </button>
            <div data-testid="toast-count">{toasts.length}</div>
          </div>
        );
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );

      const button = screen.getByText('Add Toast');
      button.click();

      await waitFor(() => {
        expect(screen.getByTestId('toast-count')).toHaveTextContent('1');
      });
    });

    it('removes toast from queue', async () => {
      const TestComponent = () => {
        const { addToast, removeToast, toasts } = useToastContext();
        return (
          <div>
            <button
              onClick={() => addToast({ message: 'Test', type: 'success' })}
            >
              Add Toast
            </button>
            <button onClick={() => removeToast(toasts[0]?.id || '')}>
              Remove Toast
            </button>
            <div data-testid="toast-count">{toasts.length}</div>
          </div>
        );
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );

      const addButton = screen.getByText('Add Toast');
      addButton.click();

      await waitFor(() => {
        expect(screen.getByTestId('toast-count')).toHaveTextContent('1');
      });

      const removeButton = screen.getByText('Remove Toast');
      removeButton.click();

      await waitFor(() => {
        expect(screen.getByTestId('toast-count')).toHaveTextContent('0');
      });
    });

    it('renders toasts in container', async () => {
      const TestComponent = () => {
        const { addToast } = useToastContext();
        return (
          <button
            onClick={() =>
              addToast({ message: 'Test message', type: 'success' })
            }
          >
            Add Toast
          </button>
        );
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );

      const button = screen.getByText('Add Toast');
      await act(async () => {
        button.click();
      });

      await waitFor(
        () => {
          expect(screen.getByText('Test message')).toBeInTheDocument();
        },
        { timeout: 1000 },
      );
    });

    it('applies correct position classes', () => {
      const { container, rerender } = render(
        <ToastProvider position="top-right">
          <div>Test</div>
        </ToastProvider>,
      );

      let positionDiv = container.querySelector('.top-4.right-4');
      expect(positionDiv).toBeInTheDocument();

      rerender(
        <ToastProvider position="bottom-left">
          <div>Test</div>
        </ToastProvider>,
      );

      positionDiv = container.querySelector('.bottom-4.left-4');
      expect(positionDiv).toBeInTheDocument();
    });
  });

  describe('useToast hook', () => {
    it('provides toast methods', () => {
      const TestComponent = () => {
        const toast = useToast();
        expect(typeof toast.success).toBe('function');
        expect(typeof toast.error).toBe('function');
        expect(typeof toast.warning).toBe('function');
        expect(typeof toast.info).toBe('function');
        expect(typeof toast.toast).toBe('function');
        return <div>Test</div>;
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );
    });

    it('displays success toast', async () => {
      const TestComponent = () => {
        const toast = useToast();
        return (
          <button onClick={() => toast.success('Success message')}>
            Show Success
          </button>
        );
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );

      const button = screen.getByText('Show Success');
      await act(async () => {
        button.click();
      });

      // Wait for toast to appear (with animation delay)
      await waitFor(
        () => {
          const toast = screen.queryByText('Success message');
          expect(toast).toBeInTheDocument();
        },
        { timeout: 1000 },
      );
    });

    it('displays error toast', async () => {
      const TestComponent = () => {
        const toast = useToast();
        return (
          <button onClick={() => toast.error('Error message')}>
            Show Error
          </button>
        );
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );

      const button = screen.getByText('Show Error');
      await act(async () => {
        button.click();
      });

      await waitFor(
        () => {
          expect(screen.getByText('Error message')).toBeInTheDocument();
        },
        { timeout: 1000 },
      );
    });

    it('displays warning toast', async () => {
      const TestComponent = () => {
        const toast = useToast();
        return (
          <button onClick={() => toast.warning('Warning message')}>
            Show Warning
          </button>
        );
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );

      const button = screen.getByText('Show Warning');
      await act(async () => {
        button.click();
      });

      await waitFor(
        () => {
          expect(screen.getByText('Warning message')).toBeInTheDocument();
        },
        { timeout: 1000 },
      );
    });

    it('displays info toast', async () => {
      const TestComponent = () => {
        const toast = useToast();
        return (
          <button onClick={() => toast.info('Info message')}>Show Info</button>
        );
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );

      const button = screen.getByText('Show Info');
      await act(async () => {
        button.click();
      });

      await waitFor(
        () => {
          expect(screen.getByText('Info message')).toBeInTheDocument();
        },
        { timeout: 1000 },
      );
    });

    it('allows custom toast with toast method', async () => {
      const TestComponent = () => {
        const toast = useToast();
        return (
          <button
            onClick={() =>
              toast.toast({
                message: 'Custom message',
                type: 'success',
                duration: 2000,
              })
            }
          >
            Show Custom
          </button>
        );
      };

      render(
        <ToastProvider>
          <TestComponent />
        </ToastProvider>,
      );

      const button = screen.getByText('Show Custom');
      await act(async () => {
        button.click();
      });

      await waitFor(
        () => {
          expect(screen.getByText('Custom message')).toBeInTheDocument();
        },
        { timeout: 1000 },
      );
    });
  });
});
