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

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

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

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

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

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

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

    const input = container.querySelector('input') as HTMLInputElement;

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

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

    expect(spy).toHaveBeenCalledTimes(1);
    const callArg = spy.mock.calls[0][0];
    expect(callArg.target.value).toBe(mockValueUpdated);
  });

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

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

    const input = container.querySelector('input') as HTMLInputElement;
    expect(input.autocomplete).toBe(mockProps.autoComplete);
    // React's autoFocus focuses the element programmatically, not via the attribute
    expect(document.activeElement).toBe(input);
    expect(input.disabled).toBe(mockProps.disabled);
    expect(input.id).toBe(mockProps.id);
    expect(input.name).toBe(mockProps.name);
    expect(input.placeholder).toBe(mockProps.placeholder);
    expect(input.type).toBe(mockProps.type);
  });

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

    const input = container.querySelector('input') as HTMLInputElement;

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

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

    fireEvent.focus(input);
    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(
      <SingleLineText name={'test-name'} error={mockErrorMessage} />,
    );

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

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

  it('show a dummy value as the input value, when type is password', () => {
    const dummyPwd = '0000000000';
    const { container } = render(
      <SingleLineText name={'test-pwd'} type="password" isSet={true} />,
    );

    const input = container.querySelector('input') as HTMLInputElement;

    expect(input.value).toBe(dummyPwd);
  });

  it('when user changes the value it should be displayed, when type is password', () => {
    const initialVal = undefined;
    const mockValueUpdated = 'New Password';
    const changeSpy = vi.fn();

    const { container } = render(
      <SingleLineText
        name={'test-pwd'}
        type="password"
        value={initialVal}
        onChange={changeSpy}
      />,
    );

    const input = container.querySelector('input') as HTMLInputElement;
    fireEvent.change(input, { target: { value: mockValueUpdated } });

    expect(changeSpy).toHaveBeenCalledTimes(1);
    const callArg = changeSpy.mock.calls[0][0];
    expect(callArg.target.value).toBe(mockValueUpdated);
  });
});
