import { TestBed } from '@angular/core/testing';
import { CanActivateOnlineBoardDetails } from '@online-board/guards/can-activate-details';
import { CanActivateDateParams } from '@online-board/guards/utils/can-activate-date-params';
import { CanActivateRouteParams } from '@online-board/guards/utils/can-activate-route-params';
import { OnlineBoardNavigationService } from '@online-board/services/navigation.service';
import { OnlineBoardRequestParserService } from '@online-board/services/request/request-parser.service';
import { OnlineBoardUrlParserService } from '@online-board/services/url';
import { IParsedFlightId } from '@typings/flight/flight-id';

describe('CanActivateOnlineBoardDetails', () => {
    let service: CanActivateOnlineBoardDetails;
    const canActivateDateParamsMock = jasmine.createSpy(
        'canActivateDateParams',
    );
    const validateDateParamsMock = jasmine.createSpy('validateDateParams');
    const validateRouteParamsMock = jasmine.createSpy('validateRouteParams');
    const parseMock = jasmine.createSpy('parse');
    const toDetailsPageMock = jasmine.createSpy('toDetailsPage');
    const parseFlightIdMock = jasmine.createSpy('parseFlightId');
    const activatedRouteParams = {
        params: 'SU0001-20220528',
    };
    const activatedRouteQueryParams = {
        request: 'request string',
    };

    const activatedRoute = {} as any;
    const parsedFlightId: IParsedFlightId = {
        flightNumber: '0001',
        carrier: 'SU',
        date: new Date(2022, 4, 28),
    };

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                CanActivateOnlineBoardDetails,
                {
                    provide: OnlineBoardUrlParserService,
                    useValue: {
                        parseFlightId: parseFlightIdMock,
                    },
                },
                {
                    provide: CanActivateDateParams,
                    useValue: {
                        canActivate: canActivateDateParamsMock,
                        validateDateParams: validateDateParamsMock,
                    },
                },
                {
                    provide: CanActivateRouteParams,
                    useValue: {
                        validateRouteParams: validateRouteParamsMock,
                    },
                },
                {
                    provide: OnlineBoardRequestParserService,
                    useValue: {
                        parse: parseMock,
                    },
                },
                {
                    provide: OnlineBoardNavigationService,
                    useValue: {
                        toDetailsPage: toDetailsPageMock,
                    },
                },
            ],
        });

        service = TestBed.inject(CanActivateOnlineBoardDetails);
        canActivateDateParamsMock.calls.reset();
        validateRouteParamsMock.calls.reset();
        parseMock.calls.reset();
        toDetailsPageMock.calls.reset();

        activatedRoute.params = activatedRouteParams;
        activatedRoute.queryParams = activatedRouteQueryParams;

        parseFlightIdMock
            .withArgs(activatedRoute.params.params)
            .and.returnValue(parsedFlightId);
    });

    it('should return date validation result because it is not true', async () => {
        const mockedResult = {};
        canActivateDateParamsMock
            .withArgs(parsedFlightId)
            .and.returnValue(Promise.resolve(mockedResult));

        const result = await service.canActivate(activatedRoute);
        expect(result).toBe(mockedResult as any);
    });

    it('should return true because route params are valid and there is no request', async () => {
        activatedRoute.queryParams = {
            request: '',
        };
        canActivateDateParamsMock
            .withArgs(parsedFlightId)
            .and.returnValue(Promise.resolve(true));

        const result = await service.canActivate(activatedRoute);
        expect(result).toBe(true);
    });

    it('should return true if route params and request are valid', async () => {
        const mockedParsingResult = {
            type: 'flight-number',
            params: {},
        } as any;

        canActivateDateParamsMock
            .withArgs(parsedFlightId)
            .and.returnValue(Promise.resolve(true));
        parseMock
            .withArgs(activatedRoute.queryParams.request)
            .and.returnValue(mockedParsingResult);
        validateDateParamsMock
            .withArgs(mockedParsingResult.params)
            .and.returnValue(Promise.resolve(true));

        const result = await service.canActivate(activatedRoute);
        expect(result).toBe(true);
    });

    it('should call toDetailsPage because flight number params are invalid', async () => {
        const mockedParsingResult = {
            type: 'flight-number',
            params: {},
        } as any;

        canActivateDateParamsMock
            .withArgs(parsedFlightId)
            .and.returnValue(Promise.resolve(true));
        parseMock
            .withArgs(activatedRoute.queryParams.request)
            .and.returnValue(mockedParsingResult);
        validateDateParamsMock
            .withArgs(mockedParsingResult.params)
            .and.returnValue(Promise.resolve({}));

        const result = await service.canActivate(activatedRoute);
        expect(result).not.toBe(true);
        expect(toDetailsPageMock).toHaveBeenCalledWith({
            flightId: {
                flightNumber: '0001',
                carrier: 'SU',
                date: '',
            },
            date: new Date(2022, 4, 28),
        });
    });

    it('should call toDetailsPage because route params are invalid', async () => {
        const mockedParsingResult = {
            type: 'route',
            params: {},
        } as any;

        canActivateDateParamsMock
            .withArgs(parsedFlightId)
            .and.returnValue(Promise.resolve(true));
        parseMock
            .withArgs(activatedRoute.queryParams.request)
            .and.returnValue(mockedParsingResult);
        validateRouteParamsMock
            .withArgs(mockedParsingResult.params)
            .and.returnValue(Promise.resolve({}));

        const result = await service.canActivate(activatedRoute);
        expect(result).not.toBe(true);
        expect(toDetailsPageMock).toHaveBeenCalledWith({
            flightId: {
                flightNumber: '0001',
                carrier: 'SU',
                date: '',
            },
            date: new Date(2022, 4, 28),
        });
    });
});
