import { HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { EndpointService } from '@shared/services/api/endpoint.service';
import { ApiService } from '@shared/services/api/api.service';
import {
    IScheduleDetailsResponse,
    IScheduleResponse,
    IDaysResponse,
} from '@typings/responses';
import { IStringValues } from '@typings/common/util';
import { ApiFormatterService } from '@shared/services/api/formatter.service';

export type IScheduleRequestParams = {
    arrival: string;
    departure: string;
    dateFrom: Date;
    dateTo: Date;
    timeFrom?: string;
    timeTo?: string;
    connections?: number;
};

type IScheduleHttpParams = IStringValues<IScheduleRequestParams> & {
    attribute?: 1 | 2;
};
@Injectable()
export class ScheduleApiService {
    constructor(
        private apiService: ApiService,
        private apiFormatter: ApiFormatterService,
        private endpointService: EndpointService,
    ) {}

    /**
     * @param requestParams - params for request;
     * @param statisticAttribute - attribute to distinguish return request in
     * popular requests` statistic. If it is undefined, request will be counted
     * as outbound request. If it is 1 it will be ignored in statistic because
     * after this request will always be performed request with the value of 2.
     * Which means the request is for fetching inbound flights.
     * (1 - outbound, 2 - inbound).
     * */
    getFlights(
        requestParams: IScheduleRequestParams,
        statisticAttribute: 1 | 2 | undefined = undefined,
    ): Promise<IScheduleResponse> {
        const url = this.endpointService.buildLocalizedURL('schedule', '1');

        const dateTill = this.apiFormatter.formatDate(this.addDays(requestParams.dateTo, 1));
        this.addDays(requestParams.dateTo, -1);

        const httpParams: IScheduleHttpParams = {
            ...requestParams,
            dateFrom: this.apiFormatter.formatDate(requestParams.dateFrom),
            dateTo: dateTill,
            timeFrom: this.apiFormatter.formatTime(requestParams.timeFrom),
            timeTo: this.apiFormatter.formatTime(requestParams.timeTo),
            connections: String(requestParams.connections ?? 1),
        };

        if (statisticAttribute) {
            httpParams.attribute = statisticAttribute;
        }

        let params = new HttpParams({
            fromObject: httpParams,
        });

        if (!requestParams.timeFrom || !requestParams.timeTo) {
            params = params.delete('timeFrom').delete('timeTo');
        }

        return this.apiService.cacheFirst<IScheduleResponse>(url, params);
    }

    public getFlightDaysByRoute(date: Date, src: string, dest: string, connections: boolean): Promise<IDaysResponse> {
        const formatDate = this.apiFormatter.formatDate(date);

        const param = connections ? `connections/${src}-${dest}-1` : `route/${src}-${dest}`;

        const url = this.endpointService.buildLocalizedURL(
            `days/${formatDate}/382/${param}/schedule/`, // `days/${formatDate}/367/${param}/schedule/` расширено до 382, чтобы при нахождении на сайте нескольких дней актуальность календаря 
            'v1',                              // сохранялась по заранее запрошенным данным (не делать автообновление, возможно на будущую доработку по автообновлению)
        );
        console.log(url);
        const httpParams = new HttpParams({
        });

        return this.apiService
            .get<IDaysResponse>(url, { params: httpParams })
            .toPromise();
    }

    public getFlightDetails(
        flightsInfo: string[],
        dates: Date[],
        departure: string,
        arrival: string,
    ): Promise<IScheduleDetailsResponse> {
        const url = this.endpointService.buildLocalizedURL('schedule/details');
        const apiDates = dates.map(this.apiFormatter.formatDate);

        const params = new HttpParams({
            fromObject: {
                ...this.arrayToIndexedMapObject(apiDates, 'dates'),
                ...this.arrayToIndexedMapObject(flightsInfo, 'flights'),
                departure: departure,
                arrival: arrival,
            },
        });

        return this.apiService
            .get<IScheduleDetailsResponse>(url, { params })
            .toPromise();
    }

    private addDays(date: Date, days: number): Date {
        date.setDate(date.getDate() + days);
        return date;
    }

    private arrayToIndexedMapObject(
        array: string[],
        keyPrefix: string,
    ): object {
        return array.reduce((result, item, index) => {
            const key = `${keyPrefix}[${index}]`;
            result[key] = item;

            return result;
        }, {});
    }
}
