import * as Cesium from 'cesium'
import { toRad, heightToZoom, zoomToHeight } from '@/map/utils/utils'

// 轨迹聚焦配置常量
const TRAJECTORY_FOCUS_CONFIG = Object.freeze({
  MIN_SPAN_DEG: 0.0005,      // 最小经纬度跨度
  MIN_SPAN_METERS: 120,      // 最小米制跨度  
  MARGIN_RATIO: 0.35,        // 边距比例
  MIN_HEIGHT_OFFSET: 300,    // 最小高度偏移
  DEFAULT_FOV: 60,           // 默认视场角（度）
  MIN_FOV: 15,               // 最小视场角（度）
  DEFAULT_PITCH: -90,        // 默认俯视角度（度）
  DEFAULT_DURATION: 1.2      // 默认飞行时长（秒）
})

/**
 * 相机服务：对 Cesium 相机的常用操作做语义封装。
 * 依赖：{ viewerOrThrow, store }
 */
export function createCameraService(deps) {
  const { viewerOrThrow, store } = deps

  const hasHomeApi =
    !!store && typeof store.setHomeView === 'function' && typeof store.getHomeView === 'function'

  const snapshotFromCamera = (camera) => {
    if (!camera) return null
    const carto = camera.positionCartographic
    if (!carto) return null
    return {
      lon: Cesium.Math.toDegrees(carto.longitude),
      lat: Cesium.Math.toDegrees(carto.latitude),
      height: carto.height,
      heading: Cesium.Math.toDegrees(camera.heading),
      pitch: Cesium.Math.toDegrees(camera.pitch),
      roll: Cesium.Math.toDegrees(camera.roll),
    }
  }

  const flyToAsync = (camera, options) => {
    if (!camera) return Promise.resolve(null)
    return new Promise((resolve, reject) => {
      const opts = options ? { ...options } : {}
      const userComplete = typeof opts.complete === 'function' ? opts.complete : null
      const userCancel = typeof opts.cancel === 'function' ? opts.cancel : null

      opts.complete = (...args) => {
        try {
          if (userComplete) userComplete(...args)
        } finally {
          resolve(true)
        }
      }

      opts.cancel = (...args) => {
        try {
          if (userCancel) userCancel(...args)
        } finally {
          resolve(false)
        }
      }

      try {
        camera.flyTo(opts)
      } catch (err) {
        reject(err)
      }
    })
  }

  return {
    /**
     * 设置相机中心点（经度、纬度、高度）。
     */
    async setCenter(lon, lat, height) {
      const viewer = viewerOrThrow()
      const targetHeight = typeof height === 'number' ? height : 1500
      viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(lon, lat, targetHeight) })
    },

    /**
     * 设置相机视图与姿态。
     * 参数：{ lon, lat, height, heading, pitch, roll }
     */
    setView(options) {
      const viewer = viewerOrThrow()
      const opts = options || {}
      const { lon, lat, height } = opts
      const heading = toRad(opts.heading || 0)
      const pitch = toRad(opts.pitch != null ? opts.pitch : -45)
      const roll = toRad(opts.roll || 0)
      viewer.camera.setView({
        destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
        orientation: { heading, pitch, roll },
      })
    },

    /**
     * 相机飞行到目标视图。
     * 参数：{ lon, lat, height, heading, pitch, roll, duration }
     */
    async flyTo(options) {
      const viewer = viewerOrThrow()
      const opts = options || {}
      return flyToAsync(viewer.camera, {
        destination: Cesium.Cartesian3.fromDegrees(opts.lon, opts.lat, opts.height),
        orientation: {
          heading: toRad(opts.heading || 0),
          pitch: toRad(opts.pitch != null ? opts.pitch : -45),
          roll: toRad(opts.roll || 0),
        },
        duration: opts.duration || 1.5,
      })
    },

    /**
     * 将相机适配到经纬度范围 [minLon, minLat, maxLon, maxLat]。
     */
    async fitBounds(bounds) {
      const viewer = viewerOrThrow()
      const b = bounds || [0, 0, 0, 0]
      const rectangle = Cesium.Rectangle.fromDegrees(b[0], b[1], b[2], b[3])
      return flyToAsync(viewer.camera, { destination: rectangle })
    },

    /**
     * 智能聚焦到轨迹，考虑3D高度和视场角计算安全高度。
     * @param {Array<{lon:number, lat:number, height?:number}>} points - 轨迹点集合
     * @param {Object} options - 配置选项
     * @param {number} [options.pitch] - 俯仰角（度），默认-90度垂直向下
     * @param {number} [options.duration] - 飞行时长（秒），默认1.2秒
     * @param {number} [options.marginRatio] - 边距比例，默认0.35
     * @param {number} [options.minHeightOffset] - 最小高度偏移，默认300米
     * @returns {Promise<boolean>} 飞行是否成功
     */
    async fitBoundsWithTrajectory(points, options = {}) {
      const viewer = viewerOrThrow()
      
      if (!Array.isArray(points) || points.length === 0) {
        console.warn('轨迹点集合为空，无法执行聚焦')
        return false
      }

      const config = { ...TRAJECTORY_FOCUS_CONFIG, ...options }
      const bounds = this._calculateBounds(points)
      
      if (!bounds) {
        console.warn('无法计算轨迹边界范围')
        return false
      }

      const [minLon, minLat, maxLon, maxLat] = bounds
      const centerLon = (minLon + maxLon) / 2
      const centerLat = (minLat + maxLat) / 2

      // 计算智能高度
      const altitude = this._calculateSmartAltitude(points, bounds, config)

      return flyToAsync(viewer.camera, {
        destination: Cesium.Cartesian3.fromDegrees(centerLon, centerLat, altitude),
        orientation: {
          heading: viewer.camera.heading,
          pitch: toRad(config.pitch ?? config.DEFAULT_PITCH),
          roll: 0
        },
        duration: config.duration ?? config.DEFAULT_DURATION
      })
    },

    /**
     * 设置缩放级别（近似）。可指定固定中心 center: { lon, lat }。
     * options: { animate?: boolean, duration?: number, easing?: Function, orientation?: { heading, pitch, roll } }
     */
    async setZoom(zoom, center, options) {
      const viewer = viewerOrThrow()
      const camera = viewer.camera
      const cameraCartographic = camera.positionCartographic
      const lat = center && typeof center.lat === 'number' ? center.lat : Cesium.Math.toDegrees(cameraCartographic.latitude)
      const lon = center && typeof center.lon === 'number' ? center.lon : Cesium.Math.toDegrees(cameraCartographic.longitude)
      const height = zoomToHeight(zoom, lat)
      const destination = Cesium.Cartesian3.fromDegrees(lon, lat, height)

      const opts = options || {}
      const animate = opts.animate !== false

      if (animate) {
        const duration = typeof opts.duration === 'number' ? Math.max(0.01, opts.duration) : 0.6
        const orientation = opts.orientation || {
          heading: camera.heading,
          pitch: camera.pitch,
          roll: camera.roll,
        }
        const easing =
          typeof opts.easing === 'function'
            ? opts.easing
            : Cesium.EasingFunction?.QUADRATIC_OUT || Cesium.EasingFunction?.LINEAR_NONE

        return flyToAsync(camera, {
          destination,
          orientation,
          duration,
          easingFunction: easing,
        })
      }

      camera.setView({ destination })
      return null
    },

    /**
     * 获取当前缩放级别（近似）。
     */
    getZoom() {
      const viewer = viewerOrThrow()
      const carto = viewer.camera.positionCartographic
      return heightToZoom(carto.height, Cesium.Math.toDegrees(carto.latitude))
    },

    /**
     * 放大（步长默认为 1）。
     */
    async zoomIn(step) {
      const stepSize = typeof step === 'number' ? step : 1
      const currentZoom = this.getZoom()
      return this.setZoom(currentZoom + stepSize)
    },

    /**
     * 缩小（步长默认为 1）。
     */
    async zoomOut(step) {
      const stepSize = typeof step === 'number' ? step : 1
      const currentZoom = this.getZoom()
      return this.setZoom(Math.max(0, currentZoom - stepSize))
    },

    /**
     * 获取当前相机视图快照（经纬度 + 姿态，角度制）。
     */
    getCurrentView() {
      const viewer = viewerOrThrow()
      return snapshotFromCamera(viewer.camera)
    },

    /**
     * 覆盖 store 中的 home 视图。
     */
    setHomeView(view) {
      if (!hasHomeApi) return
      store.setHomeView(view)
    },

    /**
     * 使用当前相机姿态记住 Home 视图。
     */
    rememberHomeFromCurrent() {
      if (!hasHomeApi) return null
      const viewer = viewerOrThrow()
      const snapshot = snapshotFromCamera(viewer.camera)
      store.setHomeView(snapshot)
      return snapshot
    },

    /**
     * 读取 store 中缓存的 Home 视图。
     */
    getHomeView() {
      if (!hasHomeApi) return null
      return store.getHomeView()
    },

    /**
     * 飞回 Home 视图，可覆写部分参数。
     */
    async flyToHome(options) {
      if (!hasHomeApi) return null
      const viewer = viewerOrThrow()
      const home = store.getHomeView()
      if (!home) return null
      const overrides = options || {}
      const target = {
        lon: overrides.lon != null ? overrides.lon : home.lon,
        lat: overrides.lat != null ? overrides.lat : home.lat,
        height: overrides.height != null ? overrides.height : home.height,
        heading: overrides.heading != null ? overrides.heading : home.heading,
        pitch: overrides.pitch != null ? overrides.pitch : home.pitch,
        roll: overrides.roll != null ? overrides.roll : home.roll,
        duration: overrides.duration != null ? overrides.duration : 1.5,
      }
      return flyToAsync(viewer.camera, {
        destination: Cesium.Cartesian3.fromDegrees(target.lon, target.lat, target.height),
        orientation: {
          heading: toRad(target.heading || 0),
          pitch: toRad(target.pitch != null ? target.pitch : -45),
          roll: toRad(target.roll || 0),
        },
        duration: target.duration,
      })
    },

    /**
     * 计算轨迹点的经纬度边界范围
     * @private
     * @param {Array<{lon:number, lat:number, height?:number}>} points - 轨迹点集合
     * @returns {[number, number, number, number] | null} [minLon, minLat, maxLon, maxLat]
     */
    _calculateBounds(points) {
      if (!Array.isArray(points) || points.length === 0) return null
      
      let minLon = Number.POSITIVE_INFINITY
      let maxLon = Number.NEGATIVE_INFINITY
      let minLat = Number.POSITIVE_INFINITY
      let maxLat = Number.NEGATIVE_INFINITY
      
      points.forEach(({ lon, lat }) => {
        if (!Number.isFinite(lon) || !Number.isFinite(lat)) return
        minLon = Math.min(minLon, lon)
        maxLon = Math.max(maxLon, lon)
        minLat = Math.min(minLat, lat)
        maxLat = Math.max(maxLat, lat)
      })
      
      if (!Number.isFinite(minLon) || !Number.isFinite(minLat)) return null
      return [minLon, minLat, maxLon, maxLat]
    },

    /**
     * 计算智能高度，考虑视场角、边距和轨迹点高度
     * @private
     * @param {Array<{lon:number, lat:number, height?:number}>} points - 轨迹点集合
     * @param {[number, number, number, number]} bounds - 边界范围
     * @param {Object} config - 配置对象
     * @returns {number} 计算得出的相机高度
     */
    _calculateSmartAltitude(points, bounds, config) {
      const [minLon, minLat, maxLon, maxLat] = bounds
      const centerLat = (minLat + maxLat) / 2
      
      // 确保最小跨度
      const lonSpanDeg = Math.max(maxLon - minLon, config.MIN_SPAN_DEG)
      const latSpanDeg = Math.max(maxLat - minLat, config.MIN_SPAN_DEG)
      
      // 转换为弧度
      const centerLatRad = Cesium.Math.toRadians(centerLat)
      const lonSpanRad = Cesium.Math.toRadians(lonSpanDeg)
      const latSpanRad = Cesium.Math.toRadians(latSpanDeg)
      
      // 计算实际距离（米）
      const equatorialRadius = Cesium.Ellipsoid.WGS84.maximumRadius
      const lonSpanMeters = Math.abs(lonSpanRad * equatorialRadius * Math.cos(centerLatRad))
      const latSpanMeters = Math.abs(latSpanRad * equatorialRadius)
      const horizontalSpan = Math.max(lonSpanMeters, latSpanMeters, config.MIN_SPAN_METERS)
      
      // 添加边距
      const spanWithMargin = horizontalSpan * (1 + config.MARGIN_RATIO)
      
      // 根据视场角计算所需高度
      const viewer = viewerOrThrow()
      const camera = viewer.camera
      const verticalFov = camera?.frustum?.fov ?? Cesium.Math.toRadians(config.DEFAULT_FOV)
      const halfFov = Math.max(verticalFov / 2, Cesium.Math.toRadians(config.MIN_FOV))
      const requiredHeight = spanWithMargin / Math.tan(halfFov)
      
      // 计算轨迹点的最大高度
      const maxPointHeight = points.reduce((max, item) => {
        const value = Number.isFinite(item.height) ? item.height : 0
        return Math.max(max, value)
      }, 0)
      
      // 返回安全高度
      return Math.max(requiredHeight + maxPointHeight, maxPointHeight + config.MIN_HEIGHT_OFFSET)
    },
  }
}
