import { merge, omit } from 'lodash-es'
import { HandlerUtil, defaultStyle, loadMapControl, loadMapboxLibs, sendRequest } from '/@/components/Map/Mapbox'

// renderjs 官方文档
// https://uniapp.dcloud.io/tutorial/renderjs.html
// renderjs 的一些细节问题
// https://juejin.cn/post/7049185827582115870

async function request(url, handler) {
    const data = await sendRequest(url)
    if (data.status !== 200) {
        throw new Error(data.message)
    }

    const { body } = data
    if (handler) {
        // TODO: 待优化
        // 1. 考虑是否为函数
        // 2. 考虑箭头函数的情况，可能意外的兼容性问题

        // HandlerUtil 为 renderjs 提供的工具类，用于处理一些常用的数据转换，需要保证逻辑层同样使用此工具类进行处理
        console.debug('handler', handler, HandlerUtil)

        // eslint-disable-next-line no-eval
        const executableFunction = eval(`(${handler})`)
        return { data: executableFunction(body), raw: body }
    }

    return { data: body, raw: body }
}

const defaultEmptyGeoJSON = {
    type: 'FeatureCollection',
    features: [],
}

export default {
    mounted() {},
    methods: {
        loadLibs: loadMapboxLibs,
        initMap(options) {
            // 移除地图
            if (this.map && this.map.remove) {
                this.map.remove()
            }

            // 加载地图
            const mapboxgl = window.mapboxgl
            const map = new mapboxgl.Map({
                // accessToken,
                container: options.container,
                style: merge({
                    ...defaultStyle,
                    ...options?.style,
                }),
                ...merge(
                    {
                        minZoom: 5,
                        maxZoom: 18,
                    },
                    options?.options,
                ),
                // 禁用默认的 attribution 控件
                attributionControl: false,
                // 禁用旋转
                dragRotate: false,
                touchRotate: false,
                // 禁用倾斜
                pitchWithRotate: false,
                // 不同源的冲突碰撞检测
                crossSourceCollisions: false,
                // 启用本地字体
                localFontFamily: true,
            })

            // 绑定作用域
            this.map = map

            // 监听地图事件
            map.once('styledata', function () {
                // 禁用旋转
                map.dragRotate.disable()
                map.touchPitch.disable()
                map.touchZoomRotate.disableRotation()
            })

            // 加载地图控件
            map.on('load', () => {
                // #ifdef APP-PLUS
                if (options?.control?.geolocate) {
                    window.navigator.permissions = undefined
                    options.control.geolocate = {
                        geolocation: {
                            getCurrentPosition: (onPositionSuccess, _onPositionError, options) => {
                                this.onPositionSuccess = onPositionSuccess
                                this.$ownerInstance.callMethod('getCurrentPosition', options)
                            },
                        },
                    }
                }
                // #endif

                // 加载地图图层
                this.initMapLayers()
                    .then(() => {
                        // 调用逻辑层方法
                        this.loaded = true
                        this.$ownerInstance.callMethod('onMapLoad', {
                            center: map.getCenter(),
                        })

                        // 添加地图数据来源描述
                        if (options.attribution) {
                            map.addControl(
                                new mapboxgl.AttributionControl({
                                    compact: true,
                                    customAttribution: options.attribution?.text,
                                }),
                                options.attribution?.align || 'bottom-right',
                            )
                        }

                        // 加载地图控件
                        loadMapControl(mapboxgl, map, options?.control)
                    })
                    .catch(console.error)
            })
        },
        initMapLayers() {
            // TODO: 加载地图图层
            return Promise.resolve()
        },
        // 检测触发变更时候的有效性
        checkOnChangeValidity(options) {
            return options && Object.keys(options).length > 0 && this.map
        },
        emitChangeEvent(type) {
            // 通知逻辑层，本次变更事件已经送达
            this.$ownerInstance.callMethod('onMapOptionsChangeEvent', { type })
        },
        changeOptions(options) {
            if (!options.container) {
                return
            }

            if (typeof window.mapboxgl === 'object') {
                this.initMap(options)
            } else {
                this.loadLibs().then(() => {
                    this.initMap(options)
                })
            }
        },
        changeFlyToOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                this.map.flyTo(options)
            }
        },
        changeAddSourceOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                this.map.addSource(options.id, options.source)
            }
        },
        changeSetGeoJSONSourceForRequestOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                if (this.map.getSource(options.id)) {
                    // 清空数据
                    this.map.getSource(options.id).setData(defaultEmptyGeoJSON)
                } else {
                    // 添加空的 source
                    this.map.addSource(options.id, {
                        type: 'geojson',
                        data: defaultEmptyGeoJSON,
                    })
                }

                // 请求数据
                request(options.url, options.handler).then(({ data, raw }) => {
                    this.map.getSource(options.id).setData(data)
                    this.$ownerInstance.callMethod('onSourceRequestHandle', {
                        id: options.id,
                        url: options.url,
                        data: omit(raw, options.filters || []),
                    })
                })
            }
        },
        changeSetGeoJSONSourceDataOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                this.map.getSource(options.id).setData(options.data)
            }
        },
        changeSetVectorTileSourceTilesOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                this.map.getSource(options.id).setTiles(options.tiles)
            }
        },
        changeAddLayerOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                this.map.addLayer(options.layer, options.beforeId)
                console.log('options', options)
            }
        },
        changeRemoveOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                if (options.type === 'layer') {
                    this.map.removeLayer(options.id)
                } else if (options.type === 'source') {
                    this.map.removeSource(options.id)
                }
            }
        },
        changeSetFilterOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                this.map.setFilter(options.layer, options.filter)
            }
        },
        changeSetLayoutPropertyOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                this.map.setLayoutProperty(options.layer, options.name, options.value)
            }
        },
        changeSetPaintPropertyOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                this.map.setPaintProperty(options.layer, options.name, options.value)
            }
        },
        changeEventOptions(options) {
            this.emitChangeEvent(options.fn)
            if (this.checkOnChangeValidity(options)) {
                if (options.event === 'on') {
                    if (options.type === 'click') {
                        this.map.on(options.type, options.layer, (e) => {
                            console.log('e', e)
                            const feature = e.features[0]
                            new window.mapboxgl.Popup()
                                .setLngLat(feature.geometry.coordinates)
                                .setHTML(
                                    feature.properties.popup
                                        .replace('{key}', feature.properties.key)
                                        .replace('{value}', feature.properties.value)
                                        .replace('{suffix}', feature.properties.suffix || ''),
                                )
                                .addTo(this.map)
                            this.$ownerInstance.callMethod('onEvent', options)
                        })
                    } else {
                        this.map.on(options.type, options.layer, () => {})
                    }
                } else if (options.event === 'off') {
                    this.map.off(options.type, options.layer, () => {})
                }
            }
        },
    },
}
