/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/tslint/config */
import { NO_ERRORS_SCHEMA, Component, ViewChild } from '@angular/core';
import { CheckboxListComponent } from './checkbox-list.component';
import { ComponentFixture, TestBed } from '@angular/core/testing';

@Component({
    template: `
        <ngx-checkbox-list [classList]="classList"></ngx-checkbox-list>
    `,
})
class HostComponent {
    @ViewChild(CheckboxListComponent, { static: true }) component: CheckboxListComponent;
    classList: string[];
}

describe('CheckboxListComponent:', () => {
    let component: CheckboxListComponent;
    let fixture: ComponentFixture<CheckboxListComponent>;
    let onChange: jasmine.Spy;
    const value = () => (component as any).value; // eslint-disable-line unicorn/consistent-function-scoping

    beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [HostComponent, CheckboxListComponent],
            schemas: [NO_ERRORS_SCHEMA],
        });
    });

    describe('component behaviour:', () => {
        let hostFixture: ComponentFixture<HostComponent>;
        let hostComponent: HostComponent;

        beforeEach(() => {
            hostFixture = TestBed.createComponent(HostComponent);
            hostComponent = hostFixture.componentInstance;
            component = hostComponent.component;
        });

        it('input class list', () => {
            hostComponent.classList = ['checkbox-list', 'foo'];
            hostFixture.detectChanges();
            const rootElement = (hostFixture.debugElement.nativeElement as HTMLElement).querySelector('.checkbox-list');

            expect(rootElement && rootElement.classList.contains('foo')).toBe(true);
        });
    });

    describe('createComponent:', () => {
        beforeEach(() => {
            fixture = TestBed.createComponent(CheckboxListComponent);
            component = fixture.componentInstance;
            onChange = jasmine.createSpy('onChange');
            component.registerOnChange(onChange);
            component.options = [{ label: '', value: null }, { label: 'opt1', value: 1 }, { label: 'opt2', value: 2 }];
        });

        it('smoke', () => {
            expect(component).toBeTruthy();
            fixture.elementRef.nativeElement.blur();
        });

        it('writeValue fault tollerant case null', () => {
            component.writeValue(null);

            expect(value()).toEqual(null);
        });

        it('writeValue fault tollerant case undefined', () => {
            component.writeValue(undefined);

            expect(value()).toEqual(null);
        });

        it('writeValue fault tollerant case number', () => {
            component.writeValue(42);

            expect(value()).toEqual([]);
        });

        it('mark checkbox', () => {
            const input = { value: 42, checked: true };
            const event = { target: input };
            component.change(event as any);

            expect(onChange).toHaveBeenCalledWith(['42']);
        });

        it('unmark checkbox', () => {
            component.writeValue([123, 42]);
            const input = { value: 42, checked: false };
            const event = { target: input };
            component.change(event as any);

            expect(onChange).toHaveBeenCalledWith(['123']);
        });

        describe('objects:', () => {
            beforeEach(() => {
                component.objectId = 'id';
                component.options = [
                    { label: 'Forty Two', value: 42 },
                    { label: '123', value: 123 },
                    { label: '1', value: 1 },
                    { label: '2', value: 2 },
                ];
            });

            it('objects mark checkbox', () => {
                const input = { value: 42, checked: true };
                const event = { target: input };
                component.change(event as any);

                expect(onChange).toHaveBeenCalledWith([{ id: 42 }]);
            });

            it('objects unmark checkbox', () => {
                component.writeValue([{ id: 123 }, { id: 42 }]);
                const input = { value: 42, checked: false };
                const event = { target: input };
                component.change(event as any);

                expect(onChange).toHaveBeenCalledWith([{ id: 123 }]);
            });

            it('objects should be uniq', () => {
                component.writeValue([{ id: 1 }, { id: 2 }, { id: 1 }]);
                const input = { value: 42, checked: false };
                const event = { target: input };
                component.change(event as any);

                expect(onChange).toHaveBeenCalledWith([{ id: 1 }, { id: 2 }]);
            });

            it('objects checked object id true for incoming string but values are ints', () => {
                component.writeValue([{ id: 1 }, { id: 2 }]);

                expect(component.checked(2)).toBe(true);
                expect(component.checked('2')).toBe(true);
            });

            describe('with object key:', () => {
                beforeEach(() => {
                    component.objectKey = 'name';
                });

                it('with object key mark checkbox', () => {
                    const input = { value: 42, checked: true };
                    const event = { target: input };
                    component.change(event as any);

                    expect(onChange).toHaveBeenCalledWith([{ id: 42, name: 'Forty Two' }]);
                });
            });
        });

        it('should be uniq for primitives', () => {
            component.writeValue([1, 2, 2]);
            const input = { value: 42, checked: false };
            const event = { target: input };
            component.change((event as unknown) as Event);

            expect(onChange).toHaveBeenCalledWith(['1', '2']);
        });

        it('checked method should return true for incoming string but values are ints', () => {
            component.writeValue([1, 2]);
            const input = { value: 2, checked: true };
            const event = { target: input };
            component.change((event as unknown) as Event);

            expect(component.checked(2)).toBe(true);
            expect(component.checked('2')).toBe(true);
        });

        it('check checked method', () => {
            expect(component.checked(1)).toBe(false);
        });
    });
});
