import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { TextArea } from './TextArea';

describe('TextArea', () => {
  it('renders the component without crashing', () => {
    const { container } = render(<TextArea name={'test-name'} />);

    expect(container).toBeTruthy();
  });

  it('displays a label', () => {
    const mockLabel = 'mockLabel';
    const { container } = render(
      <TextArea name={'test-name'} label={mockLabel} />,
    );

    const label = container.querySelector('label');

    expect(label?.textContent).toBe(mockLabel);
  });

  it('sets textarea field using the value prop and emits updated values', () => {
    const spy = vi.fn();
    const mockValue = 'test-value';
    const mockValueUpdated = 'updated-test-value';
    const { container, rerender } = render(
      <TextArea name={'test-name'} value={mockValue} onChange={spy} />,
    );

    const textarea = container.querySelector('textarea') as HTMLTextAreaElement;

    expect(textarea.value).toEqual(mockValue);

    fireEvent.change(textarea, { target: { value: mockValueUpdated } });

    expect(spy).toHaveBeenCalledTimes(1);

    // For controlled components, rerender with the new value to see the change
    rerender(
      <TextArea name={'test-name'} value={mockValueUpdated} onChange={spy} />,
    );

    expect(textarea.value).toBe(mockValueUpdated);
  });

  it('uses optional props when passed in', () => {
    const mockProps = {
      autoFocus: true,
      className: '',
      disabled: false,
      id: 'test-id',
      name: 'test-name',
      onBlur: () => null,
      onFocus: () => null,
      placeholder: 'test-placeholder',
      value: '',
    } as Record<string, unknown>;

    const { container } = render(
      <TextArea name={'test-name'} {...mockProps} />,
    );

    const textarea = container.querySelector('textarea') as HTMLTextAreaElement;
    // Note: autofocus is not reliably testable in jsdom
    expect(textarea.disabled).toBe(mockProps.disabled);
    expect(textarea.id).toBe(mockProps.id);
    expect(textarea.name).toBe(mockProps.name);
    expect(textarea.placeholder).toBe(mockProps.placeholder);
    expect(textarea.value).toBe(mockProps.value);
  });

  it('raises change, blur, and focus events', () => {
    const changeSpy = vi.fn();
    const blurSpy = vi.fn();
    const focusSpy = vi.fn();
    const mockValue = 'test textarea';
    const { container } = render(
      <TextArea
        name={'test-name'}
        onChange={changeSpy}
        onBlur={blurSpy}
        onFocus={focusSpy}
      />,
    );

    const textarea = container.querySelector('textarea') as HTMLTextAreaElement;

    fireEvent.change(textarea, { target: { value: mockValue } });
    expect(changeSpy).toHaveBeenCalledTimes(1);

    fireEvent.blur(textarea);
    expect(blurSpy).toHaveBeenCalledTimes(1);

    fireEvent.focus(textarea);
    expect(focusSpy).toHaveBeenCalledTimes(1);
  });

  it('applies error styling and renders error message when an error is passed', () => {
    const mockErrorMessage = 'test-error-message';
    const { container } = render(
      <TextArea name={'test-name'} error={mockErrorMessage} />,
    );

    const errorMsg = container.querySelector('small');
    const errorStyling = container.querySelector('textarea');

    expect(errorMsg?.textContent).toBe(mockErrorMessage);
    expect(errorStyling?.classList.contains('hasError')).toBe(true);
  });
});
