import type { Ref } from 'vue'
import { toRaw } from 'vue'
import type mapboxgl from 'mapbox-gl'

import { RulerControl } from 'mapbox-gl-controls'

// 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://gis.kshg.yiring.com'
const host = 'http://hb.straw.yiring.com:40051'

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

/**
 * 地图样式
 */
export const defaultStyle: mapboxgl.Style = {
    version: 8,
    zoom: 5.5,
    center: [111.507082, 27.19464],
    sprite: `${host}/api/sprites/straw.v2/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,
        },
        'wms-vec_w-source': {
            type: 'raster',
            tiles: [buildTdtTileUrl('vec', tk)],
            tileSize: 256,
            maxzoom: 18,
        },
        'wms-cva_w-source': {
            type: 'raster',
            tiles: [buildTdtTileUrl('cva', tk)],
            tileSize: 256,
            maxzoom: 18,
        },
        'wms-ter_w-source': {
            type: 'raster',
            tiles: [buildTdtTileUrl('cva', tk)],
            tileSize: 256,
            maxzoom: 18,
        },
        'wms-cta_w-source': {
            type: 'raster',
            tiles: [buildTdtTileUrl('cva', tk)],
            tileSize: 256,
            maxzoom: 18,
        },
        '100000': {
            type: 'vector',
            url: `${host}/api/tilesets/100000/tilejson`,
        },
        '430000': {
            type: 'vector',
            url: `${host}/api/tilesets/430000/tilejson`,
        },
        '430000.mask': {
            type: 'vector',
            url: `${host}/api/tilesets/430000.mask/tilejson`,
        },
    },
    layers: [
        // 火点核查所需默认图层
        {
            id: 'wms-img_w-layer',
            type: 'raster',
            source: 'wms-img_w-source',
            layout: { visibility: 'visible' },
        },
        {
            id: 'wms-cia_w-layer',
            type: 'raster',
            source: 'wms-cia_w-source',
            layout: { visibility: 'visible' },
        },
        {
            id: 'wms-vec_w-layer',
            type: 'raster',
            source: 'wms-vec_w-source',
            layout: { visibility: 'none' },
        },
        {
            id: 'wms-cva_w-layer',
            type: 'raster',
            source: 'wms-cva_w-source',
            layout: { visibility: 'none' },
        },
        {
            id: 'wms-ter_w-layer',
            type: 'raster',
            source: 'wms-ter_w-source',
            layout: { visibility: 'none' },
        },
        {
            id: 'wms-cta_w-layer',
            type: 'raster',
            source: 'wms-cta_w-source',
            layout: { visibility: 'none' },
        },
        // 占位图层
        {
            id: 'background',
            type: 'background',
            layout: { visibility: 'none' },
            paint: { 'background-color': 'hsla(0, 0%, 0%, 0)' },
        },
        {
            id: `100000-line`,
            source: '100000',
            'source-layer': '100000',
            type: 'line',
            paint: {
                'line-width': 1,
            },
        },
        {
            id: '430000-mask',
            type: 'fill',
            source: '430000.mask',
            'source-layer': '430000.mask',
            layout: {
                visibility: 'visible',
            },
            paint: {
                'fill-color': '#fff',
                'fill-opacity': 0.3,
            },
            minzoom: 5,
        },
        {
            id: `430000-shadow`,
            type: 'line',
            source: '430000',
            'source-layer': '430000',
            layout: {
                'line-join': 'round',
                'line-cap': 'round',
            },
            paint: {
                'line-width': 8,
                'line-blur': 92.7,
                'line-offset': -4,
            },
            minzoom: 5,
        },
        {
            id: `430000-line`,
            source: '430000',
            'source-layer': '430000',
            type: 'line',
            paint: {
                'line-width': 2,
            },
        },
    ],
}

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="iconify" data-icon="fluent:layer-20-filled" data-width="24" data-height="24"></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', () => {
            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 class CustomControl 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-cloud-button">
                <span class="mapboxgl-ctrl-icon" aria-hidden="true" title="Reset"></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?: { show: boolean; callback: any }
    // 风场控件
    windy?: boolean
    // 绘制控件
    draw?: boolean
}

/**
 * 加载地图控件
 * @param map 地图实例
 * @param options 控件配置
 */
export function loadMapControl(mapboxgl: any, map: mapboxgl.Map, options?: MapControlOptions) {
    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: './resource/img/layers/img_w.png',
                            active: false,
                        },
                        {
                            label: '矢量',
                            value: 'vec,cva',
                            image: './resource/img/layers/vec_w.png',
                        },
                        {
                            label: '地形',
                            value: 'ter,cta',
                            image: './resource/img/layers/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')
    }

    // 测距控件
    const rulers = toRaw(options.ruler)

    if (rulers && rulers.show) {
        const instance = new RulerControl({
            font: ['Microsoft YaHei'],
            fontHalo: 2,
            units: 'kilometers',
            labelFormat: (n) => `${n.toFixed(3)} km`,
        })

        map.addControl(instance, 'bottom-right')

        if (rulers.callback) {
            rulers.callback(instance)
        }

        return instance
    }

    // 定位
    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 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=='

export const PopupImage = 'https://oss.kshg.yiring.com/kshg/public/popup.png'
