/**
 * OpenLayers 地图工具函数集合
 * 提供地图要素查询、选择和处理功能
 *
 * @module mapQueryService
 */

import { GeoJSON } from "ol/format";
import { Feature } from "ol";
import { Point, LineString, Polygon } from "ol/geom";
import Geometry from "ol/geom/Geometry";
import TileState from "ol/TileState";
import { toLonLat } from "ol/proj";
import { booleanIntersects, buffer, point, toWgs84 } from "@turf/turf";
import config from "@config/config";
import RenderFeature from "ol/render/Feature";
import Map from "ol/Map";
import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile";
import VectorTileSource from "ol/source/VectorTile";
import VectorLayer from "ol/layer/Vector";

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

/**
 * 几何类型枚举
 */
enum GeometryType {
  POINT = "Point",
  LINE_STRING = "LineString",
  POLYGON = "Polygon",
}

/**
 * 地图点击事件
 */
interface MapClickEvent {
  coordinate: number[];
}

// ========== 常量配置 ==========

/**
 * GeoServer 服务配置获取函数
 */
const getGeoserverConfig = () => ({
  url: config.MAP_URL,
  workspace: config.MAP_WORKSPACE,
  layers: ["geo_pipes_mat", "geo_junctions_mat", "geo_valves"],
  wfsVersion: "1.0.0",
  outputFormat: "application/json",
});

/**
 * 地图交互配置
 */
const MAP_CONFIG = {
  hitTolerance: 5, // 像素容差
  bufferUnits: "meters" as const,
} as const;

const isQueryableVectorLayer = (layer: unknown): layer is VectorLayer => {
  return layer instanceof VectorLayer && layer.get("queryable") !== false;
};

// ========== 辅助函数 ==========

/**
 * 将扁平坐标数组转换为点坐标
 * @param flatCoordinates 扁平坐标数组
 * @returns 点坐标 [x, y]
 */
const flatCoordinatesToPoint = (flatCoordinates: number[]): number[] => {
  return [flatCoordinates[0], flatCoordinates[1]];
};

/**
 * 将扁平坐标数组转换为线坐标
 * @param flatCoordinates 扁平坐标数组
 * @returns 线坐标数组 [[x1, y1], [x2, y2], ...]
 */
const flatCoordinatesToLineString = (flatCoordinates: number[]): number[][] => {
  const lineCoords: number[][] = [];
  for (let i = 0; i < flatCoordinates.length; i += 2) {
    lineCoords.push([flatCoordinates[i], flatCoordinates[i + 1]]);
  }
  return lineCoords;
};

/**
 * 将扁平坐标数组转换为多边形坐标
 * @param flatCoordinates 扁平坐标数组
 * @param ends 环的结束位置数组
 * @returns 多边形坐标数组 [[[x1, y1], [x2, y2], ...]]
 */
const flatCoordinatesToPolygon = (
  flatCoordinates: number[],
  ends: number[]
): number[][][] => {
  const rings: number[][][] = [];
  let start = 0;

  for (const end of ends) {
    const ring: number[][] = [];
    for (let i = start; i < end; i += 2) {
      ring.push([flatCoordinates[i], flatCoordinates[i + 1]]);
    }
    rings.push(ring);
    start = end;
  }
  return rings;
};

/**
 * 将 RenderFeature 转换为标准 Feature
 * @param renderFeature OpenLayers 渲染要素
 * @returns 标准 Feature 对象或 undefined
 */
const convertRenderFeatureToFeature = (
  renderFeature: RenderFeature
): Feature | undefined => {
  if (!renderFeature) {
    return undefined;
  }

  const geometry = renderFeature.getGeometry();
  if (!geometry) {
    return undefined;
  }

  try {
    let clonedGeometry: Geometry;

    // 检查是否为标准几何体
    if (geometry instanceof Geometry) {
      clonedGeometry = geometry;
    } else {
      // 处理 RenderFeature 的几何体
      const type = geometry.getType();
      const flatCoordinates = geometry.getFlatCoordinates();

      switch (type) {
        case GeometryType.POINT:
          clonedGeometry = new Point(flatCoordinatesToPoint(flatCoordinates));
          break;

        case GeometryType.LINE_STRING:
          clonedGeometry = new LineString(
            flatCoordinatesToLineString(flatCoordinates)
          );
          break;

        case GeometryType.POLYGON:
          const ends = (geometry as any).getEnds?.() || [
            flatCoordinates.length,
          ];
          clonedGeometry = new Polygon(
            flatCoordinatesToPolygon(flatCoordinates, ends)
          );
          break;

        default:
          console.warn(`不支持的几何体类型: ${type}`);
          return undefined;
      }
    }

    return new Feature({
      geometry: clonedGeometry,
      ...renderFeature.getProperties(),
    });
  } catch (error) {
    console.error("RenderFeature 转换为 Feature 时出错:", error);
    return undefined;
  }
};
/**
 * 构建 WFS 查询 URL
 * @param layer 图层名称
 * @param orFilter CQL 过滤条件
 * @returns WFS 查询 URL
 */
const buildWfsUrl = (layer: string, orFilter: string): string => {
  const { url, workspace, wfsVersion, outputFormat } = getGeoserverConfig();
  const params = new URLSearchParams({
    service: "WFS",
    version: wfsVersion,
    request: "GetFeature",
    typeName: `${workspace}:${layer}`,
    outputFormat: outputFormat,
    CQL_FILTER: orFilter,
  });
  return `${url}/${workspace}/ows?${params.toString()}`;
};

/**
 * 从指定图层查询要素
 * @param layer 图层名称
 * @param orFilter CQL 过滤条件
 * @returns GeoJSON Feature 数组
 */
const fetchFeaturesFromLayer = async (
  layer: string,
  orFilter: string
): Promise<Feature[]> => {
  try {
    const url = buildWfsUrl(layer, orFilter);
    const response = await fetch(url);

    if (!response.ok) {
      throw new Error(`HTTP 错误: ${response.status} ${response.statusText}`);
    }

    const json = await response.json();
    return new GeoJSON().readFeatures(json);
  } catch (error) {
    console.error(`图层 ${layer} 查询失败:`, error);
    return [];
  }
};

/**
 * 根据 ID 列表通过 GeoServer WFS 服务查询要素
 * @param ids 要素 ID 数组
 * @param layer 可选的指定图层名称
 * @returns Feature 数组
 */
const queryFeaturesByIds = async (
  ids: string[],
  layer?: string
): Promise<Feature[]> => {
  const normalizedIds = ids
    .map((id) => String(id).trim())
    .filter((id) => id.length > 0);

  if (!normalizedIds.length) {
    return [];
  }

  const escapedIds = normalizedIds.map((id) => id.replace(/'/g, "''"));
  const orFilter = escapedIds.map((id) => `id='${id}'`).join(" OR ");

  try {
    if (!layer) {
      // 查询所有配置的图层
      const promises = getGeoserverConfig().layers.map((layerName) =>
        fetchFeaturesFromLayer(layerName, orFilter)
      );

      const results = await Promise.all(promises);
      return results.flat();
    } else {
      // 查询指定图层
      return await fetchFeaturesFromLayer(layer, orFilter);
    }
  } catch (error) {
    console.error("根据 IDs 查询要素时出错:", error);
    return [];
  }
};
/**
 * 获取地图上所有 VectorTileSource
 * @param map OpenLayers 地图对象
 * @returns VectorTileSource 数组
 */
const getVectorTileSources = (map: Map): VectorTileSource[] => {
  return map
    .getAllLayers()
    .filter((layer) => layer instanceof WebGLVectorTileLayer)
    .map((layer) => layer.getSource() as VectorTileSource)
    .filter((source) => source !== null);
};

/**
 * 确保缩放级别在有效范围内
 * @param z 原始缩放级别
 * @param minZoom 最小缩放级别
 * @param maxZoom 最大缩放级别
 * @returns 调整后的缩放级别
 */
const clampZoomLevel = (
  z: number,
  minZoom: number,
  maxZoom: number
): number => {
  return Math.max(minZoom, Math.min(maxZoom, z));
};

/**
 * 按几何类型对要素进行分类
 * @param features 要素数组
 * @returns 分类后的要素对象
 */
const classifyFeaturesByGeometry = (
  features: Feature[]
): {
  points: Feature[];
  lines: Feature[];
  others: Feature[];
} => {
  const points: Feature[] = [];
  const lines: Feature[] = [];
  const others: Feature[] = [];

  features.forEach((feature) => {
    const geometryType = feature.getGeometry()?.getType();
    switch (geometryType) {
      case GeometryType.POINT:
        points.push(feature);
        break;
      case GeometryType.LINE_STRING:
        lines.push(feature);
        break;
      default:
        others.push(feature);
    }
  });

  return { points, lines, others };
};

/**
 * 检查要素是否与缓冲区相交
 * @param feature 要素
 * @param buffered 缓冲区几何对象
 * @returns 是否相交
 */
const isFeatureIntersectsBuffer = (
  feature: Feature,
  buffered: any
): boolean => {
  if (!feature || !buffered) {
    return false;
  }

  try {
    const geoJSONGeometry = new GeoJSON().writeGeometryObject(
      feature.getGeometry()!
    );
    const bufferedGeometry = buffered.geometry;
    return booleanIntersects(toWgs84(geoJSONGeometry), bufferedGeometry);
  } catch (error) {
    console.error("要素相交检测失败:", error);
    return false;
  }
};

/**
 * 从 VectorTile 中提取选中的要素
 * @param vectorTiles 矢量瓦片数组
 * @param buffered 缓冲区几何对象
 * @returns 选中的要素数组
 */
const extractSelectedFeatures = (
  vectorTiles: any[],
  buffered: any
): Feature[] => {
  const allFeatures: Feature[] = [];

  vectorTiles.forEach((vectorTile) => {
    if (vectorTile.getState() !== TileState.LOADED) {
      return;
    }

    const renderFeatures = vectorTile.getFeatures();
    const selectedFeatures = renderFeatures
      .map((renderFeature: RenderFeature) =>
        convertRenderFeatureToFeature(renderFeature)
      )
      .filter(
        (feature: Feature | undefined): feature is Feature =>
          feature !== undefined && isFeatureIntersectsBuffer(feature, buffered)
      );

    allFeatures.push(...selectedFeatures);
  });

  return allFeatures;
};

/**
 * 处理地图点击选择要素
 * @param event 地图点击事件
 * @param map OpenLayers 地图对象
 * @returns 选中的第一个要素的 Promise，如果没有选中则返回 null
 */
const handleMapClickSelectFeatures = async (
  event: MapClickEvent,
  map: Map
): Promise<Feature | null> => {
  if (!map) {
    return null;
  }

  const coord = event.coordinate;
  const view = map.getView();
  const projection = view.getProjection();
  const pixelRatio = window.devicePixelRatio;

  // 获取缩放级别并确保为整数
  let z = Math.floor(view.getZoom() || 0) - 1;

  // 存储所有选中的要素
  const allSelectedFeatures: Feature[] = [];
  let isFromVectorLayer = false;

  // 1. 优先处理 VectorLayer - 使用 forEachFeatureAtPixel
  const pixel = map.getPixelFromCoordinate(coord);
  map.forEachFeatureAtPixel(
    pixel,
    (feature) => {
      // 只处理标准 Feature，排除 RenderFeature
      // 使用更宽松的检查：检查是否有 getGeometry 方法且不是 RenderFeature
      if (feature && !(feature instanceof RenderFeature)) {
        allSelectedFeatures.push(feature as Feature);
        isFromVectorLayer = true;
      } else return false; // 继续遍历所有要素
    },
    {
      hitTolerance: MAP_CONFIG.hitTolerance,
      layerFilter: isQueryableVectorLayer,
    }
  );

  // 2. 如果 VectorLayer 中没有找到要素，再处理 VectorTileSource
  if (allSelectedFeatures.length === 0) {
    const vectorTileSources = getVectorTileSources(map);
    for (const vectorTileSource of vectorTileSources) {
      const tileGrid = vectorTileSource.getTileGrid();
      if (!tileGrid) {
        continue;
      }

      // 调整缩放级别到有效范围
      const minZoom = tileGrid.getMinZoom();
      const maxZoom = tileGrid.getMaxZoom();
      z = clampZoomLevel(z, minZoom, maxZoom);

      // 获取瓦片坐标
      const tileCoord = tileGrid.getTileCoordForCoordAndZ(coord, z);
      const resolution = tileGrid.getResolution(tileCoord[0]);

      // 创建点击点的缓冲区
      const hitPoint = point(toLonLat(coord));
      const buffered = buffer(hitPoint, resolution * MAP_CONFIG.hitTolerance, {
        units: MAP_CONFIG.bufferUnits,
      });

      // 获取矢量瓦片
      const vectorRenderTile = vectorTileSource.getTile(
        tileCoord[0],
        tileCoord[1],
        tileCoord[2],
        pixelRatio,
        projection
      );

      const vectorTiles = vectorTileSource.getSourceTiles(
        pixelRatio,
        projection,
        vectorRenderTile
      );

      // 提取选中的要素
      const selectedFeatures = extractSelectedFeatures(vectorTiles, buffered);
      allSelectedFeatures.push(...selectedFeatures);
    }
  }

  // 按几何类型优先级排序：点 > 线 > 其他
  const { points, lines, others } =
    classifyFeaturesByGeometry(allSelectedFeatures);
  const prioritizedFeatures = [
    ...points.reverse(),
    ...lines.reverse(),
    ...others.reverse(),
  ];

  // 获取第一个要素
  const firstFeature = prioritizedFeatures[0];
  if (!firstFeature) {
    return null;
  }

  // 如果要素来自 VectorLayer，直接返回，不需要通过 WFS 查询
  if (isFromVectorLayer) {
    return firstFeature;
  }

  // 如果要素来自 VectorTileSource，需要通过 WFS 查询完整信息
  const queryId = firstFeature.getProperties().id;
  const layerName = firstFeature.getProperties().layer;
  if (layerName === "geo_pipes" || layerName === "geo_junctions") {
    layerName.concat("_mat");
  }
  if (!queryId) {
    return null;
  }

  try {
    const features = await queryFeaturesByIds([queryId], layerName);
    return features[0] || null;
  } catch (error) {
    console.error("查询要素详情失败:", error);
    return null;
  }
};

// ========== 导出 ==========

export { handleMapClickSelectFeatures, queryFeaturesByIds };
export type { MapClickEvent };
