import { Injectable } from '@angular/core';
import { GeoService } from '@app/shared/services';
import { LocalizationService } from '@shared/services/localization.service';

import { NetworkService } from '@shared/services/network.service';
import {
    CityModel,
    RegionFlatModel,
    AirportModel,
    CountryModel,
} from '../models';

@Injectable({ providedIn: 'root' })
export class DictionariesService {
    public regions: RegionFlatModel[] = [];
    regionsAll: any[];
    countriesAll: any[];
    citiesAll: CityModel[];

    ruCitiesCodes: Set<string> = new Set<string>();
    otherCitiesCodes: Set<string> = new Set<string>();

    private citiesCodeMap: Map<string, CityModel>;
    airportsAll: AirportModel[];
    private airportsCodeMap: Map<string, AirportModel>;
    ready$: Promise<unknown>;
    private lang: string;

    private russiaRegionId = 500374;

    constructor(
        localizationService: LocalizationService,
        private networkService: NetworkService,
        private geo: GeoService,
    ) {
        this.lang = localizationService.Language;
        this.ready$ = this.load().then(() => {
            this.filterAustralia();
        });
    }

    async locate(): Promise<CityModel | undefined> {
        try {
            const position = await this.geo.getPosition();
            return this.findCityByCoord(position.latitude, position.longitude);
        } catch (e) {
            return undefined;
        }
    }

    public findCityByCoord(lat: number, lon: number): CityModel {
        if (!this.citiesAll) {
            return;
        }
        let res = this.citiesAll.map((city) => {
            if (city.location) {
                const dist = this.calcCrow(
                    lat,
                    lon,
                    city.location.lat,
                    city.location.lon,
                );
                return { dist, city };
            }
        });
        res = res.sort((a, b) => {
            if (a.dist < b.dist) return -1;
            else return 1;
        });
        return res[0]?.city;
    }

    public calcCrow(
        lat1: number,
        lon1: number,
        lat2: number,
        lon2: number,
    ): number {
        const R = 6371; // km
        const dLat = this.toRad(lat2 - lat1);
        const dLon = this.toRad(lon2 - lon1);
        lat1 = this.toRad(lat1);
        lat2 = this.toRad(lat2);

        const a =
            Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.sin(dLon / 2) *
                Math.sin(dLon / 2) *
                Math.cos(lat1) *
                Math.cos(lat2);
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        const d = R * c;
        return d;
    }

    private toRad(Value) {
        return (Value * Math.PI) / 180;
    }

    getCityOrAirport(code: string) {
        return this.citiesCodeMap?.get(code) ?? this.airportsCodeMap?.get(code);
    }

    getCityByCode(code: string) {
        return this.citiesCodeMap?.get(code.toUpperCase());
    }

    getCityCodeByAirportCode(code: string) {
        return this.airportsCodeMap?.get(code.toUpperCase()).city_code;
    }

    getAirportByCode(code: string) {
        return this.airportsCodeMap?.get(code.toUpperCase());
    }

    getCityOrAirportNameAsync(code: string) {
        return this.ready$.then(() => {
            return this.getCityOrAirport(code)?.name || '';
        });
    }

    private filterAustralia() {
        // hide australia tab https://wzcloud.visualstudio.com/Aeroflot%202/_workitems/edit/8095
        const australiaRegionId = 500373;

        this.regions = this.regions.filter((region) => {
            return region.id !== australiaRegionId;
        });

        this.regionsAll = this.regionsAll.filter((region) => {
            return region.world_region_id !== australiaRegionId;
        });
    }

    private handleLoading(regions, countries, cities, airports) {
        this.regions = [];
        this.regionsAll = this.sortRegions(regions);
        this.countriesAll = countries;
        this.citiesAll = cities.filter(
            (x) => !/^[a-zA-Z.,:; ]+$/.test(x.title['ru']),
        );

        this.citiesAll.forEach(city => {
            if (city.country_code === "RU") 
            {
                this.ruCitiesCodes.add(city.code);
            } 
            else 
            {
                this.otherCitiesCodes.add(city.code);
            }
        });

        this.citiesCodeMap = new Map<string, CityModel>(
            this.citiesAll.map((c) => [c.code, c]),
        );
        this.airportsAll = airports.filter(
            (x) =>
                x.has_afl_flights == true &&
                !/^[a-zA-Z.,:; ]+$/.test(x.title['ru'].toLowerCase()),
        );
        this.airportsCodeMap = new Map<string, AirportModel>(
            this.airportsAll.map((a) => [a.code, a]),
        );

        for (const city of this.citiesAll) {
            city.name = city.title[this.lang];
            const airports: AirportModel[] = this.airportsAll.filter(
                (x) => x.city_code == city.code,
            );
            city.has_afl_flights = airports.length > 0;
            city.airports = this.sortByTitle(airports);
            city.airports.forEach((airport) => {
                airport.name = airport.title[this.lang];
            });
            const country = this.countriesAll.find(
                (x) => x.code == city.country_code,
            );
            if (country) {
                city.countryName = country.title[this.lang];
            }
        }
        this.citiesAll = this.citiesAll.filter((x) => x.has_afl_flights);

        for (const region of regions) {
            const r: RegionFlatModel = {
                id: region.world_region_id,
                name: region.title[this.lang],
                countries: [],
            };

            this.regions.push(r);

            //_contries.push(contres.find(x=>x.code==="RU"));
            const _countries: any[] = countries
                .filter(
                    (x) =>
                        x.world_region_id == region.world_region_id &&
                        x.code != 'RU',
                )
                .sort((a, b) =>
                    a.title[this.lang] > b.title[this.lang] ? 1 : -1,
                );
            if (r.id == this.russiaRegionId) {
                _countries.splice(
                    0,
                    0,
                    countries.find((x) => x.code == 'RU'),
                );
            }

            for (const country of _countries) {
                const citiesFiltred = this.citiesAll
                    .filter(
                        (x) =>
                            x.country_code == country.code && x.code != 'MOW',
                    )
                    .sort((a, b) =>
                        a.title[this.lang] > b.title[this.lang] ? 1 : -1,
                    );

                const mowCity = this.citiesAll.find((x) => x.code == 'MOW');

                if (country.code == 'RU' && typeof mowCity !== 'undefined') {
                    citiesFiltred.splice(
                        0,
                        0,
                        mowCity,
                    );
                }

                for (let i = 0; i < citiesFiltred.length; i++) {
                    const countryModel = {
                        code: country.code,
                    } as CountryModel;
                    r.countries.push(countryModel);

                    if (i == 0) {
                        countryModel.name = country.title[this.lang];
                    }

                    countryModel.city1 = citiesFiltred[i];
                    countryModel.city1.name = citiesFiltred[i].title[this.lang];

                    if (citiesFiltred[i].airports.length > 1) {
                        continue;
                    }

                    i++;
                    if (i >= citiesFiltred.length) break;
                    countryModel.city2 = citiesFiltred[i];
                    countryModel.city2.name = citiesFiltred[i].title[this.lang];
                }
            }
        }
    }

    // ToDo: refactor
    private load() {
        return Promise.all([
            this.networkService.getDictionary('world_regions'),
            this.networkService.getDictionary('countries'),
            this.networkService.getDictionary('cities'),
            this.networkService.getDictionary('airports'),
        ]).then((res) => this.handleLoading(res[0], res[1], res[2], res[3])); // Performance optimization; saves 100ms
    }

    /**
     * Per requirements, regions should be sorted by name but Russia should always be first region
     */
    private sortRegions(regions: any[]) {
        const sortedRegions = this.sortByTitle(regions);
        const ru = sortedRegions.splice(
            sortedRegions.findIndex(
                (r) => r.world_region_id == this.russiaRegionId,
            ),
            1,
        );
        sortedRegions.unshift(...ru);

        return sortedRegions;
    }

    private sortByTitle(data: { title: any }[]): any[] {
        return data.sort((a, b) =>
            a.title[this.lang]
                .toLowerCase()
                .localeCompare(b.title[this.lang].toLowerCase()),
        );
    }
}
