import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { useForm } from 'react-hook-form';
import { describe, expect, it } from 'vitest';

import { Button } from './button';
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from './form';
import { Input } from './input';

// Test form component wrapper
function TestFormComponent() {
  const form = useForm({
    defaultValues: {
      email: '',
      name: '',
    },
  });

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const onSubmit = async (data: Record<string, string>) => {
    // Mock submit - form submission is handled by react-hook-form
  };

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
        <FormField
          control={form.control}
          name="email"
          rules={{
            required: 'Email is required',
            pattern: {
              value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
              message: 'Invalid email address',
            },
          }}
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input placeholder="email@example.com" {...field} />
              </FormControl>
              <FormDescription>Your primary email address</FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />

        <FormField
          control={form.control}
          name="name"
          rules={{ required: 'Name is required' }}
          render={({ field }) => (
            <FormItem>
              <FormLabel>Full Name</FormLabel>
              <FormControl>
                <Input placeholder="John Doe" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        <Button type="submit">Submit</Button>
      </form>
    </Form>
  );
}

describe('Form Components', () => {
  const user = userEvent.setup();

  // ============================================
  // FormItem tests
  // ============================================

  describe('FormItem', () => {
    it('renders form item container', () => {
      render(<TestFormComponent />);

      const emailLabel = screen.getByLabelText('Email');
      expect(emailLabel).toBeInTheDocument();
    });

    it('provides unique id for form elements', () => {
      render(<TestFormComponent />);

      const input = screen.getByPlaceholderText('email@example.com') as HTMLInputElement;
      expect(input.id).toBeTruthy();
      expect(input.id).toBeTruthy();
    });
  });

  // ============================================
  // FormLabel tests
  // ============================================

  describe('FormLabel', () => {
    it('renders label text', () => {
      render(<TestFormComponent />);

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

    it('associates label with input via htmlFor', () => {
      render(<TestFormComponent />);

      const emailLabel = screen.getByText('Email') as HTMLLabelElement;

      // The label should have a for attribute pointing to a form item ID
      expect(emailLabel).toHaveAttribute('for');
      const labelFor = emailLabel.getAttribute('for');
      expect(labelFor).toBeTruthy();
      expect(labelFor).toContain('form-item');
    });
  });

  // ============================================
  // FormDescription tests
  // ============================================

  describe('FormDescription', () => {
    it('renders description text', () => {
      render(<TestFormComponent />);

      expect(screen.getByText('Your primary email address')).toBeInTheDocument();
    });

    it('has unique id for aria-describedby', () => {
      const { container } = render(<TestFormComponent />);

      const descriptions = container.querySelectorAll('p[class*="text-muted-foreground"]');
      descriptions.forEach((desc) => {
        expect(desc).toHaveAttribute('id');
      });
    });
  });

  // ============================================
  // FormControl tests
  // ============================================

  describe('FormControl', () => {
    it('renders control element', () => {
      render(<TestFormComponent />);

      expect(screen.getByPlaceholderText('email@example.com')).toBeInTheDocument();
    });

    it('sets aria-invalid when field has error', async () => {
      render(<TestFormComponent />);

      const submitButton = screen.getByRole('button', { name: /submit/i });
      await user.click(submitButton);

      const emailInput = screen.getByPlaceholderText('email@example.com') as HTMLInputElement;
      expect(emailInput).toHaveAttribute('aria-invalid', 'true');
    });

    it('sets aria-invalid="false" when field is valid', () => {
      render(<TestFormComponent />);

      const emailInput = screen.getByPlaceholderText('email@example.com') as HTMLInputElement;
      expect(emailInput).toHaveAttribute('aria-invalid', 'false');
    });
  });

  // ============================================
  // FormMessage tests
  // ============================================

  describe('FormMessage', () => {
    it('renders error message when field has error', async () => {
      render(<TestFormComponent />);

      const submitButton = screen.getByRole('button', { name: /submit/i });
      await user.click(submitButton);

      expect(screen.getByText('Email is required')).toBeInTheDocument();
    });

    it('does not render message when field is valid', () => {
      render(<TestFormComponent />);

      expect(screen.queryByText('Email is required')).not.toBeInTheDocument();
    });

    it('has unique id for aria-describedby reference', async () => {
      const { container } = render(<TestFormComponent />);

      const submitButton = screen.getByRole('button', { name: /submit/i });
      await user.click(submitButton);

      const errorMessages = container.querySelectorAll('p[class*="text-destructive"]');
      errorMessages.forEach((msg) => {
        expect(msg).toHaveAttribute('id');
      });
    });
  });

  // ============================================
  // Accessibility tests (A11Y-004)
  // ============================================

  describe('accessibility - aria-describedby linking (A11Y-004)', () => {
    it('links input to description via aria-describedby', () => {
      render(<TestFormComponent />);

      const emailInput = screen.getByPlaceholderText('email@example.com') as HTMLInputElement;
      const describedById = emailInput.getAttribute('aria-describedby');

      expect(describedById).toBeTruthy();
      expect(describedById).toContain(screen.getByText('Your primary email address').id);
    });

    it('includes both description and error in aria-describedby when error exists', async () => {
      render(<TestFormComponent />);

      const submitButton = screen.getByRole('button', { name: /submit/i });
      await user.click(submitButton);

      const emailInput = screen.getByPlaceholderText('email@example.com') as HTMLInputElement;
      const describedById = emailInput.getAttribute('aria-describedby');

      expect(describedById).toBeTruthy();

      // Should include description ID
      const descriptionElement = screen.getByText('Your primary email address');
      expect(describedById).toContain(descriptionElement.id);

      // Should include error message ID
      const errorElement = screen.getByText('Email is required');
      expect(describedById).toContain(errorElement.id);
    });

    it('only includes description in aria-describedby when no error exists', () => {
      render(<TestFormComponent />);

      const emailInput = screen.getByPlaceholderText('email@example.com') as HTMLInputElement;
      const describedById = emailInput.getAttribute('aria-describedby');

      expect(describedById).toBeTruthy();

      // Should have description
      const descriptionElement = screen.getByText('Your primary email address');
      expect(describedById).toContain(descriptionElement.id);

      // Should not have error (field is valid)
      expect(screen.queryByText('Email is required')).not.toBeInTheDocument();
    });

    it('includes description id in aria-describedby even without description text', () => {
      // The implementation includes the description id in aria-describedby by default
      // even if there's no actual description text rendered
      render(<TestFormComponent />);

      const nameInput = screen.getByPlaceholderText('John Doe') as HTMLInputElement;

      // Name field has no description text, but the ID is still included
      const describedById = nameInput.getAttribute('aria-describedby');

      // The aria-describedby should still include the description ID
      // to maintain a consistent structure
      expect(describedById).toBeTruthy();
      expect(describedById).toContain('form-item-description');
    });

    it('sets aria-invalid="true" when field has validation error', async () => {
      render(<TestFormComponent />);

      const submitButton = screen.getByRole('button', { name: /submit/i });
      await user.click(submitButton);

      const emailInput = screen.getByPlaceholderText('email@example.com') as HTMLInputElement;
      expect(emailInput).toHaveAttribute('aria-invalid', 'true');
    });

    it('sets aria-invalid="false" when field is valid', () => {
      render(<TestFormComponent />);

      const emailInput = screen.getByPlaceholderText('email@example.com') as HTMLInputElement;
      expect(emailInput).toHaveAttribute('aria-invalid', 'false');
    });

    it('updates aria-invalid state when validation state changes', async () => {
      render(<TestFormComponent />);

      const emailInput = screen.getByPlaceholderText('email@example.com') as HTMLInputElement;

      // Initially valid
      expect(emailInput).toHaveAttribute('aria-invalid', 'false');

      // Submit to trigger validation error
      const submitButton = screen.getByRole('button', { name: /submit/i });
      await user.click(submitButton);

      // Now invalid
      expect(emailInput).toHaveAttribute('aria-invalid', 'true');

      // Type valid email
      await user.clear(emailInput);
      await user.type(emailInput, 'test@example.com');

      // Should return to valid state
      expect(emailInput).toHaveAttribute('aria-invalid', 'false');
    });
  });

  // ============================================
  // Form workflow tests
  // ============================================

  describe('form workflow', () => {
    it('provides complete accessible form experience', async () => {
      render(<TestFormComponent />);

      // All labels should be properly associated
      expect(screen.getByLabelText('Email')).toBeInTheDocument();
      expect(screen.getByLabelText('Full Name')).toBeInTheDocument();

      // Descriptions should be present
      expect(screen.getByText('Your primary email address')).toBeInTheDocument();

      // Initially, no error messages
      expect(screen.queryByText('Email is required')).not.toBeInTheDocument();
      expect(screen.queryByText('Name is required')).not.toBeInTheDocument();

      // Fill in form
      const emailInput = screen.getByPlaceholderText('email@example.com');
      const nameInput = screen.getByPlaceholderText('John Doe');

      await user.type(emailInput, 'john@example.com');
      await user.type(nameInput, 'John Doe');

      // Verify inputs have values
      expect(emailInput).toHaveValue('john@example.com');
      expect(nameInput).toHaveValue('John Doe');
    });
  });
});
