/**
 * SelectField Tests
 *
 * Tests the select field component including:
 * - Basic rendering
 * - Option normalization (string, object, PHP associative array)
 * - Selection handling
 * - Error display
 */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/react';
import { SelectField } from './SelectField';
import { ActionField } from '../../types/action';

// Mock Select components
vi.mock('../ui/select', () => ({
  Select: ({
    value,
    onValueChange: _onValueChange,
    children,
  }: {
    value: string;
    onValueChange: (v: string) => void;
    children: React.ReactNode;
  }) => (
    <div data-testid="select-root" data-value={value}>
      {children}
    </div>
  ),
  SelectTrigger: ({ id, children }: { id: string; children: React.ReactNode }) => (
    <button data-testid="select-trigger" id={id}>
      {children}
    </button>
  ),
  SelectValue: ({ placeholder }: { placeholder?: string }) => (
    <span data-testid="select-value" data-placeholder={placeholder} />
  ),
  SelectContent: ({ children }: { children: React.ReactNode }) => (
    <div data-testid="select-content">{children}</div>
  ),
  SelectItem: ({ value, children }: { value: string; children: React.ReactNode }) => (
    <div data-testid={`select-item-${value}`} data-value={value}>
      {children}
    </div>
  ),
}));

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

  const defaultField: ActionField = {
    label: 'Status',
    type: 'select',
    description: 'Select a status',
    required: false,
    options: [
      { value: 'active', label: 'Active' },
      { value: 'pending', label: 'Pending' },
      { value: 'inactive', label: 'Inactive' },
    ],
  };

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

  // ==========================================================================
  // Basic Rendering Tests
  // ==========================================================================

  describe('basic rendering', () => {
    it('renders the field label', () => {
      render(
        <SelectField fieldName="status" field={defaultField} value="" onChange={mockOnChange} />,
      );

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

    it('renders select component', () => {
      render(
        <SelectField fieldName="status" field={defaultField} value="" onChange={mockOnChange} />,
      );

      expect(screen.getByTestId('select-root')).toBeInTheDocument();
    });

    it('renders description', () => {
      render(
        <SelectField fieldName="status" field={defaultField} value="" onChange={mockOnChange} />,
      );

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

    it('sets correct trigger id', () => {
      render(
        <SelectField fieldName="status" field={defaultField} value="" onChange={mockOnChange} />,
      );

      expect(screen.getByTestId('select-trigger')).toHaveAttribute('id', 'status');
    });
  });

  // ==========================================================================
  // Required Indicator Tests
  // ==========================================================================

  describe('required indicator', () => {
    it('shows required asterisk when field is required', () => {
      const requiredField = { ...defaultField, required: true };

      render(
        <SelectField fieldName="status" field={requiredField} value="" onChange={mockOnChange} />,
      );

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

  // ==========================================================================
  // Option Normalization Tests - Object Format
  // ==========================================================================

  describe('option normalization - object format', () => {
    it('renders all options from object array', () => {
      render(
        <SelectField fieldName="status" field={defaultField} value="" onChange={mockOnChange} />,
      );

      expect(screen.getByTestId('select-item-active')).toBeInTheDocument();
      expect(screen.getByTestId('select-item-pending')).toBeInTheDocument();
      expect(screen.getByTestId('select-item-inactive')).toBeInTheDocument();
    });

    it('displays correct labels for object options', () => {
      render(
        <SelectField fieldName="status" field={defaultField} value="" onChange={mockOnChange} />,
      );

      expect(screen.getByText('Active')).toBeInTheDocument();
      expect(screen.getByText('Pending')).toBeInTheDocument();
      expect(screen.getByText('Inactive')).toBeInTheDocument();
    });
  });

  // ==========================================================================
  // Option Normalization Tests - String Array Format
  // ==========================================================================

  describe('option normalization - string array format', () => {
    const stringOptionsField: ActionField = {
      ...defaultField,
      options: [
        { value: 'draft', label: 'Draft' },
        { value: 'published', label: 'Published' },
        { value: 'archived', label: 'Archived' },
      ],
    };

    it('renders string options', () => {
      render(
        <SelectField
          fieldName="status"
          field={stringOptionsField}
          value=""
          onChange={mockOnChange}
        />,
      );

      expect(screen.getByTestId('select-item-draft')).toBeInTheDocument();
      expect(screen.getByTestId('select-item-published')).toBeInTheDocument();
      expect(screen.getByTestId('select-item-archived')).toBeInTheDocument();
    });

    it('formats string options as labels', () => {
      render(
        <SelectField
          fieldName="status"
          field={stringOptionsField}
          value=""
          onChange={mockOnChange}
        />,
      );

      expect(screen.getByText('Draft')).toBeInTheDocument();
      expect(screen.getByText('Published')).toBeInTheDocument();
      expect(screen.getByText('Archived')).toBeInTheDocument();
    });

    it('replaces underscores with spaces in labels', () => {
      const underscoreField: ActionField = {
        ...defaultField,
        options: [
          { value: 'in_progress', label: 'In progress' },
          { value: 'not_started', label: 'Not started' },
        ],
      };

      render(
        <SelectField fieldName="status" field={underscoreField} value="" onChange={mockOnChange} />,
      );

      expect(screen.getByText('In progress')).toBeInTheDocument();
      expect(screen.getByText('Not started')).toBeInTheDocument();
    });
  });

  // ==========================================================================
  // Option Normalization Tests - PHP Associative Array Format
  // ==========================================================================

  describe('option normalization - PHP associative array format', () => {
    const phpOptionsField: ActionField = {
      ...defaultField,
      options: {
        draft: 'Draft Status',
        published: 'Published Status',
        archived: 'Archived Status',
      } as any,
    };

    it('renders options from PHP object format', () => {
      render(
        <SelectField fieldName="status" field={phpOptionsField} value="" onChange={mockOnChange} />,
      );

      expect(screen.getByTestId('select-item-draft')).toBeInTheDocument();
      expect(screen.getByTestId('select-item-published')).toBeInTheDocument();
      expect(screen.getByTestId('select-item-archived')).toBeInTheDocument();
    });

    it('uses object values as labels', () => {
      render(
        <SelectField fieldName="status" field={phpOptionsField} value="" onChange={mockOnChange} />,
      );

      expect(screen.getByText('Draft Status')).toBeInTheDocument();
      expect(screen.getByText('Published Status')).toBeInTheDocument();
      expect(screen.getByText('Archived Status')).toBeInTheDocument();
    });
  });

  // ==========================================================================
  // Empty Options Tests
  // ==========================================================================

  describe('empty options', () => {
    it('handles undefined options', () => {
      const noOptionsField: ActionField = {
        ...defaultField,
        options: undefined,
      };

      render(
        <SelectField fieldName="status" field={noOptionsField} value="" onChange={mockOnChange} />,
      );

      expect(screen.getByTestId('select-content')).toBeEmptyDOMElement();
    });

    it('handles empty array options', () => {
      const emptyOptionsField: ActionField = {
        ...defaultField,
        options: [],
      };

      render(
        <SelectField
          fieldName="status"
          field={emptyOptionsField}
          value=""
          onChange={mockOnChange}
        />,
      );

      expect(screen.getByTestId('select-content')).toBeEmptyDOMElement();
    });
  });

  // ==========================================================================
  // Value Tests
  // ==========================================================================

  describe('value handling', () => {
    it('passes current value to select', () => {
      render(
        <SelectField
          fieldName="status"
          field={defaultField}
          value="active"
          onChange={mockOnChange}
        />,
      );

      expect(screen.getByTestId('select-root')).toHaveAttribute('data-value', 'active');
    });

    it('shows placeholder as description', () => {
      render(
        <SelectField fieldName="status" field={defaultField} value="" onChange={mockOnChange} />,
      );

      expect(screen.getByTestId('select-value')).toHaveAttribute(
        'data-placeholder',
        'Select a status',
      );
    });
  });

  // ==========================================================================
  // Error Display Tests
  // ==========================================================================

  describe('error display', () => {
    it('displays error message when provided', () => {
      render(
        <SelectField
          fieldName="status"
          field={defaultField}
          value=""
          onChange={mockOnChange}
          error="Please select a status"
        />,
      );

      expect(screen.getByText('Please select a status')).toBeInTheDocument();
    });

    it('does not render error element when no error', () => {
      const { container } = render(
        <SelectField fieldName="status" field={defaultField} value="" onChange={mockOnChange} />,
      );

      // Should have description but no error
      const errorElement = container.querySelectorAll('.text-red-600');
      expect(errorElement).toHaveLength(0);
    });
  });
});
