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

describe('Select Component', () => {
  const mockOnChange = vi.fn();
  const options = [
    { value: 'option1', label: 'Option 1' },
    { value: 'option2', label: 'Option 2' },
    { value: 'option3', label: 'Option 3' },
  ];

  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('renders select trigger correctly', () => {
    render(
      <Select
        options={options}
        onChange={mockOnChange}
        placeholder="Select..."
      />,
    );

    expect(screen.getByText('Select...')).toBeInTheDocument();
  });

  it('opens dropdown when trigger is clicked', async () => {
    const user = userEvent.setup();
    render(<Select options={options} onChange={mockOnChange} />);

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) =>
        btn.textContent?.includes('Select an option...'),
      ) || triggers[0];
    await user.click(trigger);

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

  it('displays selected value for single select', () => {
    render(
      <Select options={options} value="option1" onChange={mockOnChange} />,
    );

    expect(screen.getByText('Option 1')).toBeInTheDocument();
  });

  it('displays selected count for multi-select', () => {
    render(
      <Select
        options={options}
        value={['option1', 'option2']}
        onChange={mockOnChange}
        multiple
      />,
    );

    expect(screen.getByText('2 selected')).toBeInTheDocument();
  });

  it('calls onChange when option is selected in single mode', async () => {
    const user = userEvent.setup();
    render(<Select options={options} onChange={mockOnChange} />);

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) =>
        btn.textContent?.includes('Select an option...'),
      ) || triggers[0];
    await user.click(trigger);

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

    const option1 = screen.getByText('Option 1');
    await user.click(option1);

    expect(mockOnChange).toHaveBeenCalledWith('option1');
  });

  it('calls onChange when option is selected in multi mode', async () => {
    const user = userEvent.setup();
    render(<Select options={options} onChange={mockOnChange} multiple />);

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) =>
        btn.textContent?.includes('Select an option...'),
      ) || triggers[0];
    await user.click(trigger);

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

    const option1 = screen.getByText('Option 1');
    await user.click(option1);

    expect(mockOnChange).toHaveBeenCalledWith(['option1']);
  });

  it('toggles option selection in multi mode', async () => {
    const user = userEvent.setup();
    render(
      <Select
        options={options}
        value={['option1']}
        onChange={mockOnChange}
        multiple
      />,
    );

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) => btn.textContent?.includes('1 selected')) ||
      triggers[0];
    await user.click(trigger);

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

    const option1 = screen.getByText('Option 1');
    await user.click(option1);

    expect(mockOnChange).toHaveBeenCalledWith([]);
  });

  it('filters options when searchable is enabled', async () => {
    const user = userEvent.setup();
    render(<Select options={options} onChange={mockOnChange} searchable />);

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) =>
        btn.textContent?.includes('Select an option...'),
      ) || triggers[0];
    await user.click(trigger);

    await waitFor(() => {
      expect(screen.getByPlaceholderText('Search...')).toBeInTheDocument();
    });

    // Attendre que les options soient rendues
    await waitFor(() => {
      expect(screen.getAllByRole('menuitem').length).toBeGreaterThan(0);
    });

    const searchInput = screen.getByPlaceholderText('Search...');
    await user.type(searchInput, 'Option 1');

    await waitFor(
      () => {
        const menuItems = screen.getAllByRole('menuitem');
        const option1Item = menuItems.find((item) =>
          item.textContent?.includes('Option 1'),
        );
        expect(option1Item).toBeInTheDocument();
        const option2Item = menuItems.find((item) =>
          item.textContent?.includes('Option 2'),
        );
        expect(option2Item).not.toBeInTheDocument();
      },
      { timeout: 2000 },
    );
  });

  it('displays grouped options', async () => {
    const user = userEvent.setup();
    const groupedOptions = [
      { value: 'opt1', label: 'Option 1', group: 'Group 1' },
      { value: 'opt2', label: 'Option 2', group: 'Group 1' },
      { value: 'opt3', label: 'Option 3', group: 'Group 2' },
    ];

    render(<Select options={groupedOptions} onChange={mockOnChange} />);

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) =>
        btn.textContent?.includes('Select an option...'),
      ) || triggers[0];
    await user.click(trigger);

    await waitFor(() => {
      // Attendre que les options soient rendues
      expect(screen.getAllByRole('menuitem').length).toBeGreaterThan(0);
    });

    // Chercher les labels de groupe
    const groupLabels = Array.from(document.querySelectorAll('.text-xs'));
    const group1Label = groupLabels.find(
      (el) => el.textContent?.trim() === 'GROUP 1',
    );
    const group2Label = groupLabels.find(
      (el) => el.textContent?.trim() === 'GROUP 2',
    );
    expect(group1Label).toBeInTheDocument();
    expect(group2Label).toBeInTheDocument();
  });

  it('filters grouped options when searching', async () => {
    const user = userEvent.setup();
    const groupedOptions = [
      { value: 'opt1', label: 'Apple', group: 'Fruits' },
      { value: 'opt2', label: 'Banana', group: 'Fruits' },
      { value: 'opt3', label: 'Carrot', group: 'Vegetables' },
    ];

    render(
      <Select options={groupedOptions} onChange={mockOnChange} searchable />,
    );

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) =>
        btn.textContent?.includes('Select an option...'),
      ) || triggers[0];
    await user.click(trigger);

    await waitFor(() => {
      expect(screen.getByPlaceholderText('Search...')).toBeInTheDocument();
    });

    const searchInput = screen.getByPlaceholderText('Search...');
    await user.type(searchInput, 'Apple');

    expect(screen.getByText('Apple')).toBeInTheDocument();
    expect(screen.queryByText('Banana')).not.toBeInTheDocument();
    expect(screen.queryByText('Carrot')).not.toBeInTheDocument();
  });

  it('shows checkmark for selected option in single mode', async () => {
    const user = userEvent.setup();
    render(
      <Select options={options} value="option1" onChange={mockOnChange} />,
    );

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find(
        (btn) =>
          btn.textContent?.includes('Option 1') &&
          !btn.textContent?.includes('truncate'),
      ) || triggers[0];
    await user.click(trigger);

    await waitFor(() => {
      const menuItems = screen.getAllByRole('menuitem');
      const option1Item = menuItems.find(
        (item) => item.textContent?.trim() === 'Option 1',
      );
      expect(option1Item).toBeInTheDocument();
      expect(option1Item?.querySelector('svg')).toBeInTheDocument();
    });
  });

  it('shows checkbox for multi-select mode', async () => {
    const user = userEvent.setup();
    render(
      <Select
        options={options}
        value={['option1']}
        onChange={mockOnChange}
        multiple
      />,
    );

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) => btn.textContent?.includes('1 selected')) ||
      triggers[0];
    await user.click(trigger);

    await waitFor(() => {
      const option1 = screen.getByText('Option 1').closest('[role="menuitem"]');
      const checkbox = option1?.querySelector('.border');
      expect(checkbox).toBeInTheDocument();
    });
  });

  it('disables option when disabled prop is true', async () => {
    const user = userEvent.setup();
    const optionsWithDisabled = [
      { value: 'opt1', label: 'Option 1' },
      { value: 'opt2', label: 'Option 2', disabled: true },
    ];

    render(<Select options={optionsWithDisabled} onChange={mockOnChange} />);

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) =>
        btn.textContent?.includes('Select an option...'),
      ) || triggers[0];
    await user.click(trigger);

    await waitFor(() => {
      const option2 = screen.getByText('Option 2').closest('[role="menuitem"]');
      expect(option2).toHaveClass('opacity-50');
      expect(option2).toHaveClass('cursor-not-allowed');
    });
  });

  it('clears selection when clear button is clicked', async () => {
    const user = userEvent.setup();
    render(
      <Select options={options} value="option1" onChange={mockOnChange} />,
    );

    const triggers = screen.getAllByRole('button');
    const trigger = triggers.find((btn) =>
      btn.textContent?.includes('Option 1'),
    );
    const clearButton = trigger?.querySelector('svg');
    expect(clearButton).toBeInTheDocument();

    if (clearButton) {
      await user.click(clearButton);
      expect(mockOnChange).toHaveBeenCalledWith('');
    }
  });

  it('clears all selections in multi mode', async () => {
    const user = userEvent.setup();
    render(
      <Select
        options={options}
        value={['option1', 'option2']}
        onChange={mockOnChange}
        multiple
      />,
    );

    const triggers = screen.getAllByRole('button');
    const trigger = triggers.find((btn) =>
      btn.textContent?.includes('2 selected'),
    );
    const clearButton = trigger?.querySelector('svg');
    expect(clearButton).toBeInTheDocument();

    if (clearButton) {
      await user.click(clearButton);
      expect(mockOnChange).toHaveBeenCalledWith([]);
    }
  });

  it('displays "No options found" when search yields no results', async () => {
    const user = userEvent.setup();
    render(<Select options={options} onChange={mockOnChange} searchable />);

    const triggers = screen.getAllByRole('button');
    const trigger =
      triggers.find((btn) =>
        btn.textContent?.includes('Select an option...'),
      ) || triggers[0];
    await user.click(trigger);

    await waitFor(() => {
      expect(screen.getByPlaceholderText('Search...')).toBeInTheDocument();
    });

    const searchInput = screen.getByPlaceholderText('Search...');
    await user.type(searchInput, 'nonexistent');

    await waitFor(() => {
      expect(screen.getByText('No options found')).toBeInTheDocument();
    });
  });

  it('renders hidden input with name attribute', () => {
    render(
      <Select
        options={options}
        value="option1"
        onChange={mockOnChange}
        name="test-select"
      />,
    );

    const hiddenInput = document.querySelector(
      'input[type="hidden"][name="test-select"]',
    );
    expect(hiddenInput).toBeInTheDocument();
    expect(hiddenInput).toHaveValue('option1');
  });

  it('renders hidden input with comma-separated values for multi-select', () => {
    render(
      <Select
        options={options}
        value={['option1', 'option2']}
        onChange={mockOnChange}
        multiple
        name="test-select"
      />,
    );

    const hiddenInput = document.querySelector(
      'input[type="hidden"][name="test-select"]',
    );
    expect(hiddenInput).toBeInTheDocument();
    expect(hiddenInput).toHaveValue('option1,option2');
  });

  it('disables select when disabled prop is true', () => {
    render(<Select options={options} onChange={mockOnChange} disabled />);

    // Le Button dans le trigger devrait être disabled
    const buttons = document.querySelectorAll('button');
    const selectButton = Array.from(buttons).find(
      (btn) => btn.textContent?.includes('Select an option...') && btn.disabled,
    );
    expect(selectButton).toBeInTheDocument();
  });
});
