// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import type { Coordinate } from '@/core/coordinate.ts';

import { type LeafletTileSource, type LeafletTileSourceExtraParam, LEAFLET_TILE_SOURCES } from '@/consts/map.ts';

import type { MapProvider, MapInstance, MapInstanceInitOptions } from './base.ts';

import {
    isMapDataFetchProxyEnabled,
    getCustomMapTileLayerUrl,
    getCustomMapAnnotationLayerUrl,
    isCustomMapAnnotationLayerDataFetchProxyEnabled,
    getCustomMapMinZoomLevel,
    getCustomMapMaxZoomLevel,
    getCustomMapDefaultZoomLevel,
    getTomTomMapAPIKey,
    getTianDiTuMapAPIKey
} from '@/lib/server_settings.ts';
import services from '@/lib/services.ts';

export class LeafletMapProvider implements MapProvider {
    public static Leaflet: unknown = null;
    private readonly mapProvider: string;

    public constructor(mapProvider: string) {
        this.mapProvider = mapProvider;
    }

    public getWebsite(): string {
        if (this.mapProvider === 'custom') {
            return '';
        } else if (LEAFLET_TILE_SOURCES[this.mapProvider]) {
            return LEAFLET_TILE_SOURCES[this.mapProvider].website || '';
        } else {
            return '';
        }
    }

    public isSupportGetGeoLocationByClick(): boolean {
        return true;
    }

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    public asyncLoadAssets(language?: string): Promise<unknown> {
        return Promise.all([
            import('leaflet/dist/leaflet.css'),
            import('leaflet/dist/leaflet-src.esm.js').then(leaflet => LeafletMapProvider.Leaflet = leaflet)
        ]);
    }

    public createMapInstance(): MapInstance | null {
        const mapTileSource = LEAFLET_TILE_SOURCES[this.mapProvider];

        if (this.mapProvider !== 'custom' && !mapTileSource) {
            return null;
        }

        return new LeafletMapInstance(this.mapProvider, mapTileSource);
    }
}

export class LeafletMapInstance implements MapInstance {
    public dependencyLoaded: boolean = false;
    public inited: boolean = false;

    public readonly defaultZoomLevel: number;
    public readonly minZoomLevel: number;

    private readonly mapProvider: string;
    private readonly presetMapTileSource: LeafletTileSource;

    private leafletInstance: unknown | null;
    private leafletTileLayer: unknown | null;
    private leafletAnnotationLayer: unknown | null;
    private leafletZoomControl: unknown | null;
    private leafletAttribution: unknown | null;
    private leafletCenterMarker: unknown | null;

    public constructor(mapProvider: string, mapTileSource: LeafletTileSource) {
        this.dependencyLoaded = !!LeafletMapProvider.Leaflet;

        this.mapProvider = mapProvider;
        this.presetMapTileSource = mapTileSource;

        this.defaultZoomLevel = this.presetMapTileSource?.defaultZoomLevel || getCustomMapDefaultZoomLevel();
        this.minZoomLevel = this.presetMapTileSource?.minZoom || getCustomMapMinZoomLevel();
    }

    public initMapInstance(mapContainer: HTMLElement, options: MapInstanceInitOptions): void {
        if (!LeafletMapProvider.Leaflet) {
            return;
        }

        const leaflet = LeafletMapProvider.Leaflet;
        const leafletInstance = leaflet.map(mapContainer, {
            center: [ options.initCenter.latitude, options.initCenter.longitude ],
            zoom: options.zoomLevel,
            attributionControl: false,
            zoomControl: false
        });

        let tileUrlFormat, tileUrlSubDomains, annotationUrlFormat, annotationUrlSubDomains: string | undefined;
        let minZoom, maxZoom: number;

        if (this.mapProvider !== 'custom') {
            tileUrlFormat = this.presetMapTileSource?.tileUrlFormat;
            tileUrlSubDomains = this.presetMapTileSource?.tileUrlSubDomains;
            annotationUrlFormat = this.presetMapTileSource?.annotationUrlFormat;
            annotationUrlSubDomains = this.presetMapTileSource?.annotationUrlSubDomains;
            minZoom = this.presetMapTileSource?.minZoom;
            maxZoom = this.presetMapTileSource?.maxZoom;
        } else {
            tileUrlFormat = getCustomMapTileLayerUrl();
            annotationUrlFormat = getCustomMapAnnotationLayerUrl();
            minZoom = getCustomMapMinZoomLevel();
            maxZoom = getCustomMapMaxZoomLevel();
        }

        if (isMapDataFetchProxyEnabled()) {
            tileUrlFormat = services.generateMapProxyTileImageUrl(this.mapProvider, options.language);
            tileUrlSubDomains = '';
        } else if (this.presetMapTileSource && this.presetMapTileSource.tileUrlExtraParams) {
            tileUrlFormat = this.getFinalUrlFormat(this.presetMapTileSource.tileUrlFormat as string, this.presetMapTileSource.tileUrlExtraParams, options);
        }

        const tileLayer = leaflet.tileLayer(tileUrlFormat, {
            subdomains: tileUrlSubDomains,
            maxZoom: maxZoom,
            minZoom: minZoom
        });
        tileLayer.addTo(leafletInstance);

        if (annotationUrlFormat || (this.mapProvider === 'custom' && isCustomMapAnnotationLayerDataFetchProxyEnabled())) {
            if (isMapDataFetchProxyEnabled()) {
                annotationUrlFormat = services.generateMapProxyAnnotationImageUrl(this.mapProvider, options.language);
                annotationUrlSubDomains = '';
            } else if (this.presetMapTileSource && this.presetMapTileSource.annotationUrlExtraParams) {
                annotationUrlFormat = this.getFinalUrlFormat(this.presetMapTileSource.annotationUrlFormat as string, this.presetMapTileSource.annotationUrlExtraParams, options);
            }

            const annotationLayer = leaflet.tileLayer(annotationUrlFormat, {
                subdomains: annotationUrlSubDomains,
                maxZoom: maxZoom,
                minZoom: minZoom
            });
            annotationLayer.addTo(leafletInstance);

            this.leafletAnnotationLayer = annotationLayer;
        }

        const zoomControl = leaflet.control.zoom({
            zoomInTitle: options.text.zoomIn,
            zoomOutTitle: options.text.zoomOut
        });
        zoomControl.addTo(leafletInstance);

        if (this.presetMapTileSource && this.presetMapTileSource.attribution) {
            const attribution = leaflet.control.attribution({
                prefix: false
            });
            attribution.addAttribution(this.presetMapTileSource.attribution);
            attribution.addTo(leafletInstance);
            this.leafletAttribution = attribution;
        }

        leafletInstance.addEventListener('click', function(e) {
            if (options.onClick) {
                options.onClick({
                    latitude: e.latlng.lat,
                    longitude: e.latlng.lng
                });
            }
        });

        this.leafletInstance = leafletInstance;
        this.leafletTileLayer = tileLayer;
        this.leafletZoomControl = zoomControl;
        this.inited = true;
    }

    public setMapCenterTo(center: Coordinate, zoomLevel: number): void {
        if (!this.leafletInstance) {
            return;
        }

        this.leafletInstance.setView([ center.latitude, center.longitude ], zoomLevel);
    }

    public setMapCenterMarker(position: Coordinate): void {
        if (!LeafletMapProvider.Leaflet || !this.leafletInstance) {
            return;
        }

        const leaflet = LeafletMapProvider.Leaflet;

        if (!this.leafletCenterMarker) {
            const markerIcon = leaflet.icon({
                iconUrl: 'img/map-marker-icon.png',
                iconRetinaUrl: 'img/map-marker-icon-2x.png',
                iconSize: [25, 32],
                iconAnchor: [12, 32],
                shadowUrl: 'img/map-marker-shadow.png',
                shadowSize: [41, 32]
            });
            this.leafletCenterMarker = leaflet.marker([ position.latitude, position.longitude ], {
                icon: markerIcon
            });
            this.leafletCenterMarker.addTo(this.leafletInstance);
        } else {
            this.leafletCenterMarker.setLatLng([ position.latitude, position.longitude ]);
        }
    }

    public removeMapCenterMarker(): void {
        if (!this.leafletInstance || !this.leafletCenterMarker) {
            return;
        }

        this.leafletCenterMarker.remove();
        this.leafletCenterMarker = null;
    }

    private getFinalUrlFormat(urlFormat: string, urlExtraParams: LeafletTileSourceExtraParam[], options: MapInstanceInitOptions) {
        const params: string[] = [];

        for (let i = 0; i < urlExtraParams.length; i++) {
            const param = urlExtraParams[i];

            if (param.paramValueType === 'tomtom_key') {
                params.push(param.paramName + '=' + getTomTomMapAPIKey());
            } else if (param.paramValueType === 'tianditu_key') {
                params.push(param.paramName + '=' + getTianDiTuMapAPIKey());
            } else if (param.paramValueType === 'language' && options.language) {
                params.push(param.paramName + '=' + options.language);
            }
        }

        if (params.length) {
            if (urlFormat.indexOf('?') >= 0) {
                urlFormat = urlFormat + '&' + params.join('&');
            } else {
                urlFormat = urlFormat + '?' + params.join('&');
            }
        }

        return urlFormat;
    }
}
