import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { AgentTypeForm } from '@/components/agents/AgentTypeForm';
import type { AgentTypeResponse } from '@/lib/api/types/agentTypes';

const mockAgentType: AgentTypeResponse = {
  id: 'type-001',
  name: 'Software Architect',
  slug: 'software-architect',
  description: 'Designs system architecture',
  expertise: ['system design', 'api design'],
  personality_prompt: 'You are a Software Architect...',
  primary_model: 'claude-opus-4-5-20251101',
  fallback_models: ['claude-sonnet-4-20250514'],
  model_params: { temperature: 0.7, max_tokens: 8192, top_p: 0.95 },
  mcp_servers: ['gitea'],
  tool_permissions: {},
  is_active: true,
  created_at: '2025-01-10T00:00:00Z',
  updated_at: '2025-01-18T00:00:00Z',
  instance_count: 2,
};

describe('AgentTypeForm', () => {
  const defaultProps = {
    onSubmit: jest.fn(),
    onCancel: jest.fn(),
    isSubmitting: false,
  };

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

  describe('Create Mode', () => {
    it('renders create form title', () => {
      render(<AgentTypeForm {...defaultProps} />);
      expect(screen.getByText('Create Agent Type')).toBeInTheDocument();
      expect(screen.getByText('Define a new agent type template')).toBeInTheDocument();
    });

    it('renders all tabs', () => {
      render(<AgentTypeForm {...defaultProps} />);
      expect(screen.getByRole('tab', { name: /basic info/i })).toBeInTheDocument();
      expect(screen.getByRole('tab', { name: /model/i })).toBeInTheDocument();
      expect(screen.getByRole('tab', { name: /permissions/i })).toBeInTheDocument();
      expect(screen.getByRole('tab', { name: /personality/i })).toBeInTheDocument();
    });

    it('renders basic info fields by default', () => {
      render(<AgentTypeForm {...defaultProps} />);
      expect(screen.getByLabelText(/name/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/slug/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/description/i)).toBeInTheDocument();
    });

    it('auto-generates slug from name', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      const nameInput = screen.getByLabelText(/name/i);
      await user.type(nameInput, 'Product Owner');

      const slugInput = screen.getByLabelText(/slug/i) as HTMLInputElement;
      await waitFor(() => {
        expect(slugInput.value).toBe('product-owner');
      });
    });

    it('shows validation error for empty name', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      await user.click(screen.getByRole('button', { name: /create/i }));

      await waitFor(() => {
        expect(screen.getByText('Name is required')).toBeInTheDocument();
      });
    });

    it('shows validation error for empty personality prompt', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      // Fill name to pass first validation
      await user.type(screen.getByLabelText(/name/i), 'Test Agent');

      // Switch to personality tab
      await user.click(screen.getByRole('tab', { name: /personality/i }));

      await user.click(screen.getByRole('button', { name: /create/i }));

      await waitFor(() => {
        expect(screen.getByText('Personality prompt is required')).toBeInTheDocument();
      });
    });

    it('submits with default values when minimum required fields are filled', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      // Fill name (which auto-generates slug)
      await user.type(screen.getByLabelText(/name/i), 'Test Agent');

      // Wait for slug to auto-populate
      await waitFor(() => {
        expect((screen.getByLabelText(/slug/i) as HTMLInputElement).value).toBe('test-agent');
      });

      // Note: onSubmit will not be called because personality_prompt is required
      // This test just verifies the form fields are working correctly
      expect(defaultProps.onSubmit).not.toHaveBeenCalled();
    });

    it('calls onCancel when cancel button is clicked', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      await user.click(screen.getByRole('button', { name: /cancel/i }));
      expect(defaultProps.onCancel).toHaveBeenCalledTimes(1);
    });

    it('calls onCancel when back button is clicked', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      await user.click(screen.getByRole('button', { name: /go back/i }));
      expect(defaultProps.onCancel).toHaveBeenCalledTimes(1);
    });
  });

  describe('Edit Mode', () => {
    it('renders edit form title', () => {
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);
      expect(screen.getByText('Edit Agent Type')).toBeInTheDocument();
      expect(screen.getByText('Modify agent type configuration')).toBeInTheDocument();
    });

    it('pre-fills form with agent type data', () => {
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      const nameInput = screen.getByLabelText(/name/i) as HTMLInputElement;
      expect(nameInput.value).toBe('Software Architect');

      const slugInput = screen.getByLabelText(/slug/i) as HTMLInputElement;
      expect(slugInput.value).toBe('software-architect');
    });

    it('shows save changes button', () => {
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);
      expect(screen.getByRole('button', { name: /save changes/i })).toBeInTheDocument();
    });

    it('does not auto-generate slug when editing', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      const nameInput = screen.getByLabelText(/name/i);
      await user.clear(nameInput);
      await user.type(nameInput, 'New Name');

      // Slug should remain unchanged
      const slugInput = screen.getByLabelText(/slug/i) as HTMLInputElement;
      expect(slugInput.value).toBe('software-architect');
    });
  });

  describe('Tabs', () => {
    it('renders all tab triggers', () => {
      render(<AgentTypeForm {...defaultProps} />);

      expect(screen.getByRole('tab', { name: /basic info/i })).toBeInTheDocument();
      expect(screen.getByRole('tab', { name: /model/i })).toBeInTheDocument();
      expect(screen.getByRole('tab', { name: /permissions/i })).toBeInTheDocument();
      expect(screen.getByRole('tab', { name: /personality/i })).toBeInTheDocument();
    });

    it('basic info tab is active by default', () => {
      render(<AgentTypeForm {...defaultProps} />);

      // Basic Info content should be visible
      expect(screen.getByLabelText(/name/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/slug/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/description/i)).toBeInTheDocument();
    });
  });

  describe('Expertise Management', () => {
    it('adds expertise when add button is clicked', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      const expertiseInput = screen.getByPlaceholderText(/e.g., system design/i);
      await user.type(expertiseInput, 'new skill');
      await user.click(screen.getByRole('button', { name: /^add$/i }));

      expect(screen.getByText('new skill')).toBeInTheDocument();
    });

    it('adds expertise on enter key', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      const expertiseInput = screen.getByPlaceholderText(/e.g., system design/i);
      await user.type(expertiseInput, 'keyboard skill{Enter}');

      expect(screen.getByText('keyboard skill')).toBeInTheDocument();
    });

    it('removes expertise when X button is clicked', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      // Should have existing expertise
      expect(screen.getByText('system design')).toBeInTheDocument();

      // Click remove button
      const removeButton = screen.getByRole('button', { name: /remove system design/i });
      await user.click(removeButton);

      expect(screen.queryByText('system design')).not.toBeInTheDocument();
    });
  });

  describe('Form State', () => {
    it('disables buttons when submitting', () => {
      render(<AgentTypeForm {...defaultProps} isSubmitting={true} />);

      expect(screen.getByRole('button', { name: /saving/i })).toBeDisabled();
      expect(screen.getByRole('button', { name: /cancel/i })).toBeDisabled();
    });

    it('applies custom className', () => {
      const { container } = render(<AgentTypeForm {...defaultProps} className="custom-class" />);
      expect(container.querySelector('form')).toHaveClass('custom-class');
    });
  });

  describe('Model Tab', () => {
    it('switches to model tab and shows model selection', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      await user.click(screen.getByRole('tab', { name: /model/i }));

      expect(screen.getByText('Model Selection')).toBeInTheDocument();
      expect(screen.getByText('Choose the AI models that power this agent type')).toBeInTheDocument();
      expect(screen.getByLabelText(/primary model/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/fallover model/i)).toBeInTheDocument();
    });

    it('shows model parameters section', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      await user.click(screen.getByRole('tab', { name: /model/i }));

      expect(screen.getByText('Model Parameters')).toBeInTheDocument();
      expect(screen.getByLabelText(/temperature/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/max tokens/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/top p/i)).toBeInTheDocument();
    });

    it('allows changing temperature', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      await user.click(screen.getByRole('tab', { name: /model/i }));

      const temperatureInput = screen.getByLabelText(/temperature/i) as HTMLInputElement;
      expect(temperatureInput.value).toBe('0.7');

      await user.clear(temperatureInput);
      await user.type(temperatureInput, '0.9');

      expect(temperatureInput.value).toBe('0.9');
    });

    it('allows changing max tokens', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      await user.click(screen.getByRole('tab', { name: /model/i }));

      const maxTokensInput = screen.getByLabelText(/max tokens/i) as HTMLInputElement;
      expect(maxTokensInput.value).toBe('8192');

      await user.clear(maxTokensInput);
      await user.type(maxTokensInput, '16384');

      expect(maxTokensInput.value).toBe('16384');
    });

    it('allows changing top p', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      await user.click(screen.getByRole('tab', { name: /model/i }));

      const topPInput = screen.getByLabelText(/top p/i) as HTMLInputElement;
      expect(topPInput.value).toBe('0.95');

      await user.clear(topPInput);
      await user.type(topPInput, '0.8');

      expect(topPInput.value).toBe('0.8');
    });

    it('shows primary model select trigger', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      await user.click(screen.getByRole('tab', { name: /model/i }));

      // Verify the select trigger is present and labeled correctly
      const primaryModelTrigger = screen.getByLabelText(/primary model/i);
      expect(primaryModelTrigger).toBeInTheDocument();
      expect(primaryModelTrigger).toHaveAttribute('role', 'combobox');
    });

    it('shows fallback model select trigger', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      await user.click(screen.getByRole('tab', { name: /model/i }));

      // Verify the select trigger is present and labeled correctly
      const fallbackModelTrigger = screen.getByLabelText(/fallover model/i);
      expect(fallbackModelTrigger).toBeInTheDocument();
      expect(fallbackModelTrigger).toHaveAttribute('role', 'combobox');
    });

    it('displays pre-selected model in edit mode', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      await user.click(screen.getByRole('tab', { name: /model/i }));

      // Check that the selected model is displayed (multiple elements exist due to hidden native select)
      const modelTexts = screen.getAllByText('Claude Opus 4.5');
      expect(modelTexts.length).toBeGreaterThan(0);
    });
  });

  describe('Permissions Tab', () => {
    it('has a permissions tab trigger', () => {
      render(<AgentTypeForm {...defaultProps} />);

      const permissionsTab = screen.getByRole('tab', { name: /permissions/i });
      expect(permissionsTab).toBeInTheDocument();
      expect(permissionsTab).toHaveAttribute('aria-controls');
    });

    it('permissions tab content exists', () => {
      render(<AgentTypeForm {...defaultProps} />);

      // The tab panel for permissions should exist (even if hidden)
      const tabPanels = document.querySelectorAll('[role="tabpanel"]');
      expect(tabPanels.length).toBeGreaterThan(0);
    });
  });

  describe('Personality Tab', () => {
    it('switches to personality tab and shows prompt editor', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      await user.click(screen.getByRole('tab', { name: /personality/i }));

      expect(screen.getByText('Personality Prompt')).toBeInTheDocument();
      expect(screen.getByPlaceholderText(/you are a/i)).toBeInTheDocument();
    });

    it('shows character count', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      await user.click(screen.getByRole('tab', { name: /personality/i }));

      // Should show character count for the existing prompt
      expect(screen.getByText(/character count:/i)).toBeInTheDocument();
    });

    it('updates character count when typing', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      await user.click(screen.getByRole('tab', { name: /personality/i }));

      const promptTextarea = screen.getByPlaceholderText(/you are a/i);
      await user.type(promptTextarea, 'Test prompt');

      expect(screen.getByText('Character count: 11')).toBeInTheDocument();
    });

    it('pre-fills personality prompt in edit mode', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      await user.click(screen.getByRole('tab', { name: /personality/i }));

      const promptTextarea = screen.getByPlaceholderText(/you are a/i) as HTMLTextAreaElement;
      expect(promptTextarea.value).toBe('You are a Software Architect...');
    });
  });

  describe('Status Field', () => {
    it('shows status select in basic info tab', () => {
      render(<AgentTypeForm {...defaultProps} />);

      expect(screen.getByLabelText(/status/i)).toBeInTheDocument();
    });

    it('shows status select as combobox', () => {
      render(<AgentTypeForm {...defaultProps} />);

      const statusTrigger = screen.getByLabelText(/status/i);
      expect(statusTrigger).toHaveAttribute('role', 'combobox');
    });

    it('displays active status for new agent by default', () => {
      render(<AgentTypeForm {...defaultProps} />);

      // The select trigger should show "Active" by default
      expect(screen.getByText('Active')).toBeInTheDocument();
    });

    it('preserves active status in edit mode', () => {
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      // Mock agent has is_active = true
      // Status select trigger should show "Active"
      const statusTrigger = screen.getByLabelText(/status/i);
      expect(statusTrigger).toHaveTextContent('Active');
    });

    it('shows inactive status when agent is inactive', () => {
      const inactiveAgent = { ...mockAgentType, is_active: false };
      render(<AgentTypeForm {...defaultProps} agentType={inactiveAgent} />);

      // Status select trigger should show "Inactive / Draft"
      const statusTrigger = screen.getByLabelText(/status/i);
      expect(statusTrigger).toHaveTextContent('Inactive');
    });
  });

  describe('Expertise Edge Cases', () => {
    it('does not add duplicate expertise', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} agentType={mockAgentType} />);

      // Agent type already has 'system design'
      const expertiseInput = screen.getByPlaceholderText(/e.g., system design/i);
      await user.type(expertiseInput, 'system design');
      await user.click(screen.getByRole('button', { name: /^add$/i }));

      // Should still only have one 'system design' badge
      const badges = screen.getAllByText('system design');
      expect(badges).toHaveLength(1);
    });

    it('does not add empty expertise', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      const addButton = screen.getByRole('button', { name: /^add$/i });
      await user.click(addButton);

      // No badges should be added
      expect(screen.queryByRole('button', { name: /^remove/i })).not.toBeInTheDocument();
    });

    it('converts expertise to lowercase', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      const expertiseInput = screen.getByPlaceholderText(/e.g., system design/i);
      await user.type(expertiseInput, 'API Design');
      await user.click(screen.getByRole('button', { name: /^add$/i }));

      expect(screen.getByText('api design')).toBeInTheDocument();
    });

    it('trims whitespace from expertise', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      const expertiseInput = screen.getByPlaceholderText(/e.g., system design/i);
      await user.type(expertiseInput, '  testing  ');
      await user.click(screen.getByRole('button', { name: /^add$/i }));

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

    it('clears input after adding expertise', async () => {
      const user = userEvent.setup();
      render(<AgentTypeForm {...defaultProps} />);

      const expertiseInput = screen.getByPlaceholderText(/e.g., system design/i) as HTMLInputElement;
      await user.type(expertiseInput, 'new skill');
      await user.click(screen.getByRole('button', { name: /^add$/i }));

      expect(expertiseInput.value).toBe('');
    });
  });

  describe('Form Submission', () => {
    it('calls onSubmit when form is valid', async () => {
      const user = userEvent.setup();
      const onSubmit = jest.fn();
      render(<AgentTypeForm {...defaultProps} onSubmit={onSubmit} agentType={mockAgentType} />);

      // Submit the pre-filled form
      await user.click(screen.getByRole('button', { name: /save changes/i }));

      // React Hook Form should call onSubmit with form data
      await waitFor(
        () => {
          expect(onSubmit).toHaveBeenCalled();
        },
        { timeout: 2000 }
      );
    });

    it('passes form data to onSubmit callback', async () => {
      const user = userEvent.setup();
      const onSubmit = jest.fn();
      render(<AgentTypeForm {...defaultProps} onSubmit={onSubmit} agentType={mockAgentType} />);

      await user.click(screen.getByRole('button', { name: /save changes/i }));

      await waitFor(
        () => {
          expect(onSubmit).toHaveBeenCalled();
        },
        { timeout: 2000 }
      );

      // Verify the first argument contains expected fields
      const formData = onSubmit.mock.calls[0][0];
      expect(formData).toHaveProperty('name', 'Software Architect');
      expect(formData).toHaveProperty('slug', 'software-architect');
      expect(formData).toHaveProperty('expertise');
      expect(formData).toHaveProperty('personality_prompt');
    });
  });

  describe('Null Model Params Handling', () => {
    it('handles null model_params gracefully', () => {
      const agentTypeWithNullParams: AgentTypeResponse = {
        ...mockAgentType,
        model_params: null,
      };

      render(<AgentTypeForm {...defaultProps} agentType={agentTypeWithNullParams} />);

      // Should render without errors
      expect(screen.getByText('Edit Agent Type')).toBeInTheDocument();
    });
  });
});
