import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';
import { OnlineBoardFlightDetailsPageComponent } from '@online-board/pages/flight-details/flight-details-page.component';
import { RefreshService } from '@online-board/pages/flight-details/services/refresh.service';
import { OnlineBoardDataSourceService } from '@online-board/services/data-source.service';
import { OnlineBoardNavigationService } from '@online-board/services/navigation.service';
import { IOnlineBoardRouteData } from '@online-board/types';
import { IFlightId } from '@typings/flight/flight-id';
import { BehaviorSubject } from 'rxjs';

describe('OnlineBoardFlightDetailsPageComponent', () => {
    let fixture: ComponentFixture<OnlineBoardFlightDetailsPageComponent>;
    let component: OnlineBoardFlightDetailsPageComponent;

    const data: IOnlineBoardRouteData = {};
    const loadFlightByFlightNumberMock = jasmine.createSpy(
        'loadFlightByFlightNumber',
    );
    const loadFlightsByRouteMock = jasmine.createSpy('loadFlightsByRoute');
    const loadFlightsByFlightNumberMock = jasmine.createSpy(
        'loadFlightsByFlightNumber',
    );
    const refreshStartMock = jasmine.createSpy('start');
    const refreshStopMock = jasmine.createSpy('stop');
    const toDetailsPageMock = jasmine.createSpy('toDetailsPage');
    const toNotFoundMock = jasmine.createSpy('toNotFound');

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [OnlineBoardFlightDetailsPageComponent],
            providers: [
                {
                    provide: OnlineBoardDataSourceService,
                    useValue: {
                        loadFlightByFlightNumber: loadFlightByFlightNumberMock,
                        loadFlightsByRoute: loadFlightsByRouteMock,
                        loadFlightsByFlightNumber:
                            loadFlightsByFlightNumberMock,
                    },
                },
                {
                    provide: ActivatedRoute,
                    useValue: {
                        data: new BehaviorSubject(data),
                    },
                },
                {
                    provide: OnlineBoardNavigationService,
                    useValue: {
                        toDetailsPage: toDetailsPageMock,
                        toNotFound: toNotFoundMock,
                    },
                },
            ],
            schemas: [NO_ERRORS_SCHEMA],
        }).compileComponents();

        TestBed.overrideComponent(OnlineBoardFlightDetailsPageComponent, {
            set: {
                providers: [
                    {
                        provide: RefreshService,
                        useValue: {
                            start: refreshStartMock,
                            stop: refreshStopMock,
                        },
                    },
                ],
            },
        });
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(
            OnlineBoardFlightDetailsPageComponent,
        );
        component = fixture.componentInstance;

        loadFlightByFlightNumberMock.calls.reset();
        loadFlightsByFlightNumberMock.calls.reset();
        loadFlightsByRouteMock.calls.reset();

        refreshStartMock.calls.reset();
        refreshStopMock.calls.reset();

        toDetailsPageMock.calls.reset();
        toNotFoundMock.calls.reset();

        loadFlightByFlightNumberMock.and.returnValue(Promise.resolve());

        data.urlParams = {
            details: {
                flightNumber: '0001',
                carrier: 'SU',
                date: new Date(2022, 4, 28),
            },
        };
        data.requestData = {
            raw: 'onlineboard-arrival-MOW-20220528',
            parsed: {
                type: 'route',
                params: {
                    arrival: 'MOW',
                    date: new Date(2022, 4, 27),
                },
            },
        };
    });

    it('should not call loadFlightByFlightNumber and store params', () => {
        data.urlParams.details = undefined;
        component.ngOnInit();

        expect(component.params).toBe(undefined);
        expect(loadFlightByFlightNumberMock).not.toHaveBeenCalled();

        data.urlParams = undefined;
        component.ngOnInit();

        expect(component.params).toBe(undefined);
        expect(loadFlightByFlightNumberMock).not.toHaveBeenCalled();
    });

    it('should not call dataSource methods and store request', () => {
        data.urlParams.details = undefined;
        data.requestData = undefined;
        component.ngOnInit();

        expect(component.requestQueryParam).toBe(undefined);
        expect(loadFlightByFlightNumberMock).not.toHaveBeenCalled();
        expect(loadFlightsByRouteMock).not.toHaveBeenCalled();
    });

    it('should call loadFlightByFlightNumber and store params', () => {
        data.requestData = undefined;

        component.ngOnInit();
        expect(component.params).toEqual(data.urlParams.details);
        expect(loadFlightByFlightNumberMock).toHaveBeenCalledWith(
            data.urlParams.details,
        );
    });

    it('should start refresh service', async () => {
        loadFlightByFlightNumberMock.and.returnValue(
            Promise.resolve({
                data: {
                    routes: ['1'],
                },
            }),
        );
        data.requestData = undefined;

        component.ngOnInit();
        await Promise.resolve();
        await Promise.resolve();
        expect(refreshStartMock).toHaveBeenCalledWith(data.urlParams.details);
    });

    it('should navigate to not found if loadFlightByFlightNumber failed', async () => {
        data.requestData = undefined;
        loadFlightByFlightNumberMock.and.returnValue(Promise.reject());

        component.ngOnInit();
        await Promise.resolve();
        await Promise.resolve();

        expect(toNotFoundMock).toHaveBeenCalled();
    });

    it('should navigate to not found if api request resolved with empty result', async () => {
        loadFlightByFlightNumberMock.and.returnValue(
            Promise.resolve({
                data: {
                    routes: [],
                },
            }),
        );

        component.ngOnInit();
        await Promise.resolve();

        expect(refreshStartMock).not.toHaveBeenCalled();
        expect(toNotFoundMock).toHaveBeenCalled();
    });

    it('should stop refresh service', () => {
        component.ngOnDestroy();

        expect(refreshStopMock).toHaveBeenCalled();
    });

    it('should call loadFlightsByRoute and store request', () => {
        component.ngOnInit();

        expect(component.requestQueryParam).toBe(data.requestData.raw);
        expect(loadFlightsByRouteMock).toHaveBeenCalledWith(
            data.requestData.parsed.params,
        );
    });

    it('should call loadFlightsByFlightNumber and store request', () => {
        data.requestData = {
            raw: 'onlineboard-flight-SU0001-20220528',
            parsed: {
                type: 'flight-number',
                params: {
                    flightNumber: '0001',
                    carrier: 'SU',
                    date: new Date(2022, 4, 28),
                },
            },
        };
        component.ngOnInit();

        expect(component.requestQueryParam).toBe(data.requestData.raw);
        expect(loadFlightsByFlightNumberMock).toHaveBeenCalledWith(
            data.requestData.parsed.params,
        );
    });

    it('should return stored date', () => {
        component.ngOnInit();
        expect(component.searchDate).toEqual(data.urlParams.details.date);
    });

    it('should navigate to details with stored data and provided date', () => {
        const date = new Date(2022, 4, 29);
        const flightId: IFlightId = {
            date: '2022-05-28T00:00:00',
            flightNumber: '0001',
            carrier: 'SU',
        };
        (component.dataSource as any).flight = { flightId };
        component.ngOnInit();
        component.handleDateChange(date);

        expect(toDetailsPageMock).toHaveBeenCalledWith({
            flightId,
            date,
            request: data.requestData.raw,
        });
    });

    it('should navigate to details with stored data and provided flight', () => {
        const flightId: IFlightId = {
            date: '2022-05-25T00:00:00',
            flightNumber: '0001',
            carrier: 'SU',
        };
        const flight: any = { flightId };
        component.ngOnInit();
        component.handleOpenEvent(flight);

        expect(toDetailsPageMock).toHaveBeenCalledWith({
            flightId,
            date: new Date(2022, 4, 25),
            request: data.requestData.raw,
        });
    });
});
