import { Spectator, createComponentFactory } from '@ngneat/spectator/jest';
import { CheckboxInputControlComponent } from './checkbox-input-control.component';
import { FilterService } from '../../core';
import { CheckboxFilterInputOption, CheckboxFilterInput } from '../../core';
import { InputType } from '../../types';
import { signal } from '@angular/core';
import { CheckboxComponent } from '@isa/ui/input-controls';
import { MockComponent } from 'ng-mocks';
import { NgIcon } from '@ng-icons/core';

describe('CheckboxInputControlComponent', () => {
  let spectator: Spectator<CheckboxInputControlComponent>;
  let filterService: FilterService;

  const option: CheckboxFilterInputOption = {
    path: ['group', 'key', 'opt'],
    key: 'opt',
    label: 'Option',
    value: 'opt',
  };

  const parentOption: CheckboxFilterInputOption = {
    path: ['group', 'key', 'parent'],
    key: 'parent',
    label: 'Parent',
    values: [
      {
        path: ['group', 'key', 'parent', 'child1'],
        key: 'child1',
        label: 'Child1',
        value: 'child1',
      },
      {
        path: ['group', 'key', 'parent', 'child2'],
        key: 'child2',
        label: 'Child2',
        value: 'child2',
      },
    ],
  };

  const mockInputsSignal = signal<CheckboxFilterInput[]>([
    {
      group: 'group',
      key: 'key',
      type: InputType.Checkbox,
      options: [option, parentOption],
      selected: [],
      label: 'label',
      target: 'input',
    },
  ]);

  const mockFilterService = {
    inputs: mockInputsSignal,
    setInputCheckboxOptionSelected: jest.fn(),
  };

  const createComponent = createComponentFactory({
    component: CheckboxInputControlComponent,
    declarations: [MockComponent(CheckboxComponent), MockComponent(NgIcon)],
    providers: [{ provide: FilterService, useValue: mockFilterService }],
    detectChanges: false,
  });

  beforeEach(() => {
    spectator = createComponent({ props: { option } });
    filterService = spectator.inject(FilterService);
    jest.clearAllMocks();
  });

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

  it('should call setInputCheckboxOptionSelected on setSelected', () => {
    spectator.detectChanges();
    spectator.component.setSelected(true);
    expect(filterService.setInputCheckboxOptionSelected).toHaveBeenCalledWith(
      option,
      true,
    );

    spectator.component.setSelected(false);
    expect(filterService.setInputCheckboxOptionSelected).toHaveBeenCalledWith(
      option,
      false,
    );
  });

  describe('checkbox state computation', () => {
    it('should compute selected and indeterminate states for unchecked option', () => {
      spectator.detectChanges();
      expect(spectator.component.selected()).toBe(false);
      expect(spectator.component.indeterminate()).toBe(false);
      expect(spectator.component.checkboxState()).toBe('none');
    });

    it('should compute selected state when option is selected', () => {
      mockInputsSignal.set([
        {
          group: 'group',
          key: 'key',
          type: InputType.Checkbox,
          options: [option],
          selected: ['opt'],
          label: 'label',
          target: 'input',
        },
      ]);
      spectator.detectChanges();

      expect(spectator.component.selected()).toBe(true);
      expect(spectator.component.indeterminate()).toBe(false);
      expect(spectator.component.checkboxState()).toBe('checked');
    });

    it('should compute indeterminate state for parent with partial selection', () => {
      spectator.setInput('option', parentOption);
      mockInputsSignal.set([
        {
          group: 'group',
          key: 'key',
          type: InputType.Checkbox,
          options: [parentOption],
          selected: ['parent.child1'],
          label: 'label',
          target: 'input',
        },
      ]);
      spectator.detectChanges();

      expect(spectator.component.selected()).toBe(false);
      expect(spectator.component.indeterminate()).toBe(true);
      expect(spectator.component.checkboxState()).toBe('indeterminate');
    });
  });

  describe('expand/collapse functionality', () => {
    it('should handle expand/collapse state', () => {
      spectator.detectChanges();
      expect(spectator.component.expanded()).toBe(false);

      spectator.component.expanded.set(true);
      expect(spectator.component.expanded()).toBe(true);

      spectator.component.expanded.set(false);
      expect(spectator.component.expanded()).toBe(false);
    });

    it('should detect if option is parent', () => {
      spectator.detectChanges();
      expect(spectator.component.isParent()).toBe(false);

      spectator.setInput('option', parentOption);
      spectator.detectChanges();
      expect(spectator.component.isParent()).toBe(true);
    });

    it('should update icon name based on expanded state', () => {
      spectator.setInput('option', parentOption);
      spectator.detectChanges();

      expect(spectator.component.expandedIconName()).toBe(
        'isaActionChevronDown',
      );

      spectator.component.expanded.set(true);
      expect(spectator.component.expandedIconName()).toBe('isaActionChevronUp');
    });

    it('should handle parent with empty values array', () => {
      const emptyParent: CheckboxFilterInputOption = {
        path: ['group', 'key', 'empty'],
        key: 'empty',
        label: 'Empty Parent',
        values: [],
      };

      spectator.setInput('option', emptyParent);
      spectator.detectChanges();
      expect(spectator.component.isParent()).toBe(false);
    });

    it('should handle option without values property', () => {
      const optionWithoutValues: CheckboxFilterInputOption = {
        path: ['group', 'key', 'simple'],
        key: 'simple',
        label: 'Simple Option',
        value: 'simple',
      };

      spectator.setInput('option', optionWithoutValues);
      spectator.detectChanges();
      expect(spectator.component.isParent()).toBe(false);
    });
  });

  describe('data attributes for e2e testing', () => {
    it('should have correct data attributes on checkbox input', () => {
      spectator.detectChanges();
      const checkboxInput = spectator.query('input[type="checkbox"]');
      expect(checkboxInput).toHaveAttribute('aria-label', 'Option');
    });

    it('should have data attributes for expand button when option is parent', () => {
      spectator.setInput('option', parentOption);
      spectator.detectChanges();
      const expandButton = spectator.query('button');
      expect(expandButton).toBeTruthy();
    });
  });
});
