/**
 * usePopup - 地图弹窗管理组合式函数
 *
 * 基于 MapManager 单例中的 Map 实例，提供在地图上显示/隐藏 Popup 弹窗的
 * 响应式操作方法。支持自定义 HTML 内容和完整的 Popup 配置选项。
 *
 * @module map/composables/usePopup
 */

import { ref, readonly, onUnmounted, type Ref, type DeepReadonly } from 'vue'
import { Popup, type PopupOptions, type LngLatLike, type Offset } from 'maplibre-gl'
import { useMap } from './useMap'

// ============================================================
// 类型定义
// ============================================================

/**
 * 扩展的弹窗选项
 *
 * 继承 maplibregl.PopupOptions 并增加便利配置项
 */
export interface UsePopupOptions extends Omit<PopupOptions, 'className' | 'maxWidth'> {
  /**
   * Popup 的 CSS 类名
   */
  className?: string | string[]

  /**
   * Popup 最大宽度
   * @defaultValue '240px'
   */
  maxWidth?: string

  /**
   * 是否在弹窗关闭时自动销毁（移除 DOM）
   * @defaultValue true
   */
  autoRemove?: boolean

  /**
   * 弹窗关闭回调
   */
  onClose?: () => void
}

/**
 * usePopup 返回类型
 */
export interface UsePopupReturn {
  /**
   * 当前 Popup 实例（只读）
   *
   * 无弹窗时为 null
   */
  popup: Ref<Popup | null>

  /**
   * 弹窗是否可见（只读）
   */
  isVisible: DeepReadonly<Ref<boolean>>

  /**
   * 弹窗当前位置（只读）
   *
   * 无弹窗时为 null
   */
  position: DeepReadonly<Ref<LngLatLike | null>>

  /**
   * 在地图上显示弹窗
   *
   * 如果已有弹窗，会先关闭旧弹窗再显示新弹窗。
   *
   * @param lngLat - 弹窗位置（经纬度坐标）
   * @param html - 弹窗 HTML 内容
   * @param options - 弹窗配置选项（可选）
   * @returns Popup 实例
   */
  showPopup: (
    lngLat: LngLatLike,
    html: string,
    options?: UsePopupOptions
  ) => Popup | null

  /**
   * 隐藏并销毁当前弹窗
   *
   * 调用后 isVisible 变为 false，popup 变为 null
   */
  hidePopup: () => void

  /**
   * 更新弹窗位置
   *
   * @param lngLat - 新的经纬度坐标
   * @returns 是否更新成功
   */
  setPosition: (lngLat: LngLatLike) => boolean

  /**
   * 更新弹窗 HTML 内容
   *
   * @param html - 新的 HTML 内容
   * @returns 是否更新成功
   */
  setHTML: (html: string) => boolean

  /**
   * 设置弹窗偏移量
   *
   * @param offset - 偏移量配置
   * @returns 是否设置成功
   */
  setOffset: (offset: Offset) => boolean
}

// ============================================================
// usePopup
// ============================================================

/**
 * 地图弹窗管理组合式函数
 *
 * 封装 maplibregl.Popup 的创建、显示、隐藏逻辑，提供 Vue 响应式状态。
 * 同一时间仅管理一个弹窗，新弹窗会覆盖旧弹窗。
 * 组件卸载时自动清理弹窗。
 *
 * @returns 弹窗管理方法与响应式状态
 *
 * @example
 * ```vue
 * <script setup lang="ts">
 * import { usePopup } from '@/map/composables/usePopup'
 *
 * const { showPopup, hidePopup, isVisible, popup } = usePopup()
 *
 * // 通过地图点击事件显示弹窗
 * function onMapClick(e) {
 *   showPopup(e.lngLat, `
 *     <div class="custom-popup">
 *       <h3>坐标信息</h3>
 *       <p>经度: ${e.lngLat.lng.toFixed(6)}</p>
 *       <p>纬度: ${e.lngLat.lat.toFixed(6)}</p>
 *     </div>
 *   `, {
 *     closeButton: true,
 *     closeOnClick: false,
 *     maxWidth: '300px',
 *     onClose: () => console.log('弹窗已关闭')
 *   })
 * }
 *
 * // 手动隐藏弹窗
 * // hidePopup()
 * </script>
 * ```
 */
export function usePopup(): UsePopupReturn {
  // ----------------------------------------------------------
  // 获取当前地图
  // ----------------------------------------------------------
  const { map } = useMap()

  // ----------------------------------------------------------
  // 响应式状态
  // ----------------------------------------------------------

  /** 当前弹窗实例 */
  const popup = ref<Popup | null>(null)

  /** 弹窗可见性 */
  const isVisible = ref<boolean>(false)

  /** 弹窗当前位置 */
  const position = ref<LngLatLike | null>(null)

  /** 当前弹窗关闭回调 */
  let currentOnClose: (() => void) | null = null

  // ----------------------------------------------------------
  // 内部辅助函数
  // ----------------------------------------------------------

  /**
   * 获取当前 Map 实例
   */
  function getMap() {
    return map.value
  }

  /**
   * 销毁当前弹窗
   */
  function destroyPopup(): void {
    if (popup.value) {
      try {
        popup.value.remove()
      } catch {
        // 忽略已移除的弹窗错误
      }
      popup.value = null
    }
    isVisible.value = false
    position.value = null

    // 触发关闭回调
    if (currentOnClose) {
      const cb = currentOnClose
      currentOnClose = null
      cb()
    }
  }

  // ----------------------------------------------------------
  // 公开方法
  // ----------------------------------------------------------

  /**
   * 在地图上显示弹窗
   */
  function showPopup(
    lngLat: LngLatLike,
    html: string,
    options: UsePopupOptions = {}
  ): Popup | null {
    const m = getMap()
    if (!m) {
      console.warn('[usePopup] 地图实例不存在，无法显示弹窗')
      return null
    }

    // 如果已有弹窗，先销毁
    destroyPopup()

    // 提取自定义配置
    const { autoRemove = true, onClose, ...popupOptions } = options

    // 处理 className
    let className: string | undefined
    if (typeof popupOptions.className === 'string') {
      className = popupOptions.className
    } else if (Array.isArray(popupOptions.className)) {
      className = popupOptions.className.join(' ')
    }

    try {
      const p = new Popup({
        closeButton: popupOptions.closeButton ?? true,
        closeOnClick: popupOptions.closeOnClick ?? true,
        closeOnMove: popupOptions.closeOnMove ?? false,
        focusAfterOpen: popupOptions.focusAfterOpen ?? true,
        anchor: popupOptions.anchor,
        offset: popupOptions.offset,
        className,
        maxWidth: popupOptions.maxWidth ?? '240px',
        subpixelPositioning: popupOptions.subpixelPositioning,
      })

      // 设置 HTML 内容和位置
      p.setLngLat(lngLat).setHTML(html).addTo(m)

      // 更新状态
      popup.value = p
      isVisible.value = true
      position.value = lngLat
      currentOnClose = onClose ?? null

      // 监听弹窗关闭事件
      if (autoRemove || onClose) {
        p.on('close', () => {
          isVisible.value = false
          position.value = null
          // Use as unknown comparison to avoid TS deep type instantiation on Popup
          if ((popup.value as unknown) === p) {
            popup.value = null
          }
          if (currentOnClose) {
            const cb = currentOnClose
            currentOnClose = null
            cb()
          }
        })
      }

      return p
    } catch (err) {
      console.error('[usePopup] 创建弹窗失败:', err)
      return null
    }
  }

  /**
   * 隐藏并销毁当前弹窗
   */
  function hidePopup(): void {
    destroyPopup()
  }

  /**
   * 更新弹窗位置
   */
  function setPosition(lngLat: LngLatLike): boolean {
    if (!popup.value) {
      console.warn('[usePopup] 当前没有活动的弹窗')
      return false
    }

    try {
      popup.value.setLngLat(lngLat)
      position.value = lngLat
      return true
    } catch (err) {
      console.error('[usePopup] 更新弹窗位置失败:', err)
      return false
    }
  }

  /**
   * 更新弹窗 HTML 内容
   */
  function setHTML(html: string): boolean {
    if (!popup.value) {
      console.warn('[usePopup] 当前没有活动的弹窗')
      return false
    }

    try {
      popup.value.setHTML(html)
      return true
    } catch (err) {
      console.error('[usePopup] 更新弹窗内容失败:', err)
      return false
    }
  }

  /**
   * 设置弹窗偏移量
   */
  function setOffset(offset: Offset): boolean {
    if (!popup.value) {
      console.warn('[usePopup] 当前没有活动的弹窗')
      return false
    }

    try {
      popup.value.setOffset(offset)
      return true
    } catch (err) {
      console.error('[usePopup] 设置弹窗偏移量失败:', err)
      return false
    }
  }

  // ----------------------------------------------------------
  // 组件卸载时清理
  // ----------------------------------------------------------
  onUnmounted(() => {
    destroyPopup()
    currentOnClose = null
  })

  // ----------------------------------------------------------
  // 返回
  // ----------------------------------------------------------
  return {
    popup: popup as Ref<Popup | null>,
    isVisible: readonly(isVisible),
    position: readonly(position),
    showPopup,
    hidePopup,
    setPosition,
    setHTML,
    setOffset,
  } as UsePopupReturn
}
