import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import userEvent from '@testing-library/user-event';
import { Tooltip } from './tooltip';

describe('Tooltip Component', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.runOnlyPendingTimers();
    vi.useRealTimers();
  });

  it('renders children correctly', () => {
    render(
      <Tooltip content="Tooltip text">
        <button>Hover me</button>
      </Tooltip>,
    );

    expect(screen.getByText('Hover me')).toBeInTheDocument();
  });

  it('does not show tooltip initially', () => {
    render(
      <Tooltip content="Tooltip text">
        <button>Hover me</button>
      </Tooltip>,
    );

    expect(screen.queryByText('Tooltip text')).not.toBeInTheDocument();
  });

  it('shows tooltip on hover after delay', () => {
    render(
      <Tooltip content="Tooltip text" delay={300}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    // Avancer le temps pour déclencher le délai (300ms) et le setTimeout imbriqué (10ms)
    vi.advanceTimersByTime(310);

    expect(screen.getByText('Tooltip text')).toBeInTheDocument();
  });

  it('hides tooltip when mouse leaves', () => {
    render(
      <Tooltip content="Tooltip text" delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    // Avancer pour le setTimeout(..., 0)
    vi.advanceTimersByTime(10);

    expect(screen.getByText('Tooltip text')).toBeInTheDocument();

    fireEvent.mouseLeave(button);
    // Avancer pour la fin de l'animation (200ms)
    vi.advanceTimersByTime(250);

    // Le tooltip peut être monté mais invisible, vérifier qu'il n'est pas visible
    const tooltip = screen.queryByText('Tooltip text');
    if (tooltip) {
      expect(tooltip).toHaveClass('opacity-0');
    }
  });

  it('shows tooltip immediately when delay is 0', () => {
    render(
      <Tooltip content="Tooltip text" delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    // Avancer pour le setTimeout(..., 0)
    vi.advanceTimersByTime(10);

    expect(screen.getByText('Tooltip text')).toBeInTheDocument();
  });

  it('applies top position by default', () => {
    render(
      <Tooltip content="Tooltip text" delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    vi.advanceTimersByTime(10);

    const tooltip = screen.getByText('Tooltip text');
    expect(tooltip.closest('.bottom-full')).toBeInTheDocument();
  });

  it('applies bottom position', () => {
    render(
      <Tooltip content="Tooltip text" position="bottom" delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    vi.advanceTimersByTime(10);

    const tooltip = screen.getByText('Tooltip text');
    expect(tooltip.closest('.top-full')).toBeInTheDocument();
  });

  it('applies left position', () => {
    render(
      <Tooltip content="Tooltip text" position="left" delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    vi.advanceTimersByTime(10);

    const tooltip = screen.getByText('Tooltip text');
    expect(tooltip.closest('.right-full')).toBeInTheDocument();
  });

  it('applies right position', () => {
    render(
      <Tooltip content="Tooltip text" position="right" delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    vi.advanceTimersByTime(10);

    const tooltip = screen.getByText('Tooltip text');
    expect(tooltip.closest('.left-full')).toBeInTheDocument();
  });

  it('does not show tooltip when disabled', () => {
    render(
      <Tooltip content="Tooltip text" disabled delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    vi.advanceTimersByTime(10);

    expect(screen.queryByText('Tooltip text')).not.toBeInTheDocument();
  });

  it('renders React node as content', () => {
    render(
      <Tooltip content={<span>Custom content</span>} delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    vi.advanceTimersByTime(10);

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

  it('applies custom className', () => {
    render(
      <Tooltip content="Tooltip text" className="custom-tooltip" delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    vi.advanceTimersByTime(10);

    const tooltip = screen.getByText('Tooltip text');
    expect(tooltip.closest('.custom-tooltip')).toBeInTheDocument();
  });

  it('has correct ARIA role', () => {
    render(
      <Tooltip content="Tooltip text" delay={0}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    vi.advanceTimersByTime(10);

    const tooltip = screen.getByRole('tooltip');
    expect(tooltip).toBeInTheDocument();
  });

  it('cancels tooltip display if mouse leaves before delay', () => {
    render(
      <Tooltip content="Tooltip text" delay={300}>
        <button>Hover me</button>
      </Tooltip>,
    );

    const button = screen.getByText('Hover me');
    fireEvent.mouseEnter(button);

    // Avancer le temps mais pas assez pour déclencher l'affichage
    vi.advanceTimersByTime(200);

    fireEvent.mouseLeave(button);

    // Avancer le reste du temps
    vi.advanceTimersByTime(200);

    expect(screen.queryByText('Tooltip text')).not.toBeInTheDocument();
  });

  describe('Triggers', () => {
    it('shows tooltip on click when trigger is click', async () => {
      const user = userEvent.setup({ delay: null });
      render(
        <Tooltip content="Click tooltip" trigger="click" delay={0}>
          <button>Click me</button>
        </Tooltip>,
      );

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

      vi.advanceTimersByTime(10);

      expect(screen.getByText('Click tooltip')).toBeInTheDocument();
    });

    it('toggles tooltip on click when trigger is click', async () => {
      const user = userEvent.setup({ delay: null });
      render(
        <Tooltip content="Click tooltip" trigger="click" delay={0}>
          <button>Click me</button>
        </Tooltip>,
      );

      const button = screen.getByText('Click me');

      // Premier clic
      await user.click(button);
      vi.advanceTimersByTime(10);
      expect(screen.getByText('Click tooltip')).toBeInTheDocument();

      // Deuxième clic pour fermer
      await user.click(button);
      vi.advanceTimersByTime(300);

      // Le tooltip est monté mais invisible, vérifier qu'il n'est pas visible
      const tooltip = screen.queryByText('Click tooltip');
      // Le tooltip peut être monté mais invisible, ou complètement démonté
      // On vérifie qu'il n'est pas visible (opacity-0 ou pas présent)
      if (tooltip) {
        expect(tooltip).toHaveClass('opacity-0');
      }
    });

    it('shows tooltip on focus when trigger is focus', () => {
      render(
        <Tooltip content="Focus tooltip" trigger="focus" delay={0}>
          <input type="text" placeholder="Focus me" />
        </Tooltip>,
      );

      const input = screen.getByPlaceholderText('Focus me');
      fireEvent.focus(input);

      vi.advanceTimersByTime(10);

      expect(screen.getByText('Focus tooltip')).toBeInTheDocument();
    });

    it('hides tooltip on blur when trigger is focus', () => {
      render(
        <Tooltip content="Focus tooltip" trigger="focus" delay={0}>
          <input type="text" placeholder="Focus me" />
        </Tooltip>,
      );

      const input = screen.getByPlaceholderText('Focus me');
      fireEvent.focus(input);
      vi.advanceTimersByTime(10);

      expect(screen.getByText('Focus tooltip')).toBeInTheDocument();

      fireEvent.blur(input);
      vi.advanceTimersByTime(300);

      // Le tooltip est monté mais invisible, vérifier qu'il n'est pas visible
      const tooltip = screen.queryByText('Focus tooltip');
      // Le tooltip peut être monté mais invisible, ou complètement démonté
      // On vérifie qu'il n'est pas visible (opacity-0 ou pas présent)
      if (tooltip) {
        expect(tooltip).toHaveClass('opacity-0');
      }
    });
  });

  describe('Advanced features', () => {
    it('shows arrow when showArrow is true', () => {
      render(
        <Tooltip content="With arrow" showArrow delay={0}>
          <button>Hover me</button>
        </Tooltip>,
      );

      const button = screen.getByText('Hover me');
      fireEvent.mouseEnter(button);
      vi.advanceTimersByTime(10);

      const tooltip = screen.getByText('With arrow');
      const arrow = tooltip.parentElement?.querySelector('.border-4');
      expect(arrow).toBeInTheDocument();
    });

    it('hides arrow when showArrow is false', () => {
      render(
        <Tooltip content="No arrow" showArrow={false} delay={0}>
          <button>Hover me</button>
        </Tooltip>,
      );

      const button = screen.getByText('Hover me');
      fireEvent.mouseEnter(button);
      vi.advanceTimersByTime(10);

      const tooltip = screen.getByText('No arrow');
      const arrow = tooltip.parentElement?.querySelector('.border-4');
      expect(arrow).not.toBeInTheDocument();
    });

    it('applies maxWidth style', () => {
      render(
        <Tooltip content="Limited width" maxWidth={200} delay={0}>
          <button>Hover me</button>
        </Tooltip>,
      );

      const button = screen.getByText('Hover me');
      fireEvent.mouseEnter(button);
      vi.advanceTimersByTime(10);

      const tooltip = screen.getByText('Limited width');
      expect(tooltip).toHaveStyle({ maxWidth: '200px' });
    });

    it('renders rich content with HTML elements', () => {
      render(
        <Tooltip
          content={
            <div>
              <strong>Rich content</strong>
              <p>With multiple elements</p>
            </div>
          }
          delay={0}
        >
          <button>Hover me</button>
        </Tooltip>,
      );

      const button = screen.getByText('Hover me');
      fireEvent.mouseEnter(button);
      vi.advanceTimersByTime(10);

      expect(screen.getByText('Rich content')).toBeInTheDocument();
      expect(screen.getByText('With multiple elements')).toBeInTheDocument();
    });
  });
});
