/**
 * MapManager - maplibregl.Map 单例管理器
 *
 * 基于 mitt 事件总线的地图实例管理器，用于全局管理 maplibregl.Map 的生命周期。
 * 替代原有 GeoScene 项目中的 ytmapUtil.js 单例模式。
 *
 * @module map/MapManager
 */

import maplibregl, {
  Map,
  MapOptions,
  NavigationControl,
  NavigationControlOptions,
} from 'maplibre-gl'
import mitt, { Emitter } from 'mitt'

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

/**
 * 地图配置接口，继承 maplibregl.MapOptions 并添加自定义配置项
 */
export interface MapConfig extends Omit<MapOptions, 'container'> {
  /**
   * 地图容器元素或其 id
   */
  container: string | HTMLElement

  /**
   * 导航控件配置，设为 false 则禁用导航控件
   * @defaultValue { showCompass: true, showZoom: true, visualizePitch: false }
   */
  navigationControl?: NavigationControlOptions | false
}

/**
 * 地图事件类型定义
 *
 * 通过 mitt 事件总线触发，供其他模块订阅
 */
export type MapEvents = {
  /**
   * 地图实例创建完毕并触发 'load' 事件后触发
   */
  'map:loaded': Map

  /**
   * 地图实例被销毁后触发
   */
  'map:destroyed': void

  /**
   * 地图实例创建开始时触发
   */
  'map:creating': MapConfig
}

// ============================================================
// 默认配置
// ============================================================

/**
 * 默认地图中心点坐标（南京）
 */
export const DEFAULT_CENTER: [number, number] = [118.734575, 31.990939]

/**
 * 默认缩放级别
 */
export const DEFAULT_ZOOM = 10

/**
 * 默认导航控件配置
 */
export const DEFAULT_NAVIGATION_CONTROL_OPTIONS: NavigationControlOptions = {
  showCompass: true,
  showZoom: true,
  visualizePitch: false,
}

// ============================================================
// MapManager 单例类
// ============================================================

/**
 * 地图管理器单例
 *
 * 负责全局唯一 maplibregl.Map 实例的创建、销毁与访问，
 * 并通过 mitt 事件总线广播地图生命周期事件。
 *
 * @example
 * ```ts
 * // 获取单例
 * const manager = MapManager.getInstance()
 *
 * // 订阅地图事件
 * manager.on('map:loaded', (map) => {
 *   console.log('地图已加载', map.getCenter())
 * })
 *
 * // 创建地图
 * manager.createMap('map-container', {
 *   style: 'https://demotiles.maplibre.org/style.json',
 *   center: [118.734575, 31.990939],
 *   zoom: 10,
 * })
 *
 * // 获取当前地图实例
 * const map = manager.getMap()
 *
 * // 销毁地图
 * manager.destroyMap()
 * ```
 */
export class MapManager {
  // ==========================================================
  // 静态成员（单例模式）
  // ==========================================================

  /**
   * 单例实例持有者
   */
  private static instance: MapManager | null = null

  /**
   * 获取 MapManager 单例实例
   *
   * @returns MapManager 单例
   */
  static getInstance(): MapManager {
    if (!MapManager.instance) {
      MapManager.instance = new MapManager()
    }
    return MapManager.instance
  }

  /**
   * 重置单例（主要用于测试环境）
   *
   * @internal
   */
  static resetInstance(): void {
    if (MapManager.instance) {
      MapManager.instance.destroyMap()
      MapManager.instance = null
    }
  }

  // ==========================================================
  // 实例成员
  // ==========================================================

  /**
   * 当前持有的 maplibregl.Map 实例
   */
  private map: Map | null = null

  /**
   * 当前持有的 NavigationControl 导航控件实例
   */
  private navigationControl: NavigationControl | null = null

  /**
   * mitt 事件总线实例
   */
  private eventBus: Emitter<MapEvents>

  /**
   * 当前地图配置快照
   */
  private currentConfig: MapConfig | null = null

  /**
   * 私有构造函数，防止外部实例化
   */
  private constructor() {
    this.eventBus = mitt<MapEvents>()
  }

  // ==========================================================
  // 事件总线 API
  // ==========================================================

  /**
   * 订阅地图事件
   *
   * @typeParam K - 事件类型
   * @param type - 事件名称
   * @param handler - 事件处理函数
   */
  on<K extends keyof MapEvents>(type: K, handler: (event: MapEvents[K]) => void): void {
    this.eventBus.on(type, handler)
  }

  /**
   * 取消订阅地图事件
   *
   * @typeParam K - 事件类型
   * @param type - 事件名称
   * @param handler - 要移除的事件处理函数（可选，不传则移除该事件的所有监听器）
   */
  off<K extends keyof MapEvents>(type: K, handler?: (event: MapEvents[K]) => void): void {
    this.eventBus.off(type, handler)
  }

  /**
   * 触发地图事件（内部使用）
   *
   * @typeParam K - 事件类型
   * @param type - 事件名称
   * @param event - 事件负载
   * @internal
   */
  private emit<K extends keyof MapEvents>(type: K, event: MapEvents[K]): void {
    this.eventBus.emit(type, event)
  }

  /**
   * 获取内部事件总线，用于高级场景
   *
   * @returns mitt 事件发射器
   */
  getEventBus(): Emitter<MapEvents> {
    return this.eventBus
  }

  // ==========================================================
  // 地图生命周期 API
  // ==========================================================

  /**
   * 创建并返回 maplibregl.Map 实例
   *
   * 如果已存在地图实例，会先销毁旧实例再创建新实例。
   * 创建完成后会监听地图 'load' 事件，触发后广播 'map:loaded'。
   *
   * @param container - 地图容器元素或其 id
   * @param options - 地图配置选项（不包含 container 字段）
   * @returns 创建的 maplibregl.Map 实例
   * @throws 如果容器元素不存在
   */
  createMap(
    container: string | HTMLElement,
    options: Omit<MapConfig, 'container'> = {}
  ): Map {
    // 销毁已有地图实例
    if (this.map) {
      this.destroyMap()
    }

    // 合并配置
    const config: MapConfig = {
      container,
      center: options.center ?? DEFAULT_CENTER,
      zoom: options.zoom ?? DEFAULT_ZOOM,
      ...options,
    }

    // 广播创建开始事件
    this.emit('map:creating', config)

    // 创建 MapLibre 地图实例
    this.map = new Map({
      container: config.container,
      style: config.style,
      center: config.center,
      zoom: config.zoom,
      bearing: config.bearing,
      pitch: config.pitch,
      minZoom: config.minZoom,
      maxZoom: config.maxZoom,
      maxBounds: config.maxBounds,
      interactive: config.interactive,
      hash: config.hash,
      attributionControl: config.attributionControl,
      renderWorldCopies: config.renderWorldCopies,
      trackResize: config.trackResize,
      localIdeographFontFamily: config.localIdeographFontFamily,
      transformRequest: config.transformRequest,
      scrollZoom: config.scrollZoom,
      dragPan: config.dragPan,
      dragRotate: config.dragRotate,
      boxZoom: config.boxZoom,
      doubleClickZoom: config.doubleClickZoom,
      touchZoomRotate: config.touchZoomRotate,
      touchPitch: config.touchPitch,
      keyboard: config.keyboard,
      cooperativeGestures: config.cooperativeGestures,
      fadeDuration: config.fadeDuration,
      crossSourceCollisions: config.crossSourceCollisions,
      collectResourceTiming: config.collectResourceTiming,
      clickTolerance: config.clickTolerance,
      bounds: config.bounds,
      fitBoundsOptions: config.fitBoundsOptions,
      pixelRatio: config.pixelRatio,
      validateStyle: config.validateStyle,
      maxTileCacheSize: config.maxTileCacheSize,
      maxTileCacheZoomLevels: config.maxTileCacheZoomLevels,
      refreshExpiredTiles: config.refreshExpiredTiles,
      logoPosition: config.logoPosition,
      maplibreLogo: config.maplibreLogo,
      bearingSnap: config.bearingSnap,
      zoomSnap: config.zoomSnap,
      pitchWithRotate: config.pitchWithRotate,
      rollEnabled: config.rollEnabled,
      reduceMotion: config.reduceMotion,
      canvasContextAttributes: config.canvasContextAttributes,
      maxCanvasSize: config.maxCanvasSize,
    })

    this.currentConfig = config

    // 添加导航控件
    this.addNavigationControl(config.navigationControl)

    // 监听地图加载完成事件，广播 'map:loaded'
    this.map.once('load', () => {
      if (this.map) {
        this.emit('map:loaded', this.map)
      }
    })

    return this.map
  }

  /**
   * 添加或更新导航控件
   *
   * @param options - 导航控件配置，false 表示不添加导航控件
   */
  private addNavigationControl(options: NavigationControlOptions | false | undefined): void {
    if (!this.map) return

    // 移除旧控件
    if (this.navigationControl) {
      this.map.removeControl(this.navigationControl)
      this.navigationControl = null
    }

    // 如明确设为 false 则不添加
    if (options === false) return

    // 使用默认或用户配置创建 NavigationControl
    const controlOptions = options ?? DEFAULT_NAVIGATION_CONTROL_OPTIONS
    this.navigationControl = new NavigationControl(controlOptions)
    this.map.addControl(this.navigationControl, 'top-left')
  }

  /**
   * 获取当前地图实例
   *
   * @returns 当前 maplibregl.Map 实例，未创建时返回 null
   */
  getMap(): Map | null {
    return this.map
  }

  /**
   * 获取当前导航控件实例
   *
   * @returns 当前 NavigationControl 实例，未创建时返回 null
   */
  getNavigationControl(): NavigationControl | null {
    return this.navigationControl
  }

  /**
   * 获取当前地图配置
   *
   * @returns 当前地图配置快照，未创建时返回 null
   */
  getCurrentConfig(): MapConfig | null {
    return this.currentConfig
  }

  /**
   * 销毁当前地图实例
   *
   * 调用 map.remove() 清理 DOM 节点和事件监听器，
   * 并广播 'map:destroyed' 事件。
   * 销毁后可通过 createMap() 重新创建新实例。
   */
  destroyMap(): void {
    if (this.map) {
      // 移除导航控件引用
      this.navigationControl = null

      // 销毁地图实例
      this.map.remove()
      this.map = null
      this.currentConfig = null

      // 广播销毁事件
      this.emit('map:destroyed', undefined)
    }
  }

  /**
   * 检查地图是否已创建
   *
   * @returns 地图实例是否存在
   */
  hasMap(): boolean {
    return this.map !== null
  }

  /**
   * 清除事件总线上的所有监听器
   *
   * 通常在页面卸载或全局重置时调用。
   */
  clearAllListeners(): void {
    this.eventBus.all.clear()
  }
}
