import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { Component, Input } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';

import { MockStore, provideMockStore } from '@ngrx/store/testing';

import { CriteriaComponent } from './criteria.component';
import { Attribute, CriteriaFamily, Dataset, OutputCategory, OutputFamily } from '../../../metamodel/models';
import { ConeSearch, Criterion, Resolver, SearchQueryParams, SvomKeyword } from '../../store/models';
import { SortByCriteriaDisplayPipe } from '../pipes/sort-by-criteria-display.pipe';
import * as searchActions from '../../store/actions/search.actions';
import { AbstractSearchComponent } from './abstract-search.component';
import * as coneSearchActions from '../../store/actions/cone-search.actions';

describe('[Instance][Search][Container] CriteriaComponent', () => {
    @Component({ selector: 'app-spinner', template: '' })
    class SpinnerStubComponent { }

    @Component({ selector: 'app-cone-search-tab', template: '' })
    class ConeSearchStubComponent {
        @Input() datasetSelected: string;
        @Input() datasetList: Dataset[];
        @Input() coneSearch: ConeSearch;
        @Input() resolver: Resolver;
        @Input() resolverIsLoading: boolean;
        @Input() resolverIsLoaded: boolean;
    }

    @Component({ selector: 'app-criteria-tabs', template: '' })
    class CriteriaTabsStubComponent {
        @Input() attributeList: Attribute[];
        @Input() criteriaFamilyList: CriteriaFamily[];
        @Input() criteriaList: Criterion[];
        @Input() svomKeywords: SvomKeyword[];
    }

    @Component({ selector: 'app-summary', template: '' })
    class SummaryStubComponent {
        @Input() currentStep: string;
        @Input() datasetSelected: string;
        @Input() datasetList: Dataset[];
        @Input() attributeList: Attribute[];
        @Input() criteriaFamilyList: CriteriaFamily[];
        @Input() outputFamilyList: OutputFamily[];
        @Input() outputCategoryList: OutputCategory[];
        @Input() criteriaList: Criterion[];
        @Input() outputList: number[];
        @Input() queryParams: SearchQueryParams;
        @Input() coneSearch: ConeSearch;
    }

    let component: CriteriaComponent;
    let fixture: ComponentFixture<CriteriaComponent>;
    let store: MockStore;

    beforeEach(waitForAsync(() => {
        TestBed.configureTestingModule({
            imports: [RouterTestingModule],
            declarations: [
                CriteriaComponent,
                SpinnerStubComponent,
                ConeSearchStubComponent,
                CriteriaTabsStubComponent,
                SummaryStubComponent,
                SortByCriteriaDisplayPipe
            ],
            providers: [provideMockStore({ })]
        });
        fixture = TestBed.createComponent(CriteriaComponent);
        component = fixture.componentInstance;
        store = TestBed.inject(MockStore);
    }));

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

    it('should execute ngOnInit lifecycle', (done) => {
        const spy = jest.spyOn(store, 'dispatch');
        jest.spyOn(AbstractSearchComponent.prototype, 'ngOnInit').mockReturnThis();
        component.ngOnInit();
        Promise.resolve(null).then(function() {
            expect(spy).toHaveBeenCalledTimes(2);
            expect(spy).toHaveBeenCalledWith(searchActions.changeStep({ step: 'criteria' }));
            expect(spy).toHaveBeenCalledWith(searchActions.checkCriteria());
            done();
        });
    });

    it('#addCriterion() should dispatch addCriterion action', () => {
        const criterion: Criterion = { id: 1, type: 'field' };
        const spy = jest.spyOn(store, 'dispatch');
        component.addCriterion(criterion);
        expect(spy).toHaveBeenCalledTimes(1);
        expect(spy).toHaveBeenCalledWith(searchActions.addCriterion({ criterion }));
    });

    it('#deleteCriterion() should dispatch deleteCriterion action', () => {
        const spy = jest.spyOn(store, 'dispatch');
        component.deleteCriterion(1);
        expect(spy).toHaveBeenCalledTimes(1);
        expect(spy).toHaveBeenCalledWith(searchActions.deleteCriterion({ idCriterion: 1 }));
    });

    it('#addConeSearch() should dispatch addConeSearch action', () => {
        const coneSearch: ConeSearch = { ra: 1, dec: 2, radius: 3 };
        const spy = jest.spyOn(store, 'dispatch');
        component.addConeSearch(coneSearch);
        expect(spy).toHaveBeenCalledTimes(1);
        expect(spy).toHaveBeenCalledWith(coneSearchActions.addConeSearch({ coneSearch }));
    });

    it('#deleteConeSearch() should dispatch deleteConeSearch action', () => {
        const spy = jest.spyOn(store, 'dispatch');
        component.deleteConeSearch();
        expect(spy).toHaveBeenCalledTimes(1);
        expect(spy).toHaveBeenCalledWith(coneSearchActions.deleteConeSearch());
    });

    it('#retrieveCoordinates() should dispatch deleteConeSearch action', () => {
        const spy = jest.spyOn(store, 'dispatch');
        component.retrieveCoordinates('myObject');
        expect(spy).toHaveBeenCalledTimes(1);
        expect(spy).toHaveBeenCalledWith(coneSearchActions.retrieveCoordinates({ name: 'myObject' }));
    });
});