import { Injectable } from '@angular/core';
import { AppSettingsService } from '@shared/services';
import { StationCodeValidationService } from '@shared/services/validators/station-code.service';
import { TimeRangeValidationService } from '@shared/services/validators/time-range.service';
import {
    getEndOfTheWeek,
    getStartOfTheWeek,
    isBetween,
    isSameOrBefore,
} from '@utils/date';
import { IScheduleRouteDirectionParams } from './url/types';

export type IScheduleRouteParamsValidationResult = {
    isValid: boolean;
    areStationsValid: boolean;
    areDatesValid: boolean;
    isTimeRangeValid: boolean;
};
@Injectable()
export class ScheduleRouteParamsValidationService {
    constructor(
        private timeRangeValidator: TimeRangeValidationService,
        private stationValidator: StationCodeValidationService,
        private settings: AppSettingsService,
    ) {}

    async validateRoute(
        params: IScheduleRouteDirectionParams,
        returnParams: Partial<IScheduleRouteDirectionParams> = {},
    ): Promise<IScheduleRouteParamsValidationResult> {
        const areDatesValid = await this.areDatesValid(
            params.dateFrom,
            params.dateTo,
            returnParams.dateFrom,
            returnParams.dateTo,
        );

        const stations = [
            params.departure,
            params.arrival,
            returnParams.departure,
            returnParams.arrival,
        ].filter(Boolean);

        const areStationsValid = await this.areStationsValid(stations);

        const isTimeRangeValid = this.timeRangeValidator.isTimeRangeValid(
            params.timeFrom,
            params.timeTo,
        );

        const isReturnTimeRangeValid = this.timeRangeValidator.isTimeRangeValid(
            returnParams.timeFrom,
            returnParams.timeTo,
        );

        return {
            isValid:
                areDatesValid &&
                areStationsValid &&
                isTimeRangeValid &&
                isReturnTimeRangeValid,
            areDatesValid,
            areStationsValid,
            isTimeRangeValid: isTimeRangeValid && isReturnTimeRangeValid,
        };
    }

    private async areDatesValid(
        dateFrom: Date,
        dateTo: Date,
        returnDateFrom?: Date,
        returnDateTo?: Date,
    ): Promise<boolean> {
        const { scheduleMinDate, scheduleMaxDate } =
            await this.settings.getSettings();

        const minDate = getStartOfTheWeek(scheduleMinDate);
        const maxDate = getEndOfTheWeek(scheduleMaxDate);

        const maxDateTo = returnDateTo || maxDate;

        // dateFrom must be less or equal than scheduleMinDate
        // and more or equal than dateTo
        if (!isBetween(dateFrom, minDate, dateTo)) {
            return false;
        }

        // dateTo must be less or equal than scheduleMaxDate
        // or returnDateTo.
        if (!isSameOrBefore(dateTo, maxDateTo)) {
            return false;
        }

        // It is valid if there is no return dates
        if (!returnDateFrom && !returnDateTo) {
            return true;
        }

        // But it isn't valid if there is no one of return dates
        if (!returnDateFrom || !returnDateTo) {
            return false;
        }

        // returnDateFrom must be less or equal than returnDateTo
        // and more or equal than dateFrom
        if (
            !isBetween(
                returnDateFrom,
                getStartOfTheWeek(dateFrom),
                returnDateTo,
            )
        ) {
            return false;
        }

        return isSameOrBefore(returnDateTo, maxDate);
    }

    private areStationsValid(stations: string[]): Promise<boolean> {
        const promises = stations.map((stationCode) => {
            return this.stationValidator.isStationCodeValid(stationCode);
        });

        return Promise.all(promises).then((res) => {
            return res.every(Boolean);
        });
    }
}
