/**
 * LayerFactory - 旧版 GeoScene 图层配置 → MapLibre 样式规范转换器
 *
 * 将原 GeoScene 项目中 jsonToLayerFactory.js 的图层配置对象
 * 转换为 maplibregl 兼容的 style spec（source + layer 组合）。
 * 支持的图层类型与原工厂保持一致。
 *
 * @module map/LayerFactory
 */

import type { GeoJSON } from 'geojson'
import maplibregl from 'maplibre-gl'
import type {
  SourceSpecification,
  RasterSourceSpecification,
  VectorSourceSpecification,
  GeoJSONSourceSpecification,
  LayerSpecification,
  RasterLayerSpecification,
} from 'maplibre-gl'
import {
  createTdtSource,
  type TdtSourceOptions,
  type TdtSourceResult,
} from './sources/tdt-source'

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

/**
 * 旧版 GeoScene 图层配置（jsonToLayerFactory 输入格式）
 *
 * 对应原 GeoScene 项目中各个图层构造函数所需的 options 参数。
 * type 字段决定使用哪种转换策略。
 */
export interface OldLayerConfig {
  /**
   * 图层类型标识（必填）
   *
   * - `'TdtLayer'`: 天地图 WMTS 图层
   * - `'TileLayer'`: ArcGIS 切片服务 / 标准 XYZ 瓦片
   * - `'MapImageLayer'`: ArcGIS 动态地图服务
   * - `'VectorTileLayer'`: 矢量瓦片图层（Mapbox Vector Tile）
   * - `'WMTSLayer'`: 通用 WMTS 图层（OGC 标准）
   * - `'GeoJSONLayer'`: GeoJSON 图层（FeatureLayer）
   */
  type: 'TdtLayer' | 'TileLayer' | 'MapImageLayer' | 'VectorTileLayer' | 'WMTSLayer' | 'GeoJSONLayer'

  /** 图层 ID，缺失时自动生成 */
  id?: string

  /** 图层标题/名称 */
  title?: string

  /** 初始可见性 */
  visible?: boolean

  /** 图层不透明度 (0-1) */
  opacity?: number

  /** 最小可见缩放级别 */
  minZoom?: number

  /** 最大可见缩放级别 */
  maxZoom?: number

  // ---- TdtLayer 专用 ----
  /** 天地图样式（vec/img/ter/cva/cia/cta） */
  style?: string
  /** 天地图瓦片矩阵集（c=经纬度, w=墨卡托） */
  matrix?: string
  /** 天地图 API 密钥 */
  tk?: string
  /** 天地图服务地址 */
  url?: string
  /** 天地图子域列表 */
  subdomains?: string[]

  // ---- TileLayer / MapImageLayer / WMTSLayer 专用 ----
  /** 服务 URL 或 URL 模板 */
  urlTemplate?: string

  // ---- VectorTileLayer 专用 ----
  /** 矢量瓦片样式 URL 或内联样式对象 */
  styleUrl?: string | object

  // ---- GeoJSONLayer 专用 ----
  /** GeoJSON 数据 URL 或内联对象 */
  data?: string | GeoJSON.GeoJSON
  /** 矢量图层样式配置 */
  layerConfig?: GeoJSONLayerStyleConfig

  // ---- 通用扩展 ----
  /** 其他任意配置，透传处理 */
  [key: string]: unknown
}

/**
 * GeoJSON 图层渲染样式配置
 */
export interface GeoJSONLayerStyleConfig {
  /** 渲染类型 */
  type?: 'line' | 'fill' | 'circle' | 'symbol'
  /** 线宽 */
  lineWidth?: number
  /** 线颜色 */
  lineColor?: string
  /** 线不透明度 */
  lineOpacity?: number
  /** 填充颜色 */
  fillColor?: string
  /** 填充不透明度 */
  fillOpacity?: number
  /** 圆形半径 */
  circleRadius?: number
  /** 圆形颜色 */
  circleColor?: string
  /** 圆形不透明度 */
  circleOpacity?: number
  /** 圆形描边宽度 */
  circleStrokeWidth?: number
  /** 圆形描边颜色 */
  circleStrokeColor?: string
  /** 其他 paint/layout 属性 */
  [key: string]: unknown
}

/**
 * LayerFactory 返回结果
 */
export interface LayerFactoryResult {
  /**
   * 源配置列表（按顺序通过 map.addSource 添加）
   */
  sources: Array<{ id: string; config: SourceSpecification }>

  /**
   * 图层配置列表（按顺序通过 map.addLayer 添加）
   */
  layers: LayerSpecification[]

  /**
   * 源图层名称（用于追踪）
   */
  originalType: string
}

// ============================================================
// 内部常量
// ============================================================

/**
 * ArcGIS 服务标准导出参数
 */
const ARCGIS_EXPORT_PARAMS = {
  f: 'image',
  format: 'png32',
  transparent: 'true',
  dpi: '96',
}

// ============================================================
// ID 生成
// ============================================================

/** ID 自增计数器 */
let idCounter = 0

/**
 * 生成唯一 ID
 */
function generateId(prefix: string): string {
  idCounter += 1
  return `${prefix}-${idCounter}-${Date.now().toString(36)}`
}

// ============================================================
// 各类型转换函数
// ============================================================

/**
 * 转换天地图图层 (TdtLayer)
 *
 * 委托给 {@link createTdtSource} 完成实际转换。
 */
function convertTdtLayer(config: OldLayerConfig): LayerFactoryResult {
  const tdtOptions: TdtSourceOptions = {
    style: (config.style as TdtSourceOptions['style']) ?? 'vec',
    matrix: (config.matrix as TdtSourceOptions['matrix']) ?? 'c',
    tk: config.tk,
    url: config.url,
    subdomains: config.subdomains,
    minzoom: config.minZoom ?? 0,
    maxzoom: config.maxZoom ?? 18,
    layer: {
      layout: {
        visibility: config.visible === false ? 'none' : 'visible',
      },
      paint: config.opacity !== undefined
        ? { 'raster-opacity': config.opacity }
        : undefined,
      metadata: {
        title: config.title,
      },
    } as RasterLayerSpecification,
  }

  const result: TdtSourceResult = createTdtSource(tdtOptions)

  return {
    sources: [{ id: result.sourceId, config: result.source }],
    layers: [result.layer],
    originalType: 'TdtLayer',
  }
}

/**
 * 转换 ArcGIS 切片服务图层 (TileLayer)
 *
 * ArcGIS 切片服务（MapServer/tile/{z}/{y}/{x}）→ raster source + layer
 */
function convertTileLayer(config: OldLayerConfig): LayerFactoryResult {
  const layerId = config.id || generateId('tile')
  const sourceId = `${layerId}-source`
  const url = config.urlTemplate || config.url || ''

  // ArcGIS 切片服务 URL 格式：{serviceUrl}/tile/{z}/{y}/{x}
  // 如果已经是 {z}/{x}/{y} 模板，则直接使用
  const tileUrl: string = url.includes('{z}')
    ? url
    : url.replace(/\/?$/, '/tile/{z}/{y}/{x}')

  const source: RasterSourceSpecification = {
    type: 'raster',
    tiles: [tileUrl],
    tileSize: 256,
    minzoom: config.minZoom ?? 0,
    maxzoom: config.maxZoom ?? 19,
    attribution: config.title ?? '',
  }

  const layer: RasterLayerSpecification = {
    id: layerId,
    type: 'raster',
    source: sourceId,
    minzoom: config.minZoom ?? 0,
    maxzoom: config.maxZoom ?? 19,
    layout: {
      visibility: config.visible === false ? 'none' : 'visible',
    },
    paint: {
      ...(config.opacity !== undefined ? { 'raster-opacity': config.opacity } : {}),
    },
    metadata: {
      title: config.title ?? layerId,
      type: 'TileLayer',
    },
  }

  return {
    sources: [{ id: sourceId, config: source }],
    layers: [layer],
    originalType: 'TileLayer',
  }
}

/**
 * 转换 ArcGIS 动态地图服务图层 (MapImageLayer)
 *
 * ArcGIS MapServer/export → raster source（单张图片，非瓦片）
 * 注意：MapLibre 的 raster 源需要瓦片，这里通过自定义 scheme 生成单瓦片
 */
function convertMapImageLayer(config: OldLayerConfig): LayerFactoryResult {
  const layerId = config.id || generateId('map-image')
  const sourceId = `${layerId}-source`
  const url = config.urlTemplate || config.url || ''

  // 构建 ArcGIS export 请求 URL 模板
  const params = new URLSearchParams({
    ...ARCGIS_EXPORT_PARAMS,
    bbox: '{bbox-epsg-3857}',
    bboxSR: '3857',
    imageSR: '3857',
    size: '256,256',
  }).toString()

  // ArcGIS MapServer export 端点
  const exportUrl = url.replace(/\/?$/, '/export?' + params)

  const source: RasterSourceSpecification = {
    type: 'raster',
    tiles: [exportUrl],
    tileSize: 256,
    minzoom: config.minZoom ?? 0,
    maxzoom: config.maxZoom ?? 19,
  }

  const layer: RasterLayerSpecification = {
    id: layerId,
    type: 'raster',
    source: sourceId,
    minzoom: config.minZoom ?? 0,
    maxzoom: config.maxZoom ?? 19,
    layout: {
      visibility: config.visible === false ? 'none' : 'visible',
    },
    paint: {
      ...(config.opacity !== undefined ? { 'raster-opacity': config.opacity } : {}),
    },
    metadata: {
      title: config.title ?? layerId,
      type: 'MapImageLayer',
    },
  }

  return {
    sources: [{ id: sourceId, config: source }],
    layers: [layer],
    originalType: 'MapImageLayer',
  }
}

/**
 * 转换矢量瓦片图层 (VectorTileLayer)
 *
 * 支持两种模式：
 * 1. URL 指向 MapLibre 样式 JSON（直接作为 style URL 使用）
 * 2. 直接使用矢量瓦片源 URL
 */
function convertVectorTileLayer(config: OldLayerConfig): LayerFactoryResult {
  const layerId = config.id || generateId('vector-tile')
  const sourceId = `${layerId}-source`
  const url = config.urlTemplate || config.url || ''

  const source: VectorSourceSpecification = {
    type: 'vector',
    tiles: [url],
    minzoom: config.minZoom ?? 0,
    maxzoom: config.maxZoom ?? 14,
  }

  // 返回一个占位图层，实际渲染样式需由调用方根据 vector source 的 source-layer 自行添加
  const layer: LayerSpecification = {
    id: layerId,
    type: 'line',
    source: sourceId,
    'source-layer': '',
    layout: {
      visibility: config.visible === false ? 'none' : 'visible',
    },
    metadata: {
      title: config.title ?? layerId,
      type: 'VectorTileLayer',
      note: '需要调用方根据 source-layer 自行设置渲染样式',
    },
  }

  return {
    sources: [{ id: sourceId, config: source }],
    layers: [layer],
    originalType: 'VectorTileLayer',
  }
}

/**
 * 转换 WMTS 图层 (WMTSLayer)
 *
 * OGC WMTS 标准 → raster source
 * 使用 RESTful 风格的 WMTS URL 模板
 */
function convertWMTSLayer(config: OldLayerConfig): LayerFactoryResult {
  const layerId = config.id || generateId('wmts')
  const sourceId = `${layerId}-source`
  const url = config.urlTemplate || config.url || ''

  // 如果 URL 不含 {z}/{x}/{y}，通常 WMTS RESTful URL 格式为:
  // {serviceUrl}/{layer}/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}
  const tileUrl: string = url.includes('{z}')
    ? url
    : `${url}/{z}/{y}/{x}`

  const source: RasterSourceSpecification = {
    type: 'raster',
    tiles: [tileUrl],
    tileSize: 256,
    minzoom: config.minZoom ?? 0,
    maxzoom: config.maxZoom ?? 19,
  }

  const layer: RasterLayerSpecification = {
    id: layerId,
    type: 'raster',
    source: sourceId,
    minzoom: config.minZoom ?? 0,
    maxzoom: config.maxZoom ?? 19,
    layout: {
      visibility: config.visible === false ? 'none' : 'visible',
    },
    paint: {
      ...(config.opacity !== undefined ? { 'raster-opacity': config.opacity } : {}),
    },
    metadata: {
      title: config.title ?? layerId,
      type: 'WMTSLayer',
    },
  }

  return {
    sources: [{ id: sourceId, config: source }],
    layers: [layer],
    originalType: 'WMTSLayer',
  }
}

/**
 * 转换 GeoJSON 图层 (GeoJSONLayer)
 *
 * GeoJSON 数据源 → source + 对应类型的渲染图层
 */
function convertGeoJSONLayer(config: OldLayerConfig): LayerFactoryResult {
  const layerId = config.id || generateId('geojson')
  const sourceId = `${layerId}-source`
  const styleConfig = config.layerConfig || {}

  // 数据源：支持 URL 和内联 GeoJSON
  const data = config.data || undefined

  let geoJsonData: string | GeoJSON.GeoJSON = ''

  if (typeof data === 'string') {
    // URL 字符串
    geoJsonData = data
  } else if (data && typeof data === 'object') {
    // 内联 GeoJSON 对象
    geoJsonData = data as GeoJSON.GeoJSON
  }

  const source: GeoJSONSourceSpecification = {
    type: 'geojson',
    data: geoJsonData,
  }

  // 根据 layerConfig.type 确定 MapLibre 图层类型
  const renderType = (styleConfig.type as string) || 'line'

  // 构建 paint 属性
  const paint: Record<string, unknown> = {}

  switch (renderType) {
    case 'line':
      if (styleConfig.lineColor) paint['line-color'] = styleConfig.lineColor
      if (styleConfig.lineWidth !== undefined) paint['line-width'] = styleConfig.lineWidth
      if (styleConfig.lineOpacity !== undefined) paint['line-opacity'] = styleConfig.lineOpacity
      break
    case 'fill':
      if (styleConfig.fillColor) paint['fill-color'] = styleConfig.fillColor
      if (styleConfig.fillOpacity !== undefined) paint['fill-opacity'] = styleConfig.fillOpacity
      break
    case 'circle':
      if (styleConfig.circleRadius !== undefined) paint['circle-radius'] = styleConfig.circleRadius
      if (styleConfig.circleColor) paint['circle-color'] = styleConfig.circleColor
      if (styleConfig.circleOpacity !== undefined) paint['circle-opacity'] = styleConfig.circleOpacity
      if (styleConfig.circleStrokeWidth !== undefined) paint['circle-stroke-width'] = styleConfig.circleStrokeWidth
      if (styleConfig.circleStrokeColor) paint['circle-stroke-color'] = styleConfig.circleStrokeColor
      break
    default:
      break
  }

  // 合并其他未映射的 paint 属性
  const knownKeys = [
    'type', 'lineColor', 'lineWidth', 'lineOpacity',
    'fillColor', 'fillOpacity',
    'circleRadius', 'circleColor', 'circleOpacity',
    'circleStrokeWidth', 'circleStrokeColor',
  ]
  for (const key of Object.keys(styleConfig)) {
    if (!knownKeys.includes(key)) {
      paint[key] = styleConfig[key]
    }
  }

  const layer = {
    id: layerId,
    type: renderType as LayerSpecification['type'],
    source: sourceId,
    layout: {
      visibility: config.visible === false ? 'none' : 'visible',
    },
    paint: Object.keys(paint).length > 0 ? (paint as LayerSpecification['paint']) : undefined,
    metadata: {
      title: config.title ?? layerId,
      type: 'GeoJSONLayer',
    },
  } as LayerSpecification

  return {
    sources: [{ id: sourceId, config: source }],
    layers: [layer],
    originalType: 'GeoJSONLayer',
  }
}

// ============================================================
// 类型 → 转换函数映射表
// ============================================================

/**
 * 图层类型与转换函数的映射表
 *
 * 保持与原 jsonToLayerFactory.js 中 `layerConstructors` 一致的
 * 图层类型支持范围。
 */
const TYPE_CONVERTERS: Record<string, (config: OldLayerConfig) => LayerFactoryResult> = {
  TdtLayer: convertTdtLayer,
  TileLayer: convertTileLayer,
  MapImageLayer: convertMapImageLayer,
  VectorTileLayer: convertVectorTileLayer,
  WMTSLayer: convertWMTSLayer,
  GeoJSONLayer: convertGeoJSONLayer,
}

// ============================================================
// 公开 API
// ============================================================

/**
 * 获取支持的图层类型列表
 *
 * @returns 图层类型字符串数组
 */
export function getSupportedLayerTypes(): string[] {
  return Object.keys(TYPE_CONVERTERS)
}

/**
 * 检查给定的图层类型是否受支持
 *
 * @param type - 图层类型字符串
 * @returns 是否支持
 */
export function isLayerTypeSupported(type: string): boolean {
  return type in TYPE_CONVERTERS
}

/**
 * 将旧版 GeoScene 图层配置转换为 MapLibre 样式配置
 *
 * 根据配置中的 `type` 字段自动选择对应的转换策略，
 * 返回 MapLibre 兼容的 source + layer 配置，可直接传入
 * `map.addSource()` 和 `map.addLayer()`。
 *
 * @param config - 旧版图层配置对象（必须有 `type` 字段）
 * @returns 包含 sources 和 layers 数组的转换结果
 * @throws 如果 config 为空或 type 不支持
 *
 * @example
 * ```ts
 * import { convertLayer } from '@/map/LayerFactory'
 *
 * const map = MapFactory.getCurrentMap()
 *
 * // 转换天地图图层
 * const result = convertLayer({
 *   type: 'TdtLayer',
 *   style: 'vec',
 *   matrix: 'c',
 *   tk: 'your-key',
 *   title: '天地图矢量底图',
 * })
 *
 * result.sources.forEach(s => map?.addSource(s.id, s.config))
 * result.layers.forEach(l => map?.addLayer(l))
 *
 * // 转换 GeoJSON 图层
 * const geoResult = convertLayer({
 *   type: 'GeoJSONLayer',
 *   id: 'my-points',
 *   data: '/api/points.geojson',
 *   layerConfig: {
 *     type: 'circle',
 *     circleRadius: 8,
 *     circleColor: '#ff6600',
 *   },
 * })
 * geoResult.sources.forEach(s => map?.addSource(s.id, s.config))
 * geoResult.layers.forEach(l => map?.addLayer(l))
 * ```
 */
export function convertLayer(config: OldLayerConfig): LayerFactoryResult {
  // 校验配置
  if (!config) {
    throw new Error('[LayerFactory] 配置为空，无法转换图层')
  }

  const type = config.type

  if (!type) {
    throw new Error('[LayerFactory] 图层配置缺少 type 字段')
  }

  const converter = TYPE_CONVERTERS[type]

  if (!converter) {
    const supported = Object.keys(TYPE_CONVERTERS).join(', ')
    throw new Error(
      `[LayerFactory] 不支持的图层类型: "${type}"，支持的类型: ${supported}`
    )
  }

  return converter(config)
}

/**
 * 批量转换旧版 GeoScene 图层配置
 *
 * 依次调用 {@link convertLayer} 转换配置数组中的每一个图层，
 * 遇到不支持的类型会跳过并打印警告日志。
 *
 * @param configs - 旧版图层配置数组
 * @returns 转换结果数组（仅包含成功转换的项）
 *
 * @example
 * ```ts
 * const results = convertLayers([
 *   { type: 'TdtLayer', style: 'vec', tk: '...' },
 *   { type: 'TdtLayer', style: 'cva', tk: '...' },
 *   { type: 'GeoJSONLayer', data: '/api/points.json' },
 * ])
 *
 * results.forEach(r => {
 *   r.sources.forEach(s => map?.addSource(s.id, s.config))
 * })
 * results.forEach(r => {
 *   r.layers.forEach(l => map?.addLayer(l))
 * })
 * ```
 */
export function convertLayers(configs: OldLayerConfig[]): LayerFactoryResult[] {
  const results: LayerFactoryResult[] = []

  for (const config of configs) {
    try {
      results.push(convertLayer(config))
    } catch (err) {
      console.warn(
        `[LayerFactory] 跳过图层转换: ${config?.type ?? '未知类型'}`,
        (err as Error).message
      )
    }
  }

  return results
}

/**
 * 将转换结果应用到 MapLibre 地图实例
 *
 * 便捷方法：按先 source 后 layer 的顺序批量添加到地图中。
 *
 * @param map - MapLibre 地图实例
 * @param results - 转换结果（单个或数组）
 * @param beforeId - 在指定图层之前插入（可选）
 *
 * @example
 * ```ts
 * const result = convertLayer(config)
 * applyToMap(map, result)
 * ```
 */
export function applyToMap(
  map: maplibregl.Map,
  results: LayerFactoryResult | LayerFactoryResult[],
  beforeId?: string
): void {
  const list = Array.isArray(results) ? results : [results]

  for (const result of list) {
    for (const src of result.sources) {
      if (!map.getSource(src.id)) {
        map.addSource(src.id, src.config)
      }
    }
    for (const layer of result.layers) {
      if (!map.getLayer(layer.id)) {
        map.addLayer(layer, beforeId)
      }
    }
  }
}
