import type { Ref } from 'vue'
import type mapboxgl from 'mapbox-gl'
import { toRaw } from 'vue'
import { appendScript, appendStylesheet } from '/@/utils/dom'

// Mapbox accessToken
export const accessToken = 'pk.eyJ1IjoiaWZ6bSIsImEiOiJjamswc3M1a2gwYWEwM3Zxa3ZsMjh3djkwIn0.h93-XtdRdtoC1KbVv_ZIow'

// 天地图密钥
export const tk = 'aa0ccd36f2dbb86dbb16cbf63f0034a6'

// 自定义图层 host
// const host = 'http://192.168.0.156:1234'
const host = 'https://foxgis.server.yiring.com'

// 构建天地图图层地址
export function buildTdtTileUrl(id: string, tk: string) {
    return `${host}/api/tdt/cache/${id}/{z}/{y}/{x}/${tk}?v=2022`
}

/**
 * 地图样式
 */
export const defaultStyle: mapboxgl.Style = {
    version: 8,
    zoom: 5.5,
    center: [111.6, 27.170844],
    sprite: `${host}/api/sprites/hntq/sprite`,
    glyphs: `${host}/api/fonts/{fontstack}/{range}.pbf`,
    sources: {
        'wms-img_w-source': {
            type: 'raster',
            tiles: [buildTdtTileUrl('img', tk)],
            tileSize: 256,
            maxzoom: 18,
        },
        'wms-cia_w-source': {
            type: 'raster',
            tiles: [buildTdtTileUrl('cia', tk)],
            tileSize: 256,
            maxzoom: 18,
        },

        // ============ 省市县边界数据源 ============
        /** 全国 */
        '100000': {
            type: 'vector',
            url: `${host}/api/tilesets/100000/tilejson`,
        },
        /** 湖南省 */
        '430000': {
            type: 'vector',
            url: `${host}/api/tilesets/430000/tilejson`,
        },
        /** 湖南省 mask */
        '430000.mask': {
            type: 'vector',
            url: `${host}/api/tilesets/430000.mask/tilejson`,
        },
        /** 湖南省-城市聚合 */
        '430000.city': {
            type: 'vector',
            url: `${host}/api/tilesets/430000.city/tilejson`,
        },
        /** 湖南省-城市聚合名称 */
        '430000.city.label': {
            type: 'vector',
            url: `${host}/api/tilesets/430000.city.label/tilejson`,
        },
        /** 湖南省-县城聚合 */
        '430000.area': {
            type: 'vector',
            url: `${host}/api/tilesets/430000.area/tilejson`,
        },
        /** 湖南省-县城聚合名称 */
        '430000.area.label': {
            type: 'vector',
            url: `${host}/api/tilesets/430000.area.label/tilejson`,
        },

        // ============ 流域河流数据源 ============
        /** 湖南省-全流域及遮罩 */
        '430000.watershed-quan-liu-yu': {
            type: 'vector',
            url: `${host}/api/tilesets/watershed-quan-liu-yu/tilejson`,
            minzoom: 0,
            maxzoom: 11,
            bounds: [87.638275, 8.296956, 136.638275, 44.385598],
        },
        /** 湖南省-大河流域 */
        '430000.watershed-big': {
            type: 'vector',
            url: `${host}/api/tilesets/watershed-big/tilejson`,
            minzoom: 1,
            maxzoom: 13,
            bounds: [107.264454, 23.925618, 114.296651, 30.548506],
        },
        /** 湖南省-大河流域名称 */
        '430000.watershed-big-label': {
            type: 'vector',
            url: `${host}/api/tilesets/watershed-big-label/tilejson`,
            minzoom: 0,
            maxzoom: 0,
            bounds: [109.539, 26.6586, 112.632, 29.6013],
        },
        /** 湖南省-中小河流域 */
        '430000.watershed-medium': {
            type: 'vector',
            url: `${host}/api/tilesets/watershed-medium/tilejson`,
            minzoom: 0,
            maxzoom: 11,
            bounds: [107.264454, 23.925618, 114.296644, 30.548506],
        },
        /** 湖南省-中小河流域名称 */
        '430000.watershed-medium-label': {
            type: 'vector',
            url: `${host}/api/tilesets/watershed-medium-label/tilejson`,
            minzoom: 0,
            maxzoom: 0,
            bounds: [108.3062, 24.5421, 114.074, 30.0494],
        },
        /** 湖南省-上中下游边界 */
        '430000.watershed-big-medium': {
            type: 'vector',
            url: `${host}/api/tilesets/watershed-big-medium/tilejson`,
            minzoom: 0,
            maxzoom: 12,
            bounds: [107.264454, 23.925618, 114.296651, 30.548506],
        },
        /** 湖南省-二级河流 */
        '430000.river-secondary': {
            type: 'vector',
            url: `${host}/api/tilesets/river-secondary/tilejson`,
            minzoom: 0,
            maxzoom: 9,
            bounds: [87.638275, 8.296956, 136.638275, 44.385598],
        },
    },
    layers: [
        // 默认图层 - 影像底图
        {
            id: 'wms-img_w-layer',
            type: 'raster',
            source: 'wms-img_w-source',
            layout: { visibility: 'none' },
        },
        // 默认图层 - 标注底图
        {
            id: 'wms-cia_w-layer',
            type: 'raster',
            source: 'wms-cia_w-source',
            layout: { visibility: 'none' },
        },
        // 占位图层
        {
            id: 'background',
            type: 'background',
            layout: { visibility: 'visible' },
            paint: { 'background-color': '#FFFFFF' },
        },
        // 天空图层
        {
            id: 'sky',
            type: 'sky',
            paint: {
                'sky-type': 'gradient',
                'sky-gradient': [
                    'interpolate',
                    ['linear'],
                    ['sky-radial-progress'],
                    0.8,
                    'rgba(135, 206, 235, 1.0)',
                    1,
                    'rgba(0,0,0,0.1)',
                ],
                'sky-gradient-center': [0, 0],
                'sky-gradient-radius': 90,
                'sky-opacity': ['interpolate', ['exponential', 0.1], ['zoom'], 5, 0, 22, 1],
            },
        },

        // ============ 全国边界图层 ============
        // 自定义图层
        {
            id: `100000-line`,
            source: '100000',
            'source-layer': '100000',
            type: 'line',
            layout: {
                visibility: 'none',
            },
            paint: {
                'line-width': 1,
            },
        },

        // 城市边界
        {
            id: `430000.city-line`,
            source: '430000.city',
            'source-layer': '430000.city',
            type: 'line',
            paint: {
                'line-width': 1,
                'line-color': '#ccc',
            },
            minzoom: 5,
        },

        // 县城边界
        {
            id: `430000.area-line`,
            source: '430000.area',
            'source-layer': '430000.area',
            type: 'line',
            paint: {
                'line-width': 1,
                'line-color': '#ccc',
            },
            minzoom: 8,
        },

        // ============ 省市县边界图层 ============
        {
            id: '430000-mask',
            type: 'fill',
            source: '430000.mask',
            'source-layer': '430000.mask',
            layout: {
                visibility: 'visible',
            },
            paint: {
                'fill-color': '#F4F5F7',
            },
            minzoom: 5,
        },
        {
            id: `430000-shadow`,
            type: 'line',
            source: '430000',
            'source-layer': '430000',
            layout: {
                'line-join': 'round',
                'line-cap': 'round',
            },
            paint: {
                'line-width': 4,
                'line-blur': 92.7,
                'line-offset': -4,
                'line-color': '#444',
            },
            minzoom: 5,
        },
        {
            id: `430000-line`,
            source: '430000',
            'source-layer': '430000',
            type: 'line',
            paint: {
                'line-width': 2,
                'line-color': '#444',
            },
        },

        // 城市名称
        {
            id: `430000.city-label`,
            source: '430000.city.label',
            'source-layer': '430000.city.label',
            type: 'symbol',
            layout: {
                'text-field': '{name}',
                'text-size': 12,
                'text-allow-overlap': true,
                'text-ignore-placement': true,
                'text-justify': 'center',
                'text-anchor': 'center',
                'text-padding': 0,
                'text-line-height': 1.2,
                'text-letter-spacing': 0.1,
                'text-max-width': 8,
            },
            paint: {
                'text-color': '#333',
                'text-halo-color': '#fff',
                'text-halo-width': 2,
                'text-halo-blur': 1,
            },
            minzoom: 5,
            maxzoom: 8,
        },
        // 县城名称
        {
            id: `430000.area-label`,
            source: '430000.area.label',
            'source-layer': '430000.area.label',
            type: 'symbol',
            layout: {
                'text-field': '{name}',
                'text-size': 12,
                'text-allow-overlap': true,
                'text-ignore-placement': true,
                'text-justify': 'center',
                'text-anchor': 'center',
                'text-padding': 0,
                'text-line-height': 1.2,
                'text-letter-spacing': 0.1,
                'text-max-width': 8,
            },
            paint: {
                'text-color': '#333',
                'text-halo-color': '#fff',
                'text-halo-width': 2,
                'text-halo-blur': 1,
            },
            minzoom: 8,
        },
    ],
}

interface LayerOption {
    label: string
    value: string
    image: string
    active?: boolean
}

/**
 * 图层控件
 * 目前仅支持天地图图层
 */
class LayerControl implements mapboxgl.IControl {
    map: mapboxgl.Map | undefined
    container: HTMLElement | undefined
    tk: string
    layers?: LayerOption[]

    constructor(options: Recordable) {
        this.tk = options.tk
        this.layers = options.layers
    }

    onAdd(map: mapboxgl.Map) {
        this.map = map
        this.container = document.createElement('div')
        this.container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group mapbox-control'

        const layersWrapHtml: string[] = []
        if (this.layers) {
            layersWrapHtml.push('<div class="mapboxgl-ctrl-layer-wrap">')
            this.layers.forEach((layer) => {
                layersWrapHtml.push(`
                    <div class="mapboxgl-ctrl-layer ${layer.active ? 'active' : ''}" data-layer-ids="${layer.value}">
                        <img src="${layer.image}" alt="${layer.label}" />
                        <span>${layer.label}</span>
                    </div>
                `)

                for (const id of layer.value.split(',')) {
                    map.addLayer(
                        {
                            id: `t_${id}`,
                            type: 'raster',
                            source: {
                                type: 'raster',
                                tiles: [buildTdtTileUrl(id, this.tk)],
                                tileSize: 256,
                                maxzoom: 18,
                            },
                            layout: { visibility: layer.active ? 'visible' : 'none' },
                        },
                        'background',
                    )
                }
            })
            layersWrapHtml.push('</div>')
        }

        this.container.innerHTML = `
            <button type="button" class="mapboxgl-ctrl-layer-button" title="地图样式选择">
                <span class="mapboxgl-ctrl-icon" aria-hidden="true" title="Layer"></span>
                ${layersWrapHtml.join('')}
            </button>
        `
        this.container.addEventListener('click', (e) => {
            const target = e.target as HTMLElement
            const parent = target.parentElement as HTMLElement
            if (parent.classList.contains('mapboxgl-ctrl-layer')) {
                const ids = parent.getAttribute('data-layer-ids') as string
                const list = parent.parentElement?.children as HTMLCollection
                for (let i = 0; i < list.length; i++) {
                    const item = list[i] as HTMLElement
                    if (item.classList.contains('mapboxgl-ctrl-layer')) {
                        if (item.getAttribute('data-layer-ids') === ids) {
                            item.classList.toggle('active')
                        } else {
                            item.classList.remove('active')
                        }
                    }
                }

                this.layers?.forEach((layer) => {
                    for (const id of layer.value.split(',')) {
                        map.setLayoutProperty(`t_${id}`, 'visibility', 'none')
                    }
                })
                if (parent.classList.contains('active')) {
                    for (const id of ids.split(',')) {
                        map.setLayoutProperty(`t_${id}`, 'visibility', 'visible')
                    }
                }
                return
            }

            this.container?.querySelector('button')?.classList.toggle('-active')
        })

        return this.container
    }

    onRemove() {
        this.container?.parentNode?.removeChild(this.container)
        this.map = undefined
    }
}

/**
 * 经纬度坐标拾取控件
 */
class GeolocateInfoControl implements mapboxgl.IControl {
    map: mapboxgl.Map | undefined
    container: HTMLElement | undefined

    toText(lngLat: mapboxgl.LngLat) {
        return `经度：${lngLat.lng.toFixed(6)}, 纬度：${lngLat.lat.toFixed(6)}`
    }

    onAdd(map: mapboxgl.Map) {
        this.map = map
        this.container = document.createElement('div')
        this.container.className = 'mapboxgl-ctrl mapboxgl-ctrl-lnglat'
        this.container.textContent = this.toText(map.getCenter())

        map.on('mousemove', (e) => {
            const element = this.container as HTMLElement
            element.textContent = this.toText(e.lngLat)
        })

        return this.container
    }

    onRemove() {
        this.container?.parentNode?.removeChild(this.container)
        this.map = undefined
    }
}

/**
 * 地图复位控件
 */
class ResetControl implements mapboxgl.IControl {
    map: mapboxgl.Map | undefined
    container: HTMLElement | undefined
    options: mapboxgl.FlyToOptions

    constructor(options: mapboxgl.FlyToOptions) {
        this.options = options
    }

    onAdd(map: mapboxgl.Map) {
        this.map = map
        this.container = document.createElement('div')
        this.container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group mapbox-control'
        this.container.innerHTML = `
            <button type="button" class="mapboxgl-ctrl-reset-button mapboxgl-ctrl-reset" title="地图复位">
                <span class="mapboxgl-ctrl-icon" aria-hidden="true" title="Reset"></span>
            </button>
        `

        this.container.addEventListener('click', () => {
            // alert('map: ' + JSON.stringify(this.map.getCenter()))
            this.map?.flyTo(this.options)
        })

        return this.container
    }

    onRemove() {
        this.container?.parentNode?.removeChild(this.container)
        this.map = undefined
    }
}

/**
 * 测量控件
 */
class MeasureControl implements mapboxgl.IControl {
    map: mapboxgl.Map | undefined
    container: HTMLElement | undefined

    onAdd(map: mapboxgl.Map) {
        this.map = map
        this.container = document.createElement('div')
        this.container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group mapbox-control'
        this.container.innerHTML = `
            <button type="button" class="mapboxgl-ctrl-reset-button" title="面积计算">
                <span class="iconify" data-icon="carbon:area-custom" data-width="24" data-height="24"></span>
            </button>
        `

        this.container.addEventListener('click', () => {
            this.container?.querySelector('button')?.classList.toggle('-active')
            console.log('TODO: 测量面积功能暂未实现, 敬请期待...')
        })

        return this.container
    }

    onRemove() {
        this.container?.parentNode?.removeChild(this.container)
        this.map = undefined
    }
}

/**
 * 风场控件
 */
export class WindyControl implements mapboxgl.IControl {
    map: mapboxgl.Map | undefined
    container: HTMLElement | undefined
    onOpen: () => Promise<void>
    onClose: Function

    constructor(options: Recordable) {
        this.onOpen = options.onOpen
        this.onClose = options.onClose
    }

    onAdd(map: mapboxgl.Map) {
        this.map = map
        this.container = document.createElement('div')
        this.container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group mapbox-control'
        this.container.innerHTML = `
            <button type="button" class="mapboxgl-ctrl-reset-button" title="风场">
                <span class="iconify" data-icon="ph:wind-light" data-width="24" data-height="24"></span>
            </button>
        `

        this.container.addEventListener('click', () => {
            const button = this.container?.querySelector('button')
            if (!button?.classList.contains('-active')) {
                this.onOpen().then(() => {
                    button?.classList.add('-active')
                })
            } else {
                this.onClose()
                button?.classList.remove('-active')
            }
        })

        return this.container
    }

    onRemove() {
        this.container?.parentNode?.removeChild(this.container)
        this.map = undefined
    }
}

export interface MapOptions {
    control?: MapControlOptions
    style?: mapboxgl.Style | Recordable
}

export interface MapControlOptions {
    // 导航控件
    navigation?: {
        zoom?: boolean
        compass?: boolean
    }
    // 图层控制
    layer?:
        | boolean
        | {
              tk: string
              layers: LayerOption[]
          }
    // 重置控件
    reset?: boolean | mapboxgl.FlyToOptions
    // 全屏控件
    fullscreen?: boolean
    // 比例尺
    scale?: boolean
    // 经纬度拾取
    info?: boolean
    // 定位控件
    geolocate?: Recordable
    // 面积测算控件
    measure?: boolean
    // 距离测算控件
    ruler?: boolean
    // 风场控件
    windy?: boolean
    // 绘制控件
    draw?: boolean
}

/**
 * 加载地图控件
 * @param map 地图实例
 * @param options 控件配置
 */
export function loadMapControl(mapboxgl: any, map: mapboxgl.Map, options?: MapControlOptions) {
    console.log(mapboxgl, map, options)
    if (!options) {
        return
    }

    // 导航控件
    if (options.navigation) {
        map.addControl(
            new mapboxgl.NavigationControl({
                showCompass: options.navigation.compass,
                showZoom: options.navigation.zoom,
            }),
        )
    }

    // 全屏控件
    if (options.fullscreen) {
        map.addControl(new mapboxgl.FullscreenControl())
    }

    // 地图复位控件
    if (options.reset) {
        if (typeof options.reset === 'boolean') {
            map.addControl(
                new ResetControl({
                    zoom: map.getZoom(),
                    pitch: map.getPitch(),
                    bearing: map.getBearing(),
                    center: map.getCenter(),
                }),
            )
        } else {
            map.addControl(new ResetControl(options.reset))
        }
    }

    // 图层选择控件
    if (options.layer) {
        if (typeof options.layer === 'boolean') {
            map.addControl(
                new LayerControl({
                    tk,
                    layers: [
                        {
                            label: '影像',
                            value: 'img,cia',
                            image: `${host}/api/assets/images/img_w.png`,
                        },
                        {
                            label: '矢量',
                            value: 'vec,cva',
                            image: `${host}/api/assets/images/vec_w.png`,
                            active: true,
                        },
                        {
                            label: '地形',
                            value: 'ter,cta',
                            image: `${host}/api/assets/images/ter_w.png`,
                        },
                    ],
                }),
            )
        } else {
            map.addControl(new LayerControl(options.layer))
        }
    }

    // 面积测量控件
    if (options.measure) {
        map.addControl(new MeasureControl())
    }

    // 经纬度信息控件
    if (options.info) {
        map.addControl(new GeolocateInfoControl(), 'bottom-left')
    }

    // 比例尺控件
    if (options.scale) {
        map.addControl(new mapboxgl.ScaleControl(), 'bottom-left')
    }

    // 定位
    if (options.geolocate) {
        const geolocate = new mapboxgl.GeolocateControl({
            positionOptions: {
                enableHighAccuracy: true,
                timeout: 10000,
            },
            trackUserLocation: true,
            showUserHeading: true,
        })
        // #ifdef APP-PLUS
        geolocate.geolocation = options.geolocate
        // #endif
        map.addControl(geolocate, 'top-right')
    }
}

export function toPointGeoJSON(
    data?: ({ lon: number; lat: number } & Recordable)[],
): GeoJSON.FeatureCollection<GeoJSON.Geometry, GeoJSON.GeoJsonProperties> {
    return {
        type: 'FeatureCollection',
        features: !data
            ? []
            : data.map((item) => {
                  return {
                      type: 'Feature',
                      properties: {
                          ...item,
                      },
                      geometry: {
                          type: 'Point',
                          coordinates: [item.lon, item.lat],
                      },
                  }
              }),
    }
}

export function createPopupHtml(entry: Recordable, mapping: Recordable) {
    const htmls: string[] = ['<div class="mapboxgl-custom-popup">']

    for (const key in entry) {
        const lable = mapping[key]
        if (!lable) {
            continue
        }

        const value = entry[key]
        htmls.push(`
            <div class="popup-row">
                <span class="popup-title">${lable}：</span>
                ${value}
            </div>
        `)
    }

    htmls.push('</div>')
    return htmls.join('')
}

/**
 * 加载地图依赖库
 */
export function loadMapboxLibs() {
    const id = 'mapbox-gl'
    const version = '2.15.0'
    const resource = `static/js/mapbox-gl-js/${version}/${id}`
    return Promise.all([
        appendScript(id, `${resource}.js`),
        appendStylesheet(id, `${resource}.css`),
        appendStylesheet(id, `${resource}.extra.css`),
    ])
}

export const getMapInstance = (ref: Ref<any>): mapboxgl.Map => toRaw(ref.value?.map) as mapboxgl.Map
export function getGeoJSONSource(map: mapboxgl.Map, sourceName: string): mapboxgl.GeoJSONSource {
    return map.getSource(sourceName) as mapboxgl.GeoJSONSource
}
export function getImageSource(map: mapboxgl.Map, sourceName: string): mapboxgl.ImageSource {
    return map.getSource(sourceName) as mapboxgl.ImageSource
}

export const EmptyImage =
    'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABlBMVEXMzMz/+v+u+uEzAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAACklEQVQImWNgAAAAAgAB9HFkpgAAAABJRU5ErkJggg=='

/**
 * Mapbox 组件配置参数类型定义
 */
export interface MapboxConfig {
    /**
     * Mapbox 构造函数参数
     * @link https://docs.mapbox.com/mapbox-gl-js/api/map/
     */
    options?: Partial<mapboxgl.MapboxOptions>
    /**
     * Mapbox 地图样式
     * @link https://docs.mapbox.com/mapbox-gl-js/style-spec/
     */
    style?: Partial<mapboxgl.Style>
    /**
     * 地图资源/数据源来源或归属描述信息
     */
    attribution?: {
        /**
         * 文本
         * @example 湖南省气象台
         * @default ''
         */
        text: string
        /**
         * 位置, 默认为 'bottom-right'
         * @example 'bottom-left', 'bottom-right'
         * @default 'bottom-right'
         */
        align?: 'bottom-left' | 'bottom-right'
    }

    // =============== 自定义事件 =================
    /**
     * 注意: 此处暴露的 map 对象的类型定义只是为了方便 IDE 提示, 实际上 map 对象上的方法和属性并不完整，依赖于 Mapbox 自定义组件的内部实现
     * @param map 组件实例
     * @param data 事件数据, 由视图层在 Map 加载完成后传入的可序列化 JSON 数据
     */
    onLoaded?: (map: MapboxInstance, data: Recordable) => void
}

/**
 * Mapbox 组件实例类型定义
 */
export interface MapboxInstance {
    /**
     * 设置地图组件配置
     * @param config 地图配置
     */
    setConfig: (config: Partial<MapboxConfig>) => void
    /**
     * 地图组件是否准备好了
     */
    isReady: ComputedRef<boolean>
}
