import { describe, expect, test, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { MantineProvider } from '@mantine/core';
import ButtonSelector from './ButtonSelector';

// Wrapper component to provide Mantine context
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
  <MantineProvider>{children}</MantineProvider>
);

describe('ButtonSelector', () => {
  const mockOnChange = vi.fn();

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

  test('should render all options as buttons', () => {
    const options = [
      { value: 'option1', label: 'Option 1' },
      { value: 'option2', label: 'Option 2' },
    ];

    render(
      <TestWrapper>
        <ButtonSelector
          value="option1"
          onChange={mockOnChange}
          options={options}
          label="Test Label"
        />
      </TestWrapper>
    );

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

  test('should highlight selected button with filled variant', () => {
    const options = [
      { value: 'option1', label: 'Option 1' },
      { value: 'option2', label: 'Option 2' },
    ];

    render(
      <TestWrapper>
        <ButtonSelector
          value="option1"
          onChange={mockOnChange}
          options={options}
          label="Selection Label"
        />
      </TestWrapper>
    );

    const selectedButton = screen.getByRole('button', { name: 'Option 1' });
    const unselectedButton = screen.getByRole('button', { name: 'Option 2' });

    // Check data-variant attribute for filled/outline
    expect(selectedButton).toHaveAttribute('data-variant', 'filled');
    expect(unselectedButton).toHaveAttribute('data-variant', 'outline');
    expect(screen.getByText('Selection Label')).toBeInTheDocument();
  });

  test('should call onChange when button is clicked', () => {
    const options = [
      { value: 'option1', label: 'Option 1' },
      { value: 'option2', label: 'Option 2' },
    ];

    render(
      <TestWrapper>
        <ButtonSelector
          value="option1"
          onChange={mockOnChange}
          options={options}
        />
      </TestWrapper>
    );

    fireEvent.click(screen.getByRole('button', { name: 'Option 2' }));

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

  test('should handle undefined value (no selection)', () => {
    const options = [
      { value: 'option1', label: 'Option 1' },
      { value: 'option2', label: 'Option 2' },
    ];

    render(
      <TestWrapper>
        <ButtonSelector
          value={undefined}
          onChange={mockOnChange}
          options={options}
        />
      </TestWrapper>
    );

    // Both buttons should be outlined when no value is selected
    const button1 = screen.getByRole('button', { name: 'Option 1' });
    const button2 = screen.getByRole('button', { name: 'Option 2' });

    expect(button1).toHaveAttribute('data-variant', 'outline');
    expect(button2).toHaveAttribute('data-variant', 'outline');
  });

  test.each([
    {
      description: 'disable buttons when disabled prop is true',
      options: [
        { value: 'option1', label: 'Option 1' },
        { value: 'option2', label: 'Option 2' },
      ],
      globalDisabled: true,
      expectedStates: [true, true],
    },
    {
      description: 'disable individual options when option.disabled is true',
      options: [
        { value: 'option1', label: 'Option 1' },
        { value: 'option2', label: 'Option 2', disabled: true },
      ],
      globalDisabled: false,
      expectedStates: [false, true],
    },
  ])('should $description', ({ options, globalDisabled, expectedStates }) => {
    render(
      <TestWrapper>
        <ButtonSelector
          value="option1"
          onChange={mockOnChange}
          options={options}
          disabled={globalDisabled}
        />
      </TestWrapper>
    );

    options.forEach((option, index) => {
      const button = screen.getByRole('button', { name: option.label });
      expect(button).toHaveProperty('disabled', expectedStates[index]);
    });
  });

  test('should not call onChange when disabled button is clicked', () => {
    const options = [
      { value: 'option1', label: 'Option 1' },
      { value: 'option2', label: 'Option 2', disabled: true },
    ];

    render(
      <TestWrapper>
        <ButtonSelector
          value="option1"
          onChange={mockOnChange}
          options={options}
        />
      </TestWrapper>
    );

    fireEvent.click(screen.getByRole('button', { name: 'Option 2' }));

    expect(mockOnChange).not.toHaveBeenCalled();
  });

  test('should not apply fullWidth styling when fullWidth is false', () => {
    const options = [
      { value: 'option1', label: 'Option 1' },
      { value: 'option2', label: 'Option 2' },
    ];

    render(
      <TestWrapper>
        <ButtonSelector
          value="option1"
          onChange={mockOnChange}
          options={options}
          fullWidth={false}
          label="Layout Label"
        />
      </TestWrapper>
    );

    const button = screen.getByRole('button', { name: 'Option 1' });
    expect(button).not.toHaveStyle({ flex: '1' });
    expect(screen.getByText('Layout Label')).toBeInTheDocument();
  });

  test('should not render label element when not provided', () => {
    const options = [
      { value: 'option1', label: 'Option 1' },
      { value: 'option2', label: 'Option 2' },
    ];

    const { container } = render(
      <TestWrapper>
        <ButtonSelector
          value="option1"
          onChange={mockOnChange}
          options={options}
        />
      </TestWrapper>
    );

    // Should render buttons
    expect(screen.getByText('Option 1')).toBeInTheDocument();
    expect(screen.getByText('Option 2')).toBeInTheDocument();
    
    // Stack should only contain the Group (buttons), no Text element for label
    const stackElement = container.querySelector('[class*="mantine-Stack-root"]');
    expect(stackElement?.children).toHaveLength(1); // Only the Group, no label Text
  });
});
