import { Component, Inject } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ScheduleFilterValidationService } from '@schedule/components/schedule-filter/services/schedule-filter-validation.service';
import {
    IScheduleRouteDirectionParams,
    IScheduleRouteParams,
} from '@schedule/services/url/types';
import { ScheduleUrlBuilderService } from '@schedule/services/url/url-builder.service';
import { ScheduleApiService } from '@schedule/services/api.service';
import { IScheduleRouteData } from '@schedule/types';
import { AppSettings } from '@shared/models-legacy';
import { APP_SETTINGS } from '@shared/services';
import { ScheduleFiltersStateService } from '@shared/services/filters/schedule-filters-state.service';
import { SearchHistoryService } from '@shared/services/history/search-history.service';
import { UserLocationService } from '@shared/services/user-location/user-location.service';
import { IUrlTimeRange } from '@typings/common/url';
import { isSameOrBefore } from '@utils/date';
import { getWeek } from '@utils/week';
import { StateService } from '@shared/services/state.service';

@Component({
    selector: 'schedule-filter',
    templateUrl: './schedule-filter.component.html',
    providers: [ScheduleFilterValidationService],
})
export class ScheduleFilterComponent {
    disabledDates: Date[];
    disabledDatesReturn: Date[];

    constructor(
        public validationService: ScheduleFilterValidationService,
        private apiService: ScheduleApiService,
        @Inject(APP_SETTINGS) public settings: AppSettings,
        private router: Router,
        private route: ActivatedRoute,
        private urlBuilder: ScheduleUrlBuilderService,
        private historyService: SearchHistoryService,
        private locationService: UserLocationService,
        private state: ScheduleFiltersStateService,
        private stateService: StateService,
    ) {}

    async ngOnInit() {
        var in_params = this.stateService.get("schedulein");
        var out_params = this.stateService.get("scheduleout");
        if (in_params) {
            this.setInboundParams(in_params);
        }
        if (out_params) {
            this.setOutboundParams(out_params);
        }
        if (in_params || out_params) {
            this.viewSchedule();
        }

        this.route.data.subscribe((data: IScheduleRouteData) => {
            if (data.urlParams?.route) {
                this.setState(data.urlParams.route);
            }
        });

        this.locationService.location.subscribe((location) => {
            // set default state only if user permitted geo position
            // search and filter isn't filled
            if (location && !this.departure) {
                this.setDefaultState(location);
            }
        });
    }

    get arrival() {
        return this.state.arrival;
    }

    set arrival(arrival: string) {
        this.state.arrival = arrival;
        this.updateCalendar();
    }

    get departure() {
        return this.state.departure;
    }

    set departure(departure: string) {
        this.state.departure = departure;
        this.updateCalendar();
    }

    get dateRange() {
        return this.state.dateRange;
    }

    set dateRange(dateRange: Date[]) {
        this.state.dateRange = dateRange;
    }

    get timeRange() {
        return this.state.timeRange;
    }

    set timeRange(timeRange: IUrlTimeRange) {
        this.state.timeRange = timeRange;
    }

    get returnDateRange() {
        return this.state.returnDateRange;
    }

    set returnDateRange(returnDateRange: Date[]) {
        this.state.returnDateRange = returnDateRange;
    }

    get returnTimeRange() {
        return this.state.returnTimeRange;
    }

    set returnTimeRange(returnTimeRange: IUrlTimeRange) {
        this.state.returnTimeRange = returnTimeRange;
    }

    get directOnly() {
        return this.state.directOnly;
    }

    set directOnly(directOnly: boolean) {
        this.state.directOnly = directOnly;
        this.updateCalendar();
    }

    get withReturn() {
        return this.state.withReturn;
    }

    set withReturn(withReturn: boolean) {
        this.state.withReturn = withReturn;
        if (withReturn)
            this.updateCalendarReturn();
    }

    async updateCalendar() {
        const isDepartureValid = await this.validationService.validateCode(this.departure);
        const isArrivalValid = await this.validationService.validateCode(this.arrival);

        if (isDepartureValid && isArrivalValid) {
            var date = new Date();
            date.setUTCHours(0,0,0,0);
            date.setDate(date.getDate() - 1);
            this.apiService
                .getFlightDaysByRoute(date, this.departure, this.arrival, !this.directOnly)
                .then((res) => {
                    this.disabledDates = new Array();
                    for(var i=0;i<res.days.length;i++) {
                        if (res.days[i] == '0') {
                            this.disabledDates.push(new Date(date));
                        }
                        date.setDate(date.getDate() + 1);
                    }
                })
                .finally(() => {
                });
            if (this.withReturn)
                this.updateCalendarReturn();
        } else {
            this.disabledDates = new Array();
        }
    }

    async updateCalendarReturn() {
        const isDepartureValid = await this.validationService.validateCode(this.departure);
        const isArrivalValid = await this.validationService.validateCode(this.arrival);

        if (isDepartureValid && isArrivalValid && this.withReturn) {
            var date = new Date();
            date.setUTCHours(0,0,0,0);
            date.setDate(date.getDate() - 1);
            this.apiService
                .getFlightDaysByRoute(date, this.arrival, this.departure, !this.directOnly)
                .then((res) => {
                    this.disabledDatesReturn = new Array();
                    for(var i=0;i<res.days.length;i++) {
                        if (res.days[i] == '0') {
                            this.disabledDatesReturn.push(new Date(date));
                        }
                        date.setDate(date.getDate() + 1);
                    }
                })
                .finally(() => {
                });
        } else {
            this.disabledDatesReturn = new Array();
        }
    }

    async viewSchedule() {
        const outboundParams = this.getOutboundParams();
        const inboundParams = this.getInboundParams();

        const areParamsValid = await this.validationService.validate({
            inbound: inboundParams,
            outbound: outboundParams,
        });

        if (!areParamsValid) {
            return;
        }

        // inlined navigateToRoutePage from ScheduleNavigationService
        // to get access to url.
        const url = this.urlBuilder.getRoutePageUrl(
            outboundParams,
            inboundParams,
        );

        this.router.navigateByUrl(url).then((navigated) => {
            if (navigated) {
                this.historyService.add({
                    type: 'schedule-route',
                    url,
                    params: {
                        inbound: inboundParams,
                        outbound: outboundParams,
                    },
                });
            }
        });
    }

    exchange() {
        [
            this.validationService.departureError,
            this.validationService.arrivalError,
        ] = [null, null];
        [this.departure, this.arrival] = [this.arrival, this.departure];
    }

    resetDateRangeError() {
        this.validationService.dateRangeError = undefined;
    }

    resetReturnDateRangeError() {
        this.validationService.returnDateRangeError = undefined;
    }

    resetReturnDateRange() {
        this.returnDateRange = [];
    }

    get maxScheduleDate() {
        const [, returnEndDate] = this.returnDateRange || [];

        if (!returnEndDate) {
            return this.settings.scheduleMaxDate;
        }

        return isSameOrBefore(returnEndDate, this.settings.scheduleMaxDate)
            ? returnEndDate
            : this.settings.scheduleMaxDate;
    }

    get minReturnScheduleDate() {
        const [scheduleStartDate] = this.dateRange || [];

        if (!scheduleStartDate) {
            return this.settings.scheduleMinDate;
        }

        return isSameOrBefore(scheduleStartDate, this.settings.scheduleMinDate)
            ? this.settings.scheduleMinDate
            : scheduleStartDate;
    }

    private setState(routeParams: IScheduleRouteParams) {
        this.setOutboundParams(routeParams.outbound);

        if (routeParams.inbound) {
            this.setInboundParams(routeParams.inbound);
        } else {
            this.withReturn = false;
        }
    }

    private setOutboundParams(params: IScheduleRouteDirectionParams) {
        this.departure = params.departure;
        this.arrival = params.arrival;
        this.dateRange = [params.dateFrom, params.dateTo];

        if (params.timeTo && params.timeFrom) {
            this.timeRange = {
                timeFrom: params.timeFrom,
                timeTo: params.timeTo,
            };
        }

        this.directOnly = params.connections === 0;
    }

    private setInboundParams(params: IScheduleRouteDirectionParams) {
        this.withReturn = true;
        this.returnDateRange = [params.dateFrom, params.dateTo];

        if (params.timeTo && params.timeFrom) {
            this.returnTimeRange = {
                timeFrom: params.timeFrom,
                timeTo: params.timeTo,
            };
        }
    }

    private setDefaultState(userLocation: string) {
        this.departure = userLocation;
        this.dateRange = getWeek();
    }

    private getInboundParams(): IScheduleRouteDirectionParams | undefined {
        return this.withReturn
            ? {
                  departure: this.arrival,
                  arrival: this.departure,
                  dateFrom: this.returnDateRange[0],
                  dateTo: this.returnDateRange[1],
                  connections: this.directOnly ? 0 : undefined,
                  ...this.returnTimeRange,
              }
            : undefined;
    }

    private getOutboundParams(): IScheduleRouteDirectionParams {
        return {
            departure: this.departure,
            arrival: this.arrival,
            dateFrom: this.dateRange[0],
            dateTo: this.dateRange[1],
            connections: this.directOnly ? 0 : undefined,
            ...this.timeRange,
        };
    }
}
