import type mapboxgl from 'mapbox-gl'
import qs from 'qs'
import { isEmpty, isNull, isObject, isString, isUndefined, omitBy } from 'lodash-es'
import type { GeoJSONSourceDataUrlParams, MapboxConfig, MapboxInstance } from './index'
import { API_URL, API_URL_PREFIX } from '/@/utils/net'
import { isProdMode } from '@/utils/env'
import { getToken } from '@/utils/auth'

// 组件名称
export const name = 'Mapbox'

/**
 * 注册 Mapbox 实例
 * @param config Mapbox 配置项
 */
export function useMapbox<T extends MapboxInstance, P extends MapboxConfig>(
    config: P,
): [(instance: T) => void, MapboxInstance] {
    const instanceRef = ref<T>()

    function register(instance: T) {
        if (isProdMode()) {
            if (instance === unref(instanceRef)) {
                return
            }

            onUnmounted(() => {
                instanceRef.value = null
            })
        }

        instanceRef.value = instance
        config && instance?.setConfig(config)
    }

    function getInstance() {
        const instance = unref(instanceRef)
        if (!instance) {
            console.warn('Mapbox instance is undefined!')
        }
        return instance as T
    }

    return [
        register,
        {
            setConfig: (config: Partial<P>) => getInstance()?.setConfig(config),
            isReady: computed(() => instanceRef?.value?.isReady) as unknown as ComputedRef<boolean>,

            on: (type: string, layerId: string, listener: (...args: any[]) => void) =>
                getInstance()?.on(type, layerId, listener),
            addSource: (id: string, source: mapboxgl.AnySourceData) => getInstance()?.addSource(id, source),
            removeSource: (id: string) => getInstance()?.removeSource(id),
            setGeoJSONSourceForRequest: (
                id: string,
                config: string | GeoJSONSourceDataUrlParams,
                handler?: (
                    data: Recordable,
                ) => GeoJSON.Feature<GeoJSON.Geometry> | GeoJSON.FeatureCollection<GeoJSON.Geometry>,
                filters?: string[],
            ) =>
                getInstance()?.setGeoJSONSourceForRequest(id, buildGeoJSONSourceDataUrl(config), handler, [
                    'jsonObject',
                    'liveVos',
                    ...(filters || []),
                ]),
            setGeoJSONSourceData: (id: string, data: mapboxgl.GeoJSONSource['setData'], filter?: string) =>
                getInstance()?.setGeoJSONSourceData(id, data, filter),
            setVectorTileSourceTiles: (id: string, tiles: string[]) =>
                getInstance()?.setVectorTileSourceTiles(id, tiles),
            addLayer: (layer: mapboxgl.Layer, beforeId?: string) => getInstance()?.addLayer(layer, beforeId),
            removeLayer: (id: string) => getInstance()?.removeLayer(id),
            setPaintProperty: (layerId: string, name: string, value: any) =>
                getInstance()?.setPaintProperty(layerId, name, value),
            setLayoutProperty: (layerId: string, name: string, value: any) =>
                getInstance()?.setLayoutProperty(layerId, name, value),
            setFilter: (layerId: string, filter: any[]) => getInstance()?.setFilter(layerId, filter),
            flyTo: (options: mapboxgl.FlyToOptions) => getInstance()?.flyTo(options),
        },
    ]
}

export function buildGeoJSONSourceDataUrl(config: GeoJSONSourceDataUrlParams | string): string {
    const baseURL = `${API_URL}${API_URL_PREFIX}`
    const defaultParams = {
        Authorization: getToken(),
    }

    const url = typeof config === 'string' ? config : config.url
    const params = typeof config === 'string' ? defaultParams : { ...defaultParams, ...config.params }
    const query = qs.stringify(
        omitBy(
            params,
            (value) =>
                isNull(value) ||
                isUndefined(value) ||
                (isString(value) && isEmpty(value)) ||
                (isObject(value) && isEmpty(value)),
        ),
        { addQueryPrefix: true },
    )
    return `${baseURL}${url}${query}`
}
