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

import { DatatableComponent } from './datatable.component';
import { Pagination, PaginationOrder, SearchQueryParams } from '../../../store/models';
import {
    DetailLinkRendererConfig,
    DownloadRendererConfig,
    ImageRendererConfig,
    LinkRendererConfig,
    RendererConfig
} from 'src/app/metamodel/models/renderers';
import { ATTRIBUTE_LIST, DATASET } from 'src/test-data';
import { Attribute } from 'src/app/metamodel/models';

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

    @Component({ selector: 'app-detail-renderer', template: '' })
    class DetailRendererStubComponent {
        @Input() value: string | number;
        @Input() datasetName: string;
        @Input() instanceSelected: string;
        @Input() queryParams: SearchQueryParams;
        @Input() config: DetailLinkRendererConfig;
    }

    @Component({ selector: 'app-link-renderer', template: '' })
    class LinkRendererStubComponent {
        @Input() value: string | number;
        @Input() datasetName: string;
        @Input() config: LinkRendererConfig;
    }

    @Component({ selector: 'app-download-renderer', template: '' })
    class DownloadRendererStubComponent {
        @Input() value: string;
        @Input() datasetName: string;
        @Input() datasetPublic: boolean;
        @Input() config: DownloadRendererConfig;
    }

    @Component({ selector: 'app-image-renderer', template: '' })
    class ImageRendererStubComponent {
        @Input() value: string | number;
        @Input() datasetName: string;
        @Input() config: ImageRendererConfig;
    }

    @Component({ selector: 'app-json-renderer', template: '' })
    class JsonRendererStubComponent {
        @Input() value: string | number;
        @Input() attributeLabel: string;
        @Input() config: RendererConfig;
    }

    @Component({ selector: 'pagination', template: '' })
    class PaginationStubComponent {
        @Input() totalItems: number;
        @Input() boundaryLinks: boolean;
        @Input() rotate: boolean;
        @Input() maxSize: number;
        @Input() itemsPerPage: number;
    }

    let component: DatatableComponent;
    let fixture: ComponentFixture<DatatableComponent>;

    beforeEach(waitForAsync(() => {
        TestBed.configureTestingModule({
            declarations: [
                DatatableComponent,
                SpinnerStubComponent,
                DetailRendererStubComponent,
                LinkRendererStubComponent,
                DownloadRendererStubComponent,
                ImageRendererStubComponent,
                JsonRendererStubComponent,
                PaginationStubComponent
            ],
            imports: [FormsModule]
        });
        fixture = TestBed.createComponent(DatatableComponent);
        component = fixture.componentInstance;
    }));

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

    it('#ngOnInit() should init sortedCol value and raise retrieveData event ', (done) => {
        component.dataset = DATASET;
        component.attributeList = ATTRIBUTE_LIST;
        const spy = jest.spyOn(component.retrieveData, 'emit');
        const expectedPagination: Pagination = {
            dname: 'myDataset',
            page: 1,
            nbItems: 10,
            sortedCol: 1,
            order: PaginationOrder.a
        }
        component.ngOnInit();
        Promise.resolve(null).then(function() {
            expect(component.sortedCol).toEqual(1);
            expect(spy).toHaveBeenCalledTimes(1);
            expect(spy).toHaveBeenCalledWith(expectedPagination);
            done();
        });
    });

    it('#getRendererConfig() should return attribute renderer configuration', () => {
        let attribute: Attribute = {
            id: 1,
            name: 'myAttribute',
            label: 'my label attribute',
            form_label: 'my form label attribute',
            description: null,
            primary_key: true,
            type: 'integer',
            search_type: 'field',
            operator: 'eq',
            dynamic_operator: false,
            min: 'one',
            max: null,
            placeholder_min: null,
            placeholder_max: null,
            criteria_display: 1,
            output_display: 1,
            selected: true,
            renderer: null,
            renderer_config: null,
            order_by: true,
            archive: false,
            detail_display: 1,
            options: null,
            vo_utype: null,
            vo_ucd: null,
            vo_unit: null,
            vo_description: null,
            vo_datatype: null,
            vo_size: null,
            id_criteria_family: 1,
            id_output_category: 1,
            id_detail_output_category: null
        };
        expect(component.getRendererConfig(attribute)).toBeNull();
        const detailLinkRendererConfig: DetailLinkRendererConfig = {
            id: 'renderer-config',
            display: 'display',
            component: 'detail'
        };
        attribute.renderer = 'detail-link';
        attribute.renderer_config = detailLinkRendererConfig;
        expect(component.getRendererConfig(attribute)).toEqual(detailLinkRendererConfig);
        const linkRendererConfig: LinkRendererConfig = {
            id: 'renderer-config',
            href: 'href',
            display: 'display',
            text: 'text',
            icon: 'icon',
            blank: true
        };
        attribute.renderer = 'link';
        attribute.renderer_config = linkRendererConfig;
        expect(component.getRendererConfig(attribute)).toEqual(linkRendererConfig);
        const downloadRendererConfig: DownloadRendererConfig = {
            id: 'renderer-config',
            display: 'display',
            text: 'text',
            icon: 'icon'
        };
        attribute.renderer = 'download';
        attribute.renderer_config = downloadRendererConfig;
        expect(component.getRendererConfig(attribute)).toEqual(downloadRendererConfig);
        const imageRendererConfig: ImageRendererConfig = {
            id: 'renderer-config',
            display: 'display',
            type: 'type',
            width: 'width',
            height: 'height'
        };
        attribute.renderer = 'image';
        attribute.renderer_config = imageRendererConfig;
        expect(component.getRendererConfig(attribute)).toEqual(imageRendererConfig);
        const jsonRendererConfig: RendererConfig = { id: 'renderer-config' };
        attribute.renderer = 'json';
        attribute.renderer_config = jsonRendererConfig;
        expect(component.getRendererConfig(attribute)).toEqual(jsonRendererConfig);
    });

    it('#getOutputList() should return filtered output list', () => {
        component.outputList = [2]
        component.attributeList = ATTRIBUTE_LIST;
        expect(component.getOutputList().length).toBe(1);
    });

    it('#toggleSelection(datum) should return added datum to selectedData', () => {
        const datum = { label_one: 123456 };
        component.attributeList = ATTRIBUTE_LIST;
        component.selectedData = [];
        component.addSelectedData.subscribe((event: any) => expect(event).toBe(123456));
        component.toggleSelection(datum);
    });

    it('#toggleSelection(datum) should return remove datum to selectedData', () => {
        const datum = { label_one: 123456 };
        component.selectedData = [123456];
        component.attributeList = ATTRIBUTE_LIST;
        component.deleteSelectedData.subscribe((event: any) => expect(event).toBe(123456));
        component.toggleSelection(datum);
    });

    it('#isSelected(datum) should return true datum is selected', () => {
        const datum = { label_one: 123456 };
        component.attributeList = ATTRIBUTE_LIST;
        component.selectedData = [123456];
        expect(component.isSelected(datum)).toBeTruthy();
    });

    it('#isSelected(datum) should return false datum is not selected', () => {
        const datum = { label_one: 123456 };
        component.attributeList = ATTRIBUTE_LIST;
        component.selectedData = [];
        expect(component.isSelected(datum)).toBeFalsy();
    });

    it('#changePage() should change page value and raise retrieveData event', () => {
        component.dataset = DATASET;
        component.sortedCol = 1;
        component.sortedOrder = PaginationOrder.a;
        const expectedPagination: Pagination = {
            dname: 'myDataset',
            page: 2,
            nbItems: 10,
            sortedCol: 1,
            order: PaginationOrder.a
        };
        const spy = jest.spyOn(component.retrieveData, 'emit');
        component.changePage(2);
        expect(spy).toHaveBeenCalledTimes(1);
        expect(spy).toHaveBeenCalledWith(expectedPagination);
    });

    it('#changeNbItems() should change nbItems value and raise retrieveData event', () => {
        component.dataset = DATASET;
        component.sortedCol = 1;
        component.sortedOrder = PaginationOrder.a;
        const expectedPagination: Pagination = {
            dname: 'myDataset',
            page: 1,
            nbItems: 20,
            sortedCol: 1,
            order: PaginationOrder.a
        };
        const spy = jest.spyOn(component.retrieveData, 'emit');
        const mockEvent: Event = <Event><any>{
            currentTarget: {
                value: 20
            }
        };
        component.changeNbItems(mockEvent);
        expect(spy).toHaveBeenCalledTimes(1);
        expect(spy).toHaveBeenCalledWith(expectedPagination);
    });

    it('#sort() should raise retrieveData event with correct parameters', () => {
        component.dataset = DATASET;
        component.sortedOrder = PaginationOrder.a;
        let expectedPagination: Pagination = {
            dname: 'myDataset',
            page: 1,
            nbItems: 10,
            sortedCol: 1,
            order: PaginationOrder.a
        };
        const spy = jest.spyOn(component.retrieveData, 'emit');
        component.sort(1);
        expect(spy).toHaveBeenCalledTimes(1);
        expect(spy).toHaveBeenCalledWith(expectedPagination);
        component.sortedCol = 1;
        component.sortedOrder = PaginationOrder.a;
        expectedPagination = {
            dname: 'myDataset',
            page: 1,
            nbItems: 10,
            sortedCol: 1,
            order: PaginationOrder.d
        };
        component.sort(1);
        expect(spy).toHaveBeenCalledTimes(2);
        expect(spy).toHaveBeenLastCalledWith(expectedPagination);
        component.sortedCol = 1;
        component.sortedOrder = PaginationOrder.d;
        expectedPagination = {
            dname: 'myDataset',
            page: 1,
            nbItems: 10,
            sortedCol: 1,
            order: PaginationOrder.a
        };
        component.sort(1);
        expect(spy).toHaveBeenCalledTimes(3);
        expect(spy).toHaveBeenLastCalledWith(expectedPagination);
    });
});