/**
 * KeyValueField Tests
 *
 * Tests the key-value field component including:
 * - Basic rendering
 * - Add/remove pairs
 * - Key and value changes
 * - Key selection from available keys
 * - Loading and dependency states
 * - Error display
 */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { KeyValueField } from './KeyValueField';
import { ActionField } from '../../types/action';
import { AvailableContext } from '../../hooks/useAvailableContext';

// Mock VariableInput
vi.mock('../VariableInput', () => ({
  VariableInput: ({
    value,
    onChange,
    placeholder,
  }: {
    value: string;
    onChange: (v: string) => void;
    placeholder?: string;
  }) => (
    <input
      data-testid="variable-input"
      value={value}
      onChange={(e) => onChange(e.target.value)}
      placeholder={placeholder}
    />
  ),
}));

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

// Mock Button
vi.mock('../ui/button', () => ({
  Button: ({
    onClick,
    children,
    'data-testid': testId,
    className,
  }: {
    onClick?: () => void;
    children: React.ReactNode;
    'data-testid'?: string;
    className?: string;
  }) => (
    <button
      onClick={onClick}
      data-testid={
        testId || (className?.includes('border-dashed') ? 'add-button' : 'remove-button')
      }
    >
      {children}
    </button>
  ),
}));

// Mock Input
vi.mock('../ui/input', () => ({
  Input: ({
    value,
    onChange,
    placeholder,
  }: {
    value: string;
    onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
    placeholder?: string;
  }) => (
    <input data-testid="key-input" value={value} onChange={onChange} placeholder={placeholder} />
  ),
}));

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

  const defaultField: ActionField = {
    label: 'Custom Fields',
    type: 'key_value',
    description: 'Map custom fields',
    required: false,
  };

  const emptyContext: AvailableContext = {
    groups: [],
  };

  const defaultProps = {
    fieldName: 'custom_fields',
    field: defaultField,
    value: {},
    availableKeys: [],
    isLoading: false,
    onChange: mockOnChange,
    availableContext: emptyContext,
    dependencyMet: true,
  };

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

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

  describe('basic rendering', () => {
    it('renders the field label', () => {
      render(<KeyValueField {...defaultProps} />);

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

    it('renders description', () => {
      render(<KeyValueField {...defaultProps} />);

      expect(screen.getByText('Map custom fields')).toBeInTheDocument();
    });

    it('renders add button', () => {
      render(<KeyValueField {...defaultProps} />);

      expect(screen.getByText('Add Custom Field')).toBeInTheDocument();
    });

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

      render(<KeyValueField {...defaultProps} field={requiredField} />);

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

  // ==========================================================================
  // Add Pair Tests
  // ==========================================================================

  describe('add pair', () => {
    it('calls onChange with new empty pair when add clicked', () => {
      render(<KeyValueField {...defaultProps} />);

      fireEvent.click(screen.getByText('Add Custom Field'));

      expect(mockOnChange).toHaveBeenCalledWith({ '': '' });
    });

    it('preserves existing pairs when adding', () => {
      render(<KeyValueField {...defaultProps} value={{ email: 'user@example.com' }} />);

      fireEvent.click(screen.getByText('Add Custom Field'));

      expect(mockOnChange).toHaveBeenCalledWith({
        email: 'user@example.com',
        '': '',
      });
    });
  });

  // ==========================================================================
  // Remove Pair Tests
  // ==========================================================================

  describe('remove pair', () => {
    it('calls onChange with pair removed', () => {
      render(
        <KeyValueField {...defaultProps} value={{ email: 'user@example.com', name: 'John' }} />,
      );

      const removeButtons = screen.getAllByTestId('remove-button');
      fireEvent.click(removeButtons[0]);

      expect(mockOnChange).toHaveBeenCalledWith({ name: 'John' });
    });

    it('renders remove button for each pair', () => {
      render(<KeyValueField {...defaultProps} value={{ field1: 'value1', field2: 'value2' }} />);

      expect(screen.getAllByTestId('remove-button')).toHaveLength(2);
    });
  });

  // ==========================================================================
  // Key Change Tests - Input Mode
  // ==========================================================================

  describe('key change - input mode', () => {
    it('renders text input for key when no availableKeys', () => {
      render(<KeyValueField {...defaultProps} value={{ '': 'value' }} />);

      expect(screen.getByTestId('key-input')).toBeInTheDocument();
    });

    it('calls onChange with updated key', () => {
      render(<KeyValueField {...defaultProps} value={{ '': 'value' }} />);

      fireEvent.change(screen.getByTestId('key-input'), {
        target: { value: 'email' },
      });

      expect(mockOnChange).toHaveBeenCalledWith({ email: 'value' });
    });
  });

  // ==========================================================================
  // Key Change Tests - Select Mode
  // ==========================================================================

  describe('key change - select mode', () => {
    const availableKeys = [
      { Key: 'email_key', FieldName: 'Email' },
      { Key: 'name_key', FieldName: 'Name' },
    ];

    it('renders select when availableKeys provided', () => {
      render(
        <KeyValueField {...defaultProps} value={{ '': 'value' }} availableKeys={availableKeys} />,
      );

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

    it('renders key options', () => {
      render(
        <KeyValueField {...defaultProps} value={{ '': 'value' }} availableKeys={availableKeys} />,
      );

      expect(screen.getByTestId('key-option-email_key')).toBeInTheDocument();
      expect(screen.getByTestId('key-option-name_key')).toBeInTheDocument();
    });

    it('displays field labels in options', () => {
      render(
        <KeyValueField {...defaultProps} value={{ '': 'value' }} availableKeys={availableKeys} />,
      );

      expect(screen.getByText('Email')).toBeInTheDocument();
      expect(screen.getByText('Name')).toBeInTheDocument();
    });

    it('uses custom key_field and label_field', () => {
      const fieldWithCustomFields = {
        ...defaultField,
        key_field: 'id',
        label_field: 'title',
      };

      const customKeys = [
        { id: 'field1', title: 'Field One' },
        { id: 'field2', title: 'Field Two' },
      ];

      render(
        <KeyValueField
          {...defaultProps}
          field={fieldWithCustomFields}
          value={{ '': 'value' }}
          availableKeys={customKeys}
        />,
      );

      expect(screen.getByTestId('key-option-field1')).toBeInTheDocument();
      expect(screen.getByText('Field One')).toBeInTheDocument();
    });
  });

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

  describe('value change', () => {
    it('renders VariableInput for value', () => {
      render(<KeyValueField {...defaultProps} value={{ email: '' }} />);

      expect(screen.getByTestId('variable-input')).toBeInTheDocument();
    });

    it('calls onChange with updated value', () => {
      render(<KeyValueField {...defaultProps} value={{ email: '' }} />);

      fireEvent.change(screen.getByTestId('variable-input'), {
        target: { value: 'user@example.com' },
      });

      expect(mockOnChange).toHaveBeenCalledWith({ email: 'user@example.com' });
    });
  });

  // ==========================================================================
  // Loading State Tests
  // ==========================================================================

  describe('loading state', () => {
    it('shows loading indicator when loading', () => {
      render(<KeyValueField {...defaultProps} isLoading={true} />);

      expect(screen.getByText('Loading custom fields...')).toBeInTheDocument();
    });

    it('does not show pairs when loading', () => {
      render(<KeyValueField {...defaultProps} isLoading={true} value={{ email: 'test' }} />);

      expect(screen.queryByTestId('variable-input')).not.toBeInTheDocument();
    });

    it('only shows loading when dependency is met', () => {
      render(<KeyValueField {...defaultProps} isLoading={true} dependencyMet={false} />);

      expect(screen.queryByText('Loading custom fields...')).not.toBeInTheDocument();
    });
  });

  // ==========================================================================
  // Dependency Not Met Tests
  // ==========================================================================

  describe('dependency not met', () => {
    it('shows dependency message when dependency not met', () => {
      const fieldWithDependency = {
        ...defaultField,
        depends_on: 'list_id',
      };

      render(<KeyValueField {...defaultProps} field={fieldWithDependency} dependencyMet={false} />);

      expect(screen.getByText(/Select a list id first/)).toBeInTheDocument();
    });

    it('does not show add button when dependency not met', () => {
      render(<KeyValueField {...defaultProps} dependencyMet={false} />);

      expect(screen.queryByText('Add Custom Field')).not.toBeInTheDocument();
    });
  });

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

  describe('error display', () => {
    it('displays validation error', () => {
      render(<KeyValueField {...defaultProps} error="At least one field required" />);

      expect(screen.getByText('At least one field required')).toBeInTheDocument();
    });

    it('displays field error', () => {
      render(<KeyValueField {...defaultProps} fieldError="Failed to load fields" />);

      expect(screen.getByText('Failed to load fields')).toBeInTheDocument();
    });

    it('shows both errors when present', () => {
      render(<KeyValueField {...defaultProps} error="Validation error" fieldError="Field error" />);

      expect(screen.getByText('Validation error')).toBeInTheDocument();
      expect(screen.getByText('Field error')).toBeInTheDocument();
    });
  });

  // ==========================================================================
  // Multiple Pairs Tests
  // ==========================================================================

  describe('multiple pairs', () => {
    it('renders all existing pairs', () => {
      render(
        <KeyValueField
          {...defaultProps}
          value={{
            email: 'user@example.com',
            name: 'John Doe',
            phone: '123-456-7890',
          }}
        />,
      );

      expect(screen.getAllByTestId('variable-input')).toHaveLength(3);
    });

    it('handles empty value object', () => {
      render(<KeyValueField {...defaultProps} value={{}} />);

      expect(screen.queryByTestId('variable-input')).not.toBeInTheDocument();
    });

    it('handles null value', () => {
      render(<KeyValueField {...defaultProps} value={null as any} />);

      expect(screen.queryByTestId('variable-input')).not.toBeInTheDocument();
    });
  });
});
