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

// Mock dependencies
vi.mock('../../../hooks', () => ({
  useClickOutside: vi.fn(),
}));

vi.mock('react-popper', () => ({
  usePopper: vi.fn(() => ({
    styles: { popper: {} },
    attributes: { popper: {} },
  })),
}));

vi.mock('react-dom', async () => ({
  ...(await vi.importActual('react-dom')),
  createPortal: (node: React.ReactNode) => node,
}));

describe('DateTimeTextField', () => {
  Settings.defaultLocale = navigator.language;
  it('renders the component without crashing', () => {
    const { container } = render(<DateTimeText name={'test-name'} />);

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

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

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

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

  it(`Button component should have type of 'button'`, () => {
    const { container } = render(<DateTimeText name={'test-name'} />);

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

    expect(button?.type).toBe('button');
  });

  it('sets input field using the value prop and emits updated values', () => {
    const spy = vi.fn();
    const mockDate = new Date();
    mockDate.setSeconds(0);
    const mockValue = mockDate.toISOString();
    // mockDate.setHours(mockDate.getHours() < 23 ? mockDate.getHours() + 1 : 0);
    const { container } = render(
      <DateTimeText name={'test-name'} value={mockValue} onChange={spy} />,
    );

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

    expect(input.value).toEqual(
      DateTime.fromJSDate(mockDate).toLocaleString(DateTime.DATETIME_SHORT),
    );
  });

  it(`defaults to an empty string if prop value Wis 'null'`, () => {
    const { container } = render(
      // @ts-expect-error intentional null value
      <DateTimeText name={'test-name'} value={null} />,
    );

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

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

  it(`defaults to an empty string if prop value is 'undefined'`, () => {
    const { container } = render(
      <DateTimeText name={'test-name'} value={undefined} />,
    );

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

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

  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',
    } as Record<string, unknown>;

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

    const input = container.querySelector('input') as HTMLInputElement;
    expect(input.autocomplete).toBe('on');
    expect(input.disabled).toBe(false);
    expect(input.id).toBe('test-id');
    expect(input.name).toBe('test-name');
    expect(input.placeholder).toBe('test-placeholder');
  });

  it('applies error styling and renders error message when an error is passed', () => {
    const mockErrorMessage = 'test-error-message';
    const { container } = render(
      <DateTimeText 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('displays the date picker when the button is clicked', () => {
    const { container } = render(
      <DateTimeText name={'test-name'} value={''} />,
    );

    const button = container.querySelector('button') as HTMLButtonElement;

    fireEvent.click(button);

    const datePicker = container.querySelector('.picker');

    expect(datePicker).toBeTruthy();
  });

  it('fires onChange event with the ISO date when localized date is entered', () => {
    const spy = vi.fn();

    const { container } = render(
      <DateTimeText name={'test-name'} value={''} onChange={spy} />,
    );

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

    const time = DateTime.local().set({ second: 0, millisecond: 0 });

    fireEvent.blur(input, {
      target: {
        value: time.toLocaleString(DateTime.DATETIME_SHORT),
      },
    });

    expect(spy).toHaveBeenCalledTimes(1);
    expect(spy).toHaveBeenCalledWith(time.toISO(), true);
  });

  it('fires onChange event with entered text and isValidDate as false when invalid date is entered', () => {
    const spy = vi.fn();

    const { container } = render(
      <DateTimeText name={'test-name'} value={''} onChange={spy} />,
    );

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

    fireEvent.blur(input, {
      target: {
        value: 'test',
      },
    });

    expect(spy).toHaveBeenCalledTimes(1);
    expect(spy).toHaveBeenCalledWith('test', false);
  });

  it('handles date only input when modifyTime is set to false', () => {
    const spy = vi.fn();

    const { container } = render(
      <DateTimeText
        name={'test-name'}
        value={''}
        onChange={spy}
        modifyTime={false}
      />,
    );

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

    const time = DateTime.local().set({
      hour: 0,
      minute: 0,
      second: 0,
      millisecond: 0,
    });

    fireEvent.blur(input, {
      target: {
        value: time.toLocaleString(DateTime.DATE_SHORT),
      },
    });

    expect(spy).toHaveBeenCalledTimes(1);
    expect(spy).toHaveBeenCalledWith(time.toISO(), true);
  });
});
