import {
  createDirectiveFactory,
  SpectatorDirective,
} from '@ngneat/spectator/jest';
import { DateInputDirective } from './date-input.directive';
import { FormsModule } from '@angular/forms';

describe('DateInputDirective', () => {
  let spectator: SpectatorDirective<DateInputDirective>;
  const createDirective = createDirectiveFactory({
    directive: DateInputDirective,
    imports: [FormsModule],
  });

  it('should render formatted date on writeValue', () => {
    // Arrange
    spectator = createDirective(`<input uiDateInput />`);
    const testDate = new Date(2020, 0, 1); // January 1, 2020

    // Act
    spectator.directive.writeValue(testDate);
    spectator.detectChanges();

    // Assert
    expect((spectator.element as HTMLInputElement).value).toBe('01.01.2020');
  });

  it('should render an empty string when value is undefined', () => {
    // Arrange
    spectator = createDirective(`<input uiDateInput />`);

    // Act
    spectator.directive.writeValue(undefined);
    spectator.detectChanges();

    // Assert
    expect((spectator.element as HTMLInputElement).value).toBe('');
  });

  it('should parse valid input and update value', () => {
    // Arrange
    spectator = createDirective(`<input uiDateInput />`);
    const inputEl = spectator.element as HTMLInputElement;
    inputEl.value = '15.08.2021';

    // Act
    inputEl.dispatchEvent(new Event('input'));
    const parsedDate = spectator.directive.value;

    // Assert
    expect(parsedDate).toBeDefined();
    expect(parsedDate?.getFullYear()).toBe(2021);
    expect(parsedDate?.getMonth()).toBe(7); // Month is zero-indexed: August => 7
    expect(parsedDate?.getDate()).toBe(15);
  });

  it('should not update value on invalid input', () => {
    // Arrange
    spectator = createDirective(`<input uiDateInput />`);
    const inputEl = spectator.element as HTMLInputElement;
    inputEl.value = 'invalid input';

    // Act
    inputEl.dispatchEvent(new Event('input'));

    // Assert
    expect(spectator.directive.value).toBeUndefined();
  });

  it('should invoke onChange and onTouched when setValue is called', () => {
    // Arrange
    spectator = createDirective(`<input uiDateInput />`);
    const mockOnChange = jest.fn();
    const mockOnTouched = jest.fn();
    spectator.directive.registerOnChange(mockOnChange);
    spectator.directive.registerOnTouched(mockOnTouched);
    const newDate = new Date(2022, 5, 10); // June 10, 2022

    // Act
    spectator.directive.setValue(newDate);
    spectator.detectChanges();

    // Assert
    expect(mockOnChange).toHaveBeenCalledWith(newDate);
    expect(mockOnTouched).toHaveBeenCalled();
    expect((spectator.element as HTMLInputElement).value).toBe('10.06.2022');
  });

  it('should update the input view on focusout', () => {
    // Arrange
    spectator = createDirective(`<input uiDateInput />`);
    const testDate = new Date(2021, 11, 25); // December 25, 2021
    spectator.directive.writeValue(testDate);
    spectator.detectChanges();

    // Act
    spectator.element.dispatchEvent(new Event('focusout'));
    spectator.detectChanges();

    // Assert
    expect((spectator.element as HTMLInputElement).value).toBe('25.12.2021');
  });

  it('should update the input view when enter key is pressed', () => {
    // Arrange
    spectator = createDirective(`<input uiDateInput />`);
    const testDate = new Date(2021, 10, 5); // November 5, 2021
    spectator.directive.writeValue(testDate);
    spectator.detectChanges();

    // Act: simulate keydown.enter event
    spectator.element.dispatchEvent(
      new KeyboardEvent('keydown', { key: 'Enter' }),
    );
    spectator.detectChanges();

    // Assert
    expect((spectator.element as HTMLInputElement).value).toBe('05.11.2021');
  });
});
