import { TestBed } from '@angular/core/testing';
import { ActivatedRouteSnapshot, Router } from '@angular/router';
import { CanActivateScheduleDetails } from '@schedule/guards/can-activate-schedule-details.service';
import { ScheduleDateValidationService } from '@schedule/services/date-validation.service';
import { ScheduleRequestParserService } from '@schedule/services/request/request-parser.service';
import { ScheduleRouteParamsValidationService } from '@schedule/services/route-params-validation.service';
import { FLIGHTS_INFO_PARAM } from '@schedule/services/url/flights-info-param';
import { SCHEDULE_URL_BASE } from '@schedule/services/url/url-base';
import { ScheduleUrlParserService } from '@schedule/services/url/url-parser.service';
import { AppSettingsService } from '@shared/services';
import { RootNavigationService } from '@shared/services/navigation.service';
import { StationCodeValidationService } from '@shared/services/validators/station-code.service';

describe('CanActivateDetails', () => {
    const scheduleMinDate = new Date(2022, 4, 1);
    const scheduleMaxDate = new Date(2022, 4, 31);
    const navigateByUrl = jasmine.createSpy('navigateByUrl');
    const routeUrl = [
        {
            path: 'SU1210-20220507',
        },
        {
            path: 'SU1765-20220507',
        },
    ];
    const expectedUrl = 'schedule/SU1210-20220507/SU1765-20220507';

    const stationCodeValidationService: Partial<StationCodeValidationService> =
        {
            isStationCodeValid: (code: string) => {
                const res = code === 'NBC' || code === 'KHV';

                return Promise.resolve(res);
            },
        };

    const defaultGetSettings = () => {
        return Promise.resolve({
            scheduleMinDate,
            scheduleMaxDate,
        });
    };
    const settingsServiceMock = {
        getSettings: defaultGetSettings,
    };

    let service: CanActivateScheduleDetails;

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                CanActivateScheduleDetails,
                ScheduleDateValidationService,
                RootNavigationService,
                ScheduleUrlParserService,
                ScheduleRequestParserService,
                ScheduleRouteParamsValidationService,
                {
                    provide: StationCodeValidationService,
                    useValue: stationCodeValidationService,
                },
                {
                    provide: SCHEDULE_URL_BASE,
                    useValue: 'schedule',
                },
                {
                    provide: Router,
                    useValue: {
                        navigateByUrl,
                    },
                },
                {
                    provide: AppSettingsService,
                    useValue: settingsServiceMock,
                },
                {
                    provide: FLIGHTS_INFO_PARAM,
                    useValue: 'flightsInfo',
                },
            ],
        });

        navigateByUrl.calls.reset();
        service = TestBed.inject(CanActivateScheduleDetails);
        settingsServiceMock.getSettings = defaultGetSettings;
    });

    it('should return true', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {},
            params: {
                flightsInfo: 'SU1210-20220507',
            },
        };

        const result = await service.canActivate(route as any);
        expect(result).toBe(true);
        expect(navigateByUrl).not.toHaveBeenCalled();
    });

    it('should return false because error occurred', async () => {
        settingsServiceMock.getSettings = () => {
            throw new Error();
        };

        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {},
            params: {
                flightsInfo: 'SU1210-20220507',
            },
        };

        const result = await service.canActivate(route as any);
        expect(result).toBe(undefined);
    });

    it('should return true for connecting flights', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {},
            params: {
                flightsInfo: 'SU1210-20220507/SU1765-20220507',
            },
        };

        const result = await service.canActivate(route as any);
        expect(result).toBe(true);
        expect(navigateByUrl).not.toHaveBeenCalled();
    });

    it('should redirect to home if one of dates is invalid', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {},
            params: {
                flightsInfo: 'SU1210-20220507/SU1765-20220407',
            },
        };

        const result = await service.canActivate(route as any);
        expect(result).not.toBe(true);
        expect(navigateByUrl).toHaveBeenCalledWith('schedule');
    });

    it('should return true with valid request', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {
                request: 'schedule-route-NBC-KHV-20220507-20220513-06002200',
            },
            params: {
                flightsInfo: 'SU1210-20220507/SU1765-20220507',
            },
        };

        const result = await service.canActivate(route as any);
        expect(result).toBe(true);
        expect(navigateByUrl).not.toHaveBeenCalled();
    });

    it('should return true with valid request with return route', async () => {
        const routes: Partial<ActivatedRouteSnapshot>[] = [
            {
                queryParams: {
                    request:
                        'schedule-route-NBC-KHV-20220507-20220513-06002200-KHV-NBC-20220507-20220513-06002200',
                },
                params: {
                    flightsInfo: 'SU1210-20220507/SU1765-20220507',
                },
            },
            {
                queryParams: {
                    request:
                        'schedule-route-NBC-KHV-20220507-20220513-06002200-KHV-NBC-20220507-20220513',
                },
                params: {
                    flightsInfo: 'SU1210-20220507/SU1765-20220507',
                },
            },
        ];

        const expectations = routes.map(async (route) => {
            const result = await service.canActivate(route as any);
            expect(result).toBe(true);
            expect(navigateByUrl).not.toHaveBeenCalled();
        });

        await Promise.all(expectations);
    });

    it('should discard request with invalid station', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {
                request: 'schedule-route-MOW-NBC-20220507-20220513-06002200',
            },
            params: {
                flightsInfo: 'SU1210-20220507/SU1765-20220507',
            },
            url: routeUrl as any,
        };

        const result = await service.canActivate(route as any);
        expect(result).not.toBe(true);
        expect(navigateByUrl).toHaveBeenCalledWith(expectedUrl);
    });

    it('should discard request with invalid date', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {
                request: 'schedule-route-KHV-NBC-20220107-20220513-06002200',
            },
            params: {
                flightsInfo: 'SU1210-20220507/SU1765-20220507',
            },
            url: routeUrl as any,
        };

        const result = await service.canActivate(route as any);
        expect(result).not.toBe(true);
        expect(navigateByUrl).toHaveBeenCalledWith(expectedUrl);
    });

    it('should discard request with invalid time range', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {
                request: 'schedule-route-KHV-NBC-20220507-20220513-12000200',
            },
            params: {
                flightsInfo: 'SU1210-20220507/SU1765-20220507',
            },
            url: routeUrl as any,
        };

        const result = await service.canActivate(route as any);
        expect(result).not.toBe(true);
        expect(navigateByUrl).toHaveBeenCalledWith(expectedUrl);
    });

    it('should discard request because its first date is less than the second', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {
                request: 'schedule-route-KHV-NBC-20220517-20220513-12000200',
            },
            params: {
                flightsInfo: 'SU1210-20220507/SU1765-20220507',
            },
            url: routeUrl as any,
        };

        const result = await service.canActivate(route as any);
        expect(result).not.toBe(true);
        expect(navigateByUrl).toHaveBeenCalledWith(expectedUrl);
    });

    it('should discard request because its first return date is less than its first date', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {
                request:
                    'schedule-route-NBC-KHV-20220507-20220513-KHV-NBC-20220501-20220518',
            },
            params: {
                flightsInfo: 'SU1210-20220507/SU1765-20220507',
            },
            url: routeUrl as any,
        };

        const result = await service.canActivate(route as any);
        expect(result).not.toBe(true);
        expect(navigateByUrl).toHaveBeenCalledWith(expectedUrl);
    });

    it('should discard request because its second return date is less than its second date', async () => {
        const route: Partial<ActivatedRouteSnapshot> = {
            queryParams: {
                request:
                    'schedule-route-NBC-KHV-20220507-20220529-KHV-NBC-20220509-20220518',
            },
            params: {
                flightsInfo: 'SU1210-20220507/SU1765-20220507',
            },
            url: routeUrl as any,
        };

        const result = await service.canActivate(route as any);
        expect(result).not.toBe(true);
        expect(navigateByUrl).toHaveBeenCalledWith(expectedUrl);
    });
});
