import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';
import { OnlineBoardSearchPageBaseComponent } from '@online-board/pages/search/components/base/search-page-base.component';
import { OnlineBoardDataSourceService } from '@online-board/services/data-source.service';
import { OnlineBoardNavigationService } from '@online-board/services/navigation.service';
import { OnlineBoardRequestBuilderService } from '@online-board/services/request/request-builder.service';
import { IOnlineBoardRouteData } from '@online-board/types';
import { IUrlTimeRange } from '@typings/common/url';
import { IFlightId } from '@typings/flight/flight-id';
import FlightDateUtils from '@utils/flight/date';
import { BehaviorSubject } from 'rxjs';

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

    const data: IOnlineBoardRouteData = {};

    const loadFlightsByRouteMock = jasmine.createSpy('loadFlightsByRoute');
    const stopLoadingMock = jasmine.createSpy('stopLoading');

    const toStartPageMock = jasmine.createSpy('toStartPage');
    const toRoutePageMock = jasmine.createSpy('toRoutePage');
    const toDetailsPageMock = jasmine.createSpy('toDetailsPage');

    const buildRouteRequestMock = jasmine.createSpy('buildRouteRequest');

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [OnlineBoardSearchPageBaseComponent],
            providers: [
                {
                    provide: ActivatedRoute,
                    useValue: {
                        data: new BehaviorSubject(data),
                    },
                },
                {
                    provide: OnlineBoardDataSourceService,
                    useValue: {
                        loadFlightsByRoute: loadFlightsByRouteMock,
                        stopLoading: stopLoadingMock,
                    },
                },
                {
                    provide: OnlineBoardNavigationService,
                    useValue: {
                        toStartPage: toStartPageMock,
                        toRoutePage: toRoutePageMock,
                        toDetailsPage: toDetailsPageMock,
                    },
                },
                {
                    provide: OnlineBoardRequestBuilderService,
                    useValue: {
                        buildRouteRequest: buildRouteRequestMock,
                    },
                },
            ],
            schemas: [NO_ERRORS_SCHEMA],
        }).compileComponents();
    });

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

        getFlightDateMock = spyOn(FlightDateUtils, 'getFlightDate');

        loadFlightsByRouteMock.calls.reset();
        stopLoadingMock.calls.reset();

        toRoutePageMock.calls.reset();
        toStartPageMock.calls.reset();
        toDetailsPageMock.calls.reset();

        buildRouteRequestMock.calls.reset();

        data.urlParams = {
            route: {
                date: new Date(2022, 4, 28),
                arrival: 'MRS',
                departure: 'MOW',
            },
        };
    });

    it('should not call dataSource.loadFlightsByRoute if no params were provided', () => {
        data.urlParams.route = undefined;

        component.ngOnInit();
        expect(component.params).toBe(undefined);
        expect(loadFlightsByRouteMock).not.toHaveBeenCalled();

        data.urlParams = undefined;

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

    it('should call dataSource.loadFlightsByRoute', () => {
        component.ngOnInit();
        expect(component.params).toBe(data.urlParams.route);
        expect(loadFlightsByRouteMock).toHaveBeenCalledWith(
            data.urlParams.route,
        );
    });

    it('should call dataSource.loadFlightsByRoute with params from urlParams.route', () => {
        component.ngOnInit();
        expect(component.params).toBe(data.urlParams.route);

        component.handleSameUrlNavigation();
        expect(loadFlightsByRouteMock.calls.count()).toBe(2);
        expect(loadFlightsByRouteMock.calls.mostRecent().args[0]).toEqual(
            data.urlParams.route,
        );
    });

    it('should call dataSource.stopLoading and navigate to start page', () => {
        component.handleRequestCancellation();

        expect(stopLoadingMock).toHaveBeenCalled();
        expect(toStartPageMock).toHaveBeenCalled();
    });

    it('should navigate to route page with saved params and provided date', () => {
        const date = new Date(2022, 5, 28);

        component.ngOnInit();
        component.handleDateChange(date);
        expect(toRoutePageMock).toHaveBeenCalledWith({
            ...data.urlParams.route,
            date,
        });
    });

    it('should navigate to route page with saved params and provided time range', () => {
        const timeRange: IUrlTimeRange = {
            timeFrom: '0000',
            timeTo: '1800',
        };

        component.ngOnInit();
        component.handleTimeRangeChange(timeRange);
        expect(toRoutePageMock).toHaveBeenCalledWith({
            ...data.urlParams.route,
            ...timeRange,
        });

        component.handleTimeRangeChange();
        expect(toRoutePageMock).toHaveBeenCalledWith({
            ...data.urlParams.route,
            timeFrom: undefined,
            timeTo: undefined,
        });
    });

    it('should navigate to details with flights info and request built from saved params', () => {
        const request = 'onlineboard-route-MOW-MRS-20220528';
        const dateStr = '2022-05-27T00:00:00';
        const date = new Date(dateStr);
        const flightId: IFlightId = {
            flightNumber: '0001',
            carrier: 'SU',
            date: dateStr,
        };

        getFlightDateMock.withArgs({ flightId }).and.returnValue(date);
        buildRouteRequestMock
            .withArgs(data.urlParams.route)
            .and.returnValue(request);

        component.ngOnInit();
        component.navigateToDetails({ flightId } as any);
        expect(toDetailsPageMock).toHaveBeenCalledWith({
            flightId,
            date,
            request,
        });
    });
});
