/**
 * Tests for FormSelect Component
 * Verifies select field rendering, accessibility, and error handling
 */

import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { useForm, FormProvider } from 'react-hook-form';
import { FormSelect, type SelectOption } from '@/components/forms/FormSelect';

// Polyfill for Radix UI Select - jsdom doesn't support these browser APIs
beforeAll(() => {
  Element.prototype.hasPointerCapture = jest.fn(() => false);
  Element.prototype.setPointerCapture = jest.fn();
  Element.prototype.releasePointerCapture = jest.fn();
  Element.prototype.scrollIntoView = jest.fn();
  window.HTMLElement.prototype.scrollIntoView = jest.fn();
});

// Helper wrapper component to provide form context
interface TestFormValues {
  model: string;
  category: string;
}

function TestWrapper({
  children,
  defaultValues = { model: '', category: '' },
}: {
  children: (props: {
    control: ReturnType<typeof useForm<TestFormValues>>['control'];
  }) => React.ReactNode;
  defaultValues?: Partial<TestFormValues>;
}) {
  const form = useForm<TestFormValues>({
    defaultValues: { model: '', category: '', ...defaultValues },
  });

  return <FormProvider {...form}>{children({ control: form.control })}</FormProvider>;
}

const mockOptions: SelectOption[] = [
  { value: 'claude-opus', label: 'Claude Opus' },
  { value: 'claude-sonnet', label: 'Claude Sonnet' },
  { value: 'claude-haiku', label: 'Claude Haiku' },
];

describe('FormSelect', () => {
  describe('Basic Rendering', () => {
    it('renders with label and select trigger', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
            />
          )}
        </TestWrapper>
      );

      expect(screen.getByText('Primary Model')).toBeInTheDocument();
      expect(screen.getByRole('combobox')).toBeInTheDocument();
    });

    it('renders with description', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
              description="Main model used for this agent"
            />
          )}
        </TestWrapper>
      );

      expect(screen.getByText('Main model used for this agent')).toBeInTheDocument();
    });

    it('renders with custom placeholder', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
              placeholder="Choose a model"
            />
          )}
        </TestWrapper>
      );

      expect(screen.getByText('Choose a model')).toBeInTheDocument();
    });

    it('renders default placeholder when none provided', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
            />
          )}
        </TestWrapper>
      );

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

  describe('Required Field', () => {
    it('shows asterisk when required is true', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
              required
            />
          )}
        </TestWrapper>
      );

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

    it('does not show asterisk when required is false', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
              required={false}
            />
          )}
        </TestWrapper>
      );

      expect(screen.queryByText('*')).not.toBeInTheDocument();
    });
  });

  describe('Options Rendering', () => {
    it('renders all options when opened', async () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
            />
          )}
        </TestWrapper>
      );

      // Open the select using fireEvent (works better with Radix UI)
      fireEvent.click(screen.getByRole('combobox'));

      // Check all options are rendered
      await waitFor(() => {
        expect(screen.getByRole('option', { name: 'Claude Opus' })).toBeInTheDocument();
      });
      expect(screen.getByRole('option', { name: 'Claude Sonnet' })).toBeInTheDocument();
      expect(screen.getByRole('option', { name: 'Claude Haiku' })).toBeInTheDocument();
    });

    it('selects option when clicked', async () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
            />
          )}
        </TestWrapper>
      );

      // Open the select and choose an option
      fireEvent.click(screen.getByRole('combobox'));

      await waitFor(() => {
        expect(screen.getByRole('option', { name: 'Claude Sonnet' })).toBeInTheDocument();
      });

      fireEvent.click(screen.getByRole('option', { name: 'Claude Sonnet' }));

      // The selected value should now be displayed
      await waitFor(() => {
        expect(screen.getByRole('combobox')).toHaveTextContent('Claude Sonnet');
      });
    });
  });

  describe('Disabled State', () => {
    it('disables select when disabled prop is true', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
              disabled
            />
          )}
        </TestWrapper>
      );

      expect(screen.getByRole('combobox')).toBeDisabled();
    });

    it('enables select when disabled prop is false', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
              disabled={false}
            />
          )}
        </TestWrapper>
      );

      expect(screen.getByRole('combobox')).not.toBeDisabled();
    });
  });

  describe('Pre-selected Value', () => {
    it('displays pre-selected value', () => {
      render(
        <TestWrapper defaultValues={{ model: 'claude-opus' }}>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
            />
          )}
        </TestWrapper>
      );

      expect(screen.getByRole('combobox')).toHaveTextContent('Claude Opus');
    });
  });

  describe('Accessibility', () => {
    it('links label to select via htmlFor/id', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
            />
          )}
        </TestWrapper>
      );

      const label = screen.getByText('Primary Model');
      const select = screen.getByRole('combobox');

      expect(label).toHaveAttribute('for', 'model');
      expect(select).toHaveAttribute('id', 'model');
    });

    it('sets aria-describedby with description ID when description exists', () => {
      render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
              description="Choose the main model"
            />
          )}
        </TestWrapper>
      );

      const select = screen.getByRole('combobox');
      expect(select).toHaveAttribute('aria-describedby', 'model-description');
    });
  });

  describe('Custom ClassName', () => {
    it('applies custom className to wrapper', () => {
      const { container } = render(
        <TestWrapper>
          {({ control }) => (
            <FormSelect
              name="model"
              control={control}
              label="Primary Model"
              options={mockOptions}
              className="custom-class"
            />
          )}
        </TestWrapper>
      );

      expect(container.querySelector('.custom-class')).toBeInTheDocument();
    });
  });

  describe('Error Handling', () => {
    it('displays error message when field has error', () => {
      function TestComponent() {
        const form = useForm<TestFormValues>({
          defaultValues: { model: '', category: '' },
        });

        React.useEffect(() => {
          form.setError('model', { type: 'required', message: 'Model is required' });
        }, [form]);

        return (
          <FormProvider {...form}>
            <FormSelect
              name="model"
              control={form.control}
              label="Primary Model"
              options={mockOptions}
            />
          </FormProvider>
        );
      }

      render(<TestComponent />);

      expect(screen.getByRole('alert')).toHaveTextContent('Model is required');
    });

    it('sets aria-invalid when error exists', () => {
      function TestComponent() {
        const form = useForm<TestFormValues>({
          defaultValues: { model: '', category: '' },
        });

        React.useEffect(() => {
          form.setError('model', { type: 'required', message: 'Model is required' });
        }, [form]);

        return (
          <FormProvider {...form}>
            <FormSelect
              name="model"
              control={form.control}
              label="Primary Model"
              options={mockOptions}
            />
          </FormProvider>
        );
      }

      render(<TestComponent />);

      expect(screen.getByRole('combobox')).toHaveAttribute('aria-invalid', 'true');
    });

    it('sets aria-describedby with error ID when error exists', () => {
      function TestComponent() {
        const form = useForm<TestFormValues>({
          defaultValues: { model: '', category: '' },
        });

        React.useEffect(() => {
          form.setError('model', { type: 'required', message: 'Model is required' });
        }, [form]);

        return (
          <FormProvider {...form}>
            <FormSelect
              name="model"
              control={form.control}
              label="Primary Model"
              options={mockOptions}
            />
          </FormProvider>
        );
      }

      render(<TestComponent />);

      expect(screen.getByRole('combobox')).toHaveAttribute('aria-describedby', 'model-error');
    });

    it('combines error and description IDs in aria-describedby', () => {
      function TestComponent() {
        const form = useForm<TestFormValues>({
          defaultValues: { model: '', category: '' },
        });

        React.useEffect(() => {
          form.setError('model', { type: 'required', message: 'Model is required' });
        }, [form]);

        return (
          <FormProvider {...form}>
            <FormSelect
              name="model"
              control={form.control}
              label="Primary Model"
              options={mockOptions}
              description="Choose the main model"
            />
          </FormProvider>
        );
      }

      render(<TestComponent />);

      expect(screen.getByRole('combobox')).toHaveAttribute(
        'aria-describedby',
        'model-error model-description'
      );
    });
  });
});
