import { Injectable } from '@angular/core';
import {
    AirportModel,
    CityModel,
} from '@modules/components/page-filters/models';
import { DictionariesService } from '@modules/components/page-filters/services/dictionaries-service';
import { Language } from '@shared/enumerators';
import { sortByName } from '@shared/helpers/sorterts';
import { LocalizationService } from '@shared/services';

type AutocompleteItem = CityModel | AirportModel;

@Injectable()
export class CitiesSearchService {
    private MAX_ITEMS_COUNT = 10;
    private CODE_LENGTH = 3;
    private lang: Language;

    constructor(
        private localizationService: LocalizationService,
        private dictService: DictionariesService,
    ) {
        this.lang = localizationService.Language;
    }

    private static addAirportsToCities(
        cities: CityModel[],
    ): AutocompleteItem[] {
        return cities.reduce((result, city) => {
            const airports = city.airports
                .filter((airport: AirportModel) => {
                    return airport.code !== city.code;
                })
                .sort(sortByName);

            return [...result, city, ...airports];
        }, [] as AutocompleteItem[]);
    }

    private static removeDuplicates(cities: CityModel[]): CityModel[] {
        const citiesMap = new Map(cities.map((city) => [city.name, city]));

        return Array.from(citiesMap, (mapItem) => mapItem[1]);
    }

    private findAirportsByName(airportName: string) {
        return this.dictService.airportsAll
            .filter((airport) => {
                return (
                    airport.name
                        .toLowerCase()
                        .indexOf(airportName.toLowerCase()) === 0
                );
            })
            .sort(sortByName);
    }

    private findCitiesStartedWith(cityNamePart: string) {
        return this.dictService.citiesAll
            .filter((city) => {
                return (
                    city.name
                        .toLowerCase()
                        .indexOf(cityNamePart.toLowerCase()) === 0
                );
            })
            .sort(sortByName);
    }

    private findCitiesThatIncludes(citiNamePart: string) {
        return this.dictService.citiesAll
            .filter((city) => {
                return city.name
                    .toLowerCase()
                    .includes(citiNamePart.toLowerCase());
            })
            .sort(sortByName);
    }

    private findCitiesByAirportName(airportName) {
        return this.findAirportsByName(airportName).map((airport) => {
            return this.dictService.getCityByCode(airport.city_code);
        });
    }

    private findAirportByCode(code: string): AirportModel | null {
        if (code.length !== this.CODE_LENGTH) {
            return null;
        }

        return this.dictService.getAirportByCode(code);
    }

    private processResult(cities: CityModel[]): AutocompleteItem[] {
        const prepared = cities.slice(0, this.MAX_ITEMS_COUNT);

        return CitiesSearchService.addAirportsToCities(prepared);
    }

    public findCitiesByNameUnprocessed(cityNamePart: string): CityModel[] {
        const startsWith = this.findCitiesStartedWith(cityNamePart);

        if (startsWith.length > this.MAX_ITEMS_COUNT) {
            return startsWith;
        }

        const includes = this.findCitiesThatIncludes(cityNamePart);
        const foundByCitiesNamePart = CitiesSearchService.removeDuplicates([
            ...startsWith,
            ...includes,
        ]);

        if (foundByCitiesNamePart.length > this.MAX_ITEMS_COUNT) {
            return foundByCitiesNamePart;
        }

        const byAirports = this.findCitiesByAirportName(cityNamePart);
        const notStartsWith = [...includes, ...byAirports].sort(sortByName);
        return CitiesSearchService.removeDuplicates([
            ...startsWith,
            ...notStartsWith,
        ]);
    }

    public findCitiesByName(cityNamePart: string) {
        return this.processResult(
            this.findCitiesByNameUnprocessed(cityNamePart),
        );
    }

    public findCitiesByAirportCode(code): AutocompleteItem[] {
        const airport = this.findAirportByCode(code);
        if (!airport) {
            return [];
        }

        return this.findCitiesByCode(airport.city_code);
    }

    public findCitiesByCode(code: string): AutocompleteItem[] {
        if (code.length !== this.CODE_LENGTH) {
            return [];
        }

        const city = this.dictService.getCityByCode(code);

        return city ? this.processResult([city]) : [];
    }
}
