import { TestBed } from '@angular/core/testing';
import { DictionariesService } from '@modules/components/page-filters/services/dictionaries-service';
import { getDictionariesServiceMock } from '@modules/components/page-filters/services/dictionaries-service.mock';
import { CanActivateDateParams } from '@online-board/guards/utils/can-activate-date-params';
import { CanActivateRouteParams } from '@online-board/guards/utils/can-activate-route-params';
import { CanActivateStations } from '@online-board/guards/utils/can-activate-stations';
import { OnlineBoardDateValidationService } from '@online-board/services/date-validation.service';
import { OnlineBoardNavigationService } from '@online-board/services/navigation.service';
import { IOnlineBoardRoutePageUrlParams } from '@online-board/services/url/types';
import { OnlineBoardUrlParserService } from '@online-board/services/url/url-parser.service';
import { AppSettingsService } from '@shared/services';
import { StationCodeValidationService } from '@shared/services/validators/station-code.service';
import { TimeRangeValidationService } from '@shared/services/validators/time-range.service';

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

    const boardMinDate = new Date(2022, 4, 23);
    const boardMaxDate = new Date(2022, 4, 29);
    const appSettingsService: Partial<AppSettingsService> = {
        getSettings: () => {
            return Promise.resolve({
                boardMinDate,
                boardMaxDate,
            }) as any;
        },
    };

    const toNotFoundMock = jasmine.createSpy('toNotFound');
    const toStartPageMock = jasmine.createSpy('toStartPage');
    const navigationService: Partial<OnlineBoardNavigationService> = {
        toNotFound: toNotFoundMock,
        toStartPage: toStartPageMock,
    };

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                CanActivateRouteParams,
                CanActivateStations,
                CanActivateDateParams,
                StationCodeValidationService,
                OnlineBoardDateValidationService,
                TimeRangeValidationService,
                OnlineBoardUrlParserService,
                {
                    provide: AppSettingsService,
                    useValue: appSettingsService,
                },
                {
                    provide: OnlineBoardNavigationService,
                    useValue: navigationService,
                },
                {
                    provide: DictionariesService,
                    useValue: getDictionariesServiceMock(),
                },
            ],
        });

        toStartPageMock.calls.reset();
        toNotFoundMock.calls.reset();
        service = TestBed.inject(CanActivateRouteParams);
    });

    it('should return true for valid arrival params without time range', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            arrival: 'MOW',
            date: new Date(2022, 4, 25),
        };
        const activationResult = await service.canActivate(params);
        expect(activationResult).toBe(true);

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).toBe(true);
    });

    it('should return true for valid arrival params with time range', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            arrival: 'MOW',
            date: new Date(2022, 4, 25),
            timeFrom: '0600',
            timeTo: '2200',
        };

        const activationResult = await service.canActivate(params);
        expect(activationResult).toBe(true);

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).toBe(true);
    });

    it('should return true for valid departure params without time range', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            departure: 'MRS',
            date: new Date(2022, 4, 25),
        };

        const activationResult = await service.canActivate(params);
        expect(activationResult).toBe(true);

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).toBe(true);
    });

    it('should return true for valid departure params with time range', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            departure: 'MRS',
            date: new Date(2022, 4, 25),
            timeFrom: '0600',
            timeTo: '2200',
        };

        const activationResult = await service.canActivate(params);
        expect(activationResult).toBe(true);

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).toBe(true);
    });

    it('should return true for valid route params without time range', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            departure: 'MRS',
            arrival: 'MOW',
            date: new Date(2022, 4, 25),
        };

        const activationResult = await service.canActivate(params);
        expect(activationResult).toBe(true);

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).toBe(true);
    });

    it('should return true for valid route params with time range', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            departure: 'MRS',
            arrival: 'MOW',
            date: new Date(2022, 4, 25),
            timeFrom: '0600',
            timeTo: '2200',
        };

        const activationResult = await service.canActivate(params);
        expect(activationResult).toBe(true);

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).toBe(true);
    });

    it('should not return true because date is invalid', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            departure: 'MRS',
            arrival: 'MOW',
            date: new Date(2022, 4, 21),
            timeFrom: '0600',
            timeTo: '2200',
        };

        const activationResult = await service.canActivate(params);
        expect(activationResult).not.toBe(true);
        expect(toStartPageMock).toHaveBeenCalled();

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).not.toBe(true);
    });

    it('should not return true because time range is invalid', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            departure: 'MRS',
            arrival: 'MOW',
            date: new Date(2022, 4, 25),
            timeFrom: '0600',
            timeTo: '0300',
        };

        const activationResult = await service.canActivate(params);
        expect(activationResult).not.toBe(true);
        expect(toStartPageMock).toHaveBeenCalled();

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).not.toBe(true);
    });

    it('should not return true because departure is invalid', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            departure: 'ABA',
            arrival: 'MOW',
            date: new Date(2022, 4, 25),
            timeFrom: '0600',
            timeTo: '0300',
        };

        const activationResult = await service.canActivate(params);
        expect(activationResult).not.toBe(true);
        expect(toNotFoundMock).toHaveBeenCalled();

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).not.toBe(true);
    });

    it('should not return true because arrival is invalid', async () => {
        const params: IOnlineBoardRoutePageUrlParams = {
            departure: 'MRS',
            arrival: 'ABA',
            date: new Date(2022, 4, 25),
            timeFrom: '0600',
            timeTo: '0300',
        };

        const result = await service.canActivate(params);
        expect(result).not.toBe(true);
        expect(toNotFoundMock).toHaveBeenCalled();

        const validationResult = await service.validateRouteParams(params);
        expect(validationResult).not.toBe(true);
    });
});
