import { TestBed } from '@angular/core/testing';
import { ScheduleApiService } from '@schedule/services/api.service';
import { ScheduleSearchDataSourceService } from '@schedule/services/data-source/search-data-source.service';
import { ScheduleDateValidationService } from '@schedule/services/date-validation.service';
import { IScheduleRouteDirectionParams } from '@schedule/services/url/types';
import ScheduleLegacyFactories from '@shared/factories/schedule.factory';
import { ScheduleItemModel } from '@shared/models-legacy';
import { getFlightMock } from '@utils/test/get-flight.mock';

describe('ScheduleSearchDataSourceService', () => {
    let service: ScheduleSearchDataSourceService;
    let createScheduleFromRoutesMock;

    const getFlightsMock = jasmine.createSpy('getFlights');
    const getMinValidDateMock = jasmine.createSpy('getMinValidDate');
    const getMaxValidDateMock = jasmine.createSpy('getMaxValidDate');

    const outboundFlights = [
        getFlightMock('GDX', 'MOW', '1'),
        getFlightMock('GDX', 'MQF', '2'),
        getFlightMock('IGT', 'MQF', '3'),
    ];

    const inboundFlights = [
        getFlightMock('MAD', 'MIA', '4'),
        getFlightMock('MIA', 'AGP', '5'),
        getFlightMock('AGP', 'MLE', '6'),
    ];

    const outboundSchedule = [
        new ScheduleItemModel({
            date: new Date(2022, 4, 9),
            flights: outboundFlights as any,
        }),
    ];

    const inboundSchedule = [
        new ScheduleItemModel({
            date: new Date(2022, 4, 16),
            flights: inboundFlights as any,
        }),
    ];

    const defaultOutboundRouteParams: IScheduleRouteDirectionParams = {
        dateFrom: new Date(2022, 4, 9),
        dateTo: new Date(2022, 4, 15),
        departure: 'MOW',
        arrival: 'MRS',
    };

    const defaultOutboundFailureRouteParams: IScheduleRouteDirectionParams = {
        dateFrom: new Date(2022, 3, 9),
        dateTo: new Date(2022, 3, 15),
        departure: 'KUF',
        arrival: 'LED',
    };

    const defaultInboundRouteParams: IScheduleRouteDirectionParams = {
        dateFrom: new Date(2022, 4, 16),
        dateTo: new Date(2022, 4, 22),
        departure: 'MRS',
        arrival: 'MOW',
    };

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                ScheduleSearchDataSourceService,
                {
                    provide: ScheduleApiService,
                    useValue: {
                        getFlights: getFlightsMock,
                    },
                },
                {
                    provide: ScheduleDateValidationService,
                    useValue: {
                        getMinValidDate: getMinValidDateMock,
                        getMaxValidDate: getMaxValidDateMock,
                    },
                },
            ],
        });

        service = TestBed.inject(ScheduleSearchDataSourceService);

        getFlightsMock.calls.reset();
        getMinValidDateMock.calls.reset();
        getMaxValidDateMock.calls.reset();

        getMinValidDateMock
            .withArgs(defaultOutboundRouteParams.dateFrom)
            .and.returnValue(
                Promise.resolve(defaultOutboundRouteParams.dateFrom),
            );
        getMinValidDateMock
            .withArgs(defaultOutboundFailureRouteParams.dateFrom)
            .and.returnValue(
                Promise.resolve(defaultOutboundFailureRouteParams.dateFrom),
            );
        getMinValidDateMock
            .withArgs(defaultInboundRouteParams.dateFrom)
            .and.returnValue(
                Promise.resolve(defaultInboundRouteParams.dateFrom),
            );

        getMaxValidDateMock
            .withArgs(defaultOutboundRouteParams.dateTo)
            .and.returnValue(
                Promise.resolve(defaultOutboundRouteParams.dateTo),
            );
        getMaxValidDateMock
            .withArgs(defaultOutboundFailureRouteParams.dateTo)
            .and.returnValue(
                Promise.resolve(defaultOutboundFailureRouteParams.dateTo),
            );
        getMaxValidDateMock
            .withArgs(defaultInboundRouteParams.dateTo)
            .and.returnValue(Promise.resolve(defaultInboundRouteParams.dateTo));

        getFlightsMock
            .withArgs(defaultOutboundRouteParams, undefined)
            .and.returnValue(Promise.resolve(outboundFlights))
            .withArgs(defaultOutboundRouteParams, 1)
            .and.returnValue(Promise.resolve(outboundFlights))
            .withArgs(defaultInboundRouteParams, 2)
            .and.returnValue(Promise.resolve(inboundFlights));

        createScheduleFromRoutesMock = spyOn(
            ScheduleLegacyFactories,
            'createScheduleFromRoutes',
        );
        createScheduleFromRoutesMock
            .withArgs(
                outboundFlights,
                defaultOutboundRouteParams.dateFrom,
                defaultOutboundRouteParams.dateTo,
            )
            .and.returnValue(outboundSchedule);
        createScheduleFromRoutesMock
            .withArgs(
                inboundFlights,
                defaultInboundRouteParams.dateFrom,
                defaultInboundRouteParams.dateTo,
            )
            .and.returnValue(inboundSchedule);
    });

    it('should set schedule after calling loadFlightsByRoute', async () => {
        expect(service.loading).toBe(false);
        expect(service.scheduleLegacy).toEqual([]);

        service.loadFlightsByRoute(defaultOutboundRouteParams);
        expect(service.loading).toBe(true);

        await Promise.resolve();
        await Promise.resolve();
        await Promise.resolve();
        await Promise.resolve();
        await Promise.resolve();
        await Promise.resolve();

        expect(service.scheduleLegacy).toEqual(outboundSchedule);
        expect(service.returnScheduleLegacy).toEqual([]);
        expect(service.loading).toBe(false);
    });

    it('should set schedule and return schedule after calling loadFlightsByRoute', async () => {
        expect(service.scheduleLegacy).toEqual([]);
        expect(service.returnScheduleLegacy).toEqual([]);

        await service.loadFlightsByRoute(
            defaultOutboundRouteParams,
            defaultInboundRouteParams,
        );

        expect(service.scheduleLegacy).toEqual(outboundSchedule);
        expect(service.returnScheduleLegacy).toEqual(inboundSchedule);
        expect(service.loading).toBe(false);
    });

    it('should reset schedules if error occurred', async () => {
        getFlightsMock
            .withArgs(defaultOutboundFailureRouteParams, 1)
            .and.returnValue(Promise.reject());

        await service.loadFlightsByRoute(
            defaultOutboundRouteParams,
            defaultInboundRouteParams,
        );

        expect(service.scheduleLegacy).toEqual(outboundSchedule);
        expect(service.returnScheduleLegacy).toEqual(inboundSchedule);

        await service.loadFlightsByRoute(
            defaultOutboundFailureRouteParams,
            defaultInboundRouteParams,
        );

        expect(service.scheduleLegacy).toEqual([]);
        expect(service.returnScheduleLegacy).toEqual([]);
        expect(service.loading).toBe(false);
    });

    it('should stop loading', () => {
        expect(service.loading).toBe(false);

        service.loadFlightsByRoute(defaultOutboundRouteParams);
        expect(service.loading).toBe(true);

        service.stopLoading();
        expect(service.loading).toBe(false);
    });
});
