import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { ChecklistComponent } from './checklist.component';
import { ChecklistValueDirective } from './checklist-value.directive';
import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms';
import { CheckboxComponent } from './checkbox.component';
import { MockComponent } from 'ng-mocks';
import { NgIconComponent } from '@ng-icons/core';

describe('ChecklistComponent', () => {
  let spectator: SpectatorHost<
    ChecklistComponent,
    { options: unknown[]; formControl: FormControl }
  >;
  const createHost = createHostFactory({
    component: ChecklistComponent,
    imports: [FormsModule, ReactiveFormsModule, ChecklistValueDirective],
    declarations: [
      MockComponent(CheckboxComponent),
      MockComponent(NgIconComponent),
    ],
    template: `
      <ui-checklist [formControl]="formControl">
        @for(option of options; track option) {
          <label class="ui-checkbox-label">
            <ui-checkbox>
              <input type="checkbox" [uiChecklistValue]="option">
            </ui-checkbox>
            {{ option }}
          </label>
        }
      </ui-checklist>
    `,
  });

  beforeEach(() => {
    spectator = createHost(undefined, {
      hostProps: {
        options: ['apple', 'orange', 'banana'],
        formControl: new FormControl([]),
      },
    });
  });

  it('should create', () => {
    expect(spectator.component).toBeTruthy();
  });

  it('should initialize with no selected values', () => {
    expect(spectator.component.value()).toEqual([]);
  });

  it('should update value when checkbox is checked', () => {
    // Arrange
    const checkbox = spectator.queryAll(
      'input[type="checkbox"]',
    )[0] as HTMLInputElement;

    // Act
    checkbox.checked = true;
    spectator.dispatchFakeEvent(checkbox, 'change');

    // Assert
    expect(spectator.component.value()).toContain('apple');
    expect(spectator.hostComponent.formControl.value).toContain('apple');
  });

  it('should sync checkbox states with form control values', () => {
    // Arrange
    const initialValues = ['orange', 'banana'];

    // Act
    spectator.hostComponent.formControl.setValue(initialValues);
    spectator.detectChanges();

    // Assert
    const checkboxes = spectator.queryAll(
      'input[type="checkbox"]',
    ) as HTMLInputElement[];
    expect(checkboxes[0].checked).toBe(false); // apple
    expect(checkboxes[1].checked).toBe(true); // orange
    expect(checkboxes[2].checked).toBe(true); // banana
  });

  it('should handle a non-array value being passed to writeValue', () => {
    // Arrange & Act
    spectator.component.writeValue(null as any);

    // Assert
    expect(spectator.component.value()).toEqual([]);
  });

  it('should call onChange callback when value changes', () => {
    // Arrange
    const onChangeSpy = jest.fn();
    spectator.component.registerOnChange(onChangeSpy);
    const checkbox = spectator.queryAll(
      'input[type="checkbox"]',
    )[0] as HTMLInputElement;

    // Act
    checkbox.checked = true;
    spectator.dispatchFakeEvent(checkbox, 'change');

    // Assert
    expect(onChangeSpy).toHaveBeenCalledWith(['apple']);
  });

  it('should call onTouched callback when value changes', () => {
    // Arrange
    const onTouchedSpy = jest.fn();
    spectator.component.registerOnTouched(onTouchedSpy);
    const checkbox = spectator.queryAll(
      'input[type="checkbox"]',
    )[0] as HTMLInputElement;

    // Act
    checkbox.checked = true;
    spectator.dispatchFakeEvent(checkbox, 'change');

    // Assert
    expect(onTouchedSpy).toHaveBeenCalled();
  });
});
