/**
 * 空间查询工具函数
 * 实现PostGIS风格的空间查询和几何计算功能
 */

import { SpatialField } from './spatialQueryReducer';

// 地球半径（米）
const EARTH_RADIUS = 6371000;

// 将角度转换为弧度
const toRadians = (degrees: number): number => degrees * Math.PI / 180;

// 将弧度转换为角度
const toDegrees = (radians: number): number => radians * 180 / Math.PI;

/**
 * 计算两点之间的距离（Haversine公式）
 */
export function calculateDistance(
  lat1: number, lng1: number,
  lat2: number, lng2: number
): number {
  const dLat = toRadians(lat2 - lat1);
  const dLng = toRadians(lng2 - lng1);

  const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
    Math.sin(dLng / 2) * Math.sin(dLng / 2);

  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

  return EARTH_RADIUS * c; // 返回米
}

/**
 * 判断点是否在多边形内（射线法）
 */
export function isPointInPolygon(
  point: { lat: number; lng: number },
  polygon: number[][]
): boolean {
  let inside = false;
  const x = point.lng;
  const y = point.lat;

  for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
    const xi = polygon[i][0], yi = polygon[i][1];
    const xj = polygon[j][0], yj = polygon[j][1];

    const intersect = ((yi > y) !== (yj > y))
      && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
    if (intersect) inside = !inside;
  }

  return inside;
}

/**
 * 判断两个多边形是否相交
 */
export function doPolygonsIntersect(
  polygon1: number[][],
  polygon2: number[][]
): boolean {
  // 简化判断：检查是否有顶点在另一个多边形内
  for (const point of polygon1) {
    if (isPointInPolygon({ lat: point[1], lng: point[0] }, polygon2)) {
      return true;
    }
  }

  for (const point of polygon2) {
    if (isPointInPolygon({ lat: point[1], lng: point[0] }, polygon1)) {
      return true;
    }
  }

  return false;
}

/**
 * 判断两个多边形是否相邻（共享边界）
 */
export function arePolygonsAdjacent(
  polygon1: number[][],
  polygon2: number[][]
): boolean {
  // 检查是否有共享的边界点（简化实现）
  for (let i = 0; i < polygon1.length - 1; i++) {
    for (let j = 0; j < polygon2.length - 1; j++) {
      // 检查是否有相同的边
      const edge1 = [polygon1[i], polygon1[i + 1]];
      const edge2 = [polygon2[j], polygon2[j + 1]];

      if ((edgesEqual(edge1, edge2) || edgesEqual(edge1, [edge2[1], edge2[0]]))) {
        return true;
      }
    }
  }

  return false;
}

function edgesEqual(edge1: number[][], edge2: number[][]): boolean {
  return (
    (pointsEqual(edge1[0], edge2[0]) && pointsEqual(edge1[1], edge2[1])) ||
    (pointsEqual(edge1[0], edge2[1]) && pointsEqual(edge1[1], edge2[0]))
  );
}

function pointsEqual(p1: number[], p2: number[]): boolean {
  return Math.abs(p1[0] - p2[0]) < 1e-9 && Math.abs(p1[1] - p2[1]) < 1e-9;
}

/**
 * 创建缓冲区（简化的圆形缓冲区）
 */
export function createBuffer(
  center: { lat: number; lng: number },
  radius: number // 米
): number[][] {
  const points: number[][] = [];
  const numPoints = 36; // 36个点形成近似圆形

  for (let i = 0; i < numPoints; i++) {
    const angle = (i / numPoints) * 2 * Math.PI;
    const bearing = toDegrees(angle);

    const point = calculateDestinationPoint(center, bearing, radius);
    points.push([point.lng, point.lat]);
  }

  return points;
}

/**
 * 计算目标点（从起点按方位角和距离计算）
 */
function calculateDestinationPoint(
  start: { lat: number; lng: number },
  bearing: number,
  distance: number
): { lat: number; lng: number } {
  const lat1 = toRadians(start.lat);
  const lng1 = toRadians(start.lng);
  const brng = toRadians(bearing);

  const angularDistance = distance / EARTH_RADIUS;

  const lat2 = Math.asin(
    Math.sin(lat1) * Math.cos(angularDistance) +
    Math.cos(lat1) * Math.sin(angularDistance) * Math.cos(brng)
  );

  const lng2 = lng1 + Math.atan2(
    Math.sin(brng) * Math.sin(angularDistance) * Math.cos(lat1),
    Math.cos(angularDistance) - Math.sin(lat1) * Math.sin(lat2)
  );

  return {
    lat: toDegrees(lat2),
    lng: toDegrees(lng2)
  };
}

/**
 * 计算多边形面积（L'Huilier定理，球面几何）
 */
export function calculatePolygonArea(polygon: number[][]): number {
  if (polygon.length < 3) return 0;

  let totalArea = 0;

  // 将多边形分割为三角形计算面积
  for (let i = 1; i < polygon.length - 1; i++) {
    const triangle = [polygon[0], polygon[i], polygon[i + 1]];
    const area = calculateTriangleArea(triangle);
    totalArea += area;
  }

  return totalArea;
}

/**
 * 计算三角形面积（球面几何）
 */
function calculateTriangleArea(triangle: number[][]): number {
  const [a, b, c] = triangle;

  // 将经纬度转换为笛卡尔坐标进行计算（简化实现）
  const area = Math.abs(
    (a[0] * (b[1] - c[1]) + b[0] * (c[1] - a[1]) + c[0] * (a[1] - b[1])) / 2
  );

  // 转换为平方米（粗略转换）
  return area * 111320 * 111320; // 1度 ≈ 111320米
}

/**
 * 计算多边形周长
 */
export function calculatePolygonPerimeter(polygon: number[][]): number {
  if (polygon.length < 2) return 0;

  let perimeter = 0;

  for (let i = 0; i < polygon.length - 1; i++) {
    const p1 = polygon[i];
    const p2 = polygon[i + 1];
    perimeter += calculateDistance(p1[1], p1[0], p2[1], p2[0]);
  }

  // 闭合多边形，计算最后一条边
  if (polygon.length > 2) {
    const first = polygon[0];
    const last = polygon[polygon.length - 1];
    perimeter += calculateDistance(last[1], last[0], first[1], first[0]);
  }

  return perimeter;
}

/**
 * 计算多边形中心点
 */
export function calculatePolygonCentroid(polygon: number[][]): { lat: number; lng: number } {
  let sumLat = 0;
  let sumLng = 0;

  for (const point of polygon) {
    sumLat += point[1];
    sumLng += point[0];
  }

  return {
    lat: sumLat / polygon.length,
    lng: sumLng / polygon.length
  };
}

/**
 * 计算多边形边界框
 */
export function calculatePolygonBounds(polygon: number[][]): {
  north: number;
  south: number;
  east: number;
  west: number;
} {
  let minLat = Infinity, maxLat = -Infinity;
  let minLng = Infinity, maxLng = -Infinity;

  for (const point of polygon) {
    minLat = Math.min(minLat, point[1]);
    maxLat = Math.max(maxLat, point[1]);
    minLng = Math.min(minLng, point[0]);
    maxLng = Math.max(maxLng, point[0]);
  }

  return {
    north: maxLat,
    south: minLat,
    east: maxLng,
    west: minLng
  };
}

/**
 * 空间查询：点在多边形内
 */
export function queryPointInPolygon(
  point: { lat: number; lng: number },
  fields: SpatialField[]
): SpatialField[] {
  return fields.filter(field => {
    const polygon = field.geometry.coordinates[0];
    return isPointInPolygon(point, polygon);
  });
}

/**
 * 空间查询：多边形相交
 */
export function queryPolygonIntersect(
  queryPolygon: number[][],
  fields: SpatialField[]
): SpatialField[] {
  return fields.filter(field => {
    const polygon = field.geometry.coordinates[0];
    return doPolygonsIntersect(queryPolygon, polygon);
  });
}

/**
 * 空间查询：多边形相邻
 */
export function queryPolygonAdjacent(
  queryPolygon: number[][],
  fields: SpatialField[]
): SpatialField[] {
  return fields.filter(field => {
    const polygon = field.geometry.coordinates[0];
    return arePolygonsAdjacent(queryPolygon, polygon);
  });
}

/**
 * 空间查询：缓冲区查询
 */
export function queryBuffer(
  center: { lat: number; lng: number },
  radius: number,
  fields: SpatialField[]
): SpatialField[] {
  const bufferPolygon = createBuffer(center, radius);

  return fields.filter(field => {
    // 检查地块中心点是否在缓冲区内
    return isPointInPolygon(field.centroid, bufferPolygon);
  });
}

/**
 * 生成GeoJSON格式数据
 */
export function generateGeoJSON(fields: SpatialField[]): string {
  const features = fields.map(field => ({
    type: 'Feature',
    properties: {
      id: field.id,
      code: field.code,
      name: field.name,
      area: field.area,
      perimeter: field.perimeter,
      soilType: field.soilType,
      plantingMode: field.plantingMode,
      status: field.status,
      owner: field.owner,
      createdAt: field.createdAt
    },
    geometry: field.geometry
  }));

  return JSON.stringify({
    type: 'FeatureCollection',
    features
  }, null, 2);
}

/**
 * 生成KML格式数据
 */
export function generateKML(fields: SpatialField[]): string {
  let kml = `<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>Spatial Query Results</name>
`;

  for (const field of fields) {
    const coordinates = field.geometry.coordinates[0]
      .map(coord => `${coord[0]},${coord[1]},0`)
      .join(' ');

    kml += `    <Placemark>
      <name>${field.name}</name>
      <description>
        <![CDATA[
          <strong>编号:</strong> ${field.code}<br/>
          <strong>面积:</strong> ${field.area.toFixed(2)} 亩<br/>
          <strong>土壤类型:</strong> ${field.soilType}<br/>
          <strong>种植模式:</strong> ${field.plantingMode}<br/>
          <strong>负责人:</strong> ${field.owner}
        ]]>
      </description>
      <Polygon>
        <outerBoundaryIs>
          <LinearRing>
            <coordinates>${coordinates}</coordinates>
          </LinearRing>
        </outerBoundaryIs>
      </Polygon>
    </Placemark>
`;
  }

  kml += `  </Document>
</kml>`;

  return kml;
}

/**
 * 生成CSV格式数据
 */
export function generateCSV(fields: SpatialField[]): string {
  const headers = [
    'ID', '编号', '名称', '面积(亩)', '周长(米)', '中心纬度', '中心经度',
    '土壤类型', '种植模式', '状态', '负责人', '创建时间'
  ];

  const rows = fields.map(field => [
    field.id,
    field.code,
    field.name,
    field.area.toFixed(2),
    field.perimeter.toFixed(0),
    field.centroid.lat.toFixed(6),
    field.centroid.lng.toFixed(6),
    field.soilType,
    field.plantingMode,
    field.status,
    field.owner,
    field.createdAt
  ]);

  return [headers, ...rows].map(row => row.join(',')).join('\n');
}

/**
 * 生成SQL查询示例
 */
export function generateSQLExample(
  queryType: string,
  geometry: any,
  bufferDistance?: number
): string {
  switch (queryType) {
    case 'point-in-polygon':
      return `-- 点在多边形内查询
SELECT * FROM fields
WHERE ST_Contains(
  ST_GeomFromText('POLYGON((${geometry.map((p: any) => `${p[0]} ${p[1]}`).join(', ')}))', 4326),
  ST_GeomFromText('POINT(${geometry.lng} ${geometry.lat})', 4326)
);`;

    case 'polygon-intersect':
      return `-- 多边形相交查询
SELECT * FROM fields
WHERE ST_Intersects(
  geometry,
  ST_GeomFromText('POLYGON((${geometry.map((p: any) => `${p[0]} ${p[1]}`).join(', ')}))', 4326)
);`;

    case 'polygon-adjacent':
      return `-- 多边形相邻查询
SELECT * FROM fields
WHERE ST_Touches(
  geometry,
  ST_GeomFromText('POLYGON((${geometry.map((p: any) => `${p[0]} ${p[1]}`).join(', ')}))', 4326)
);`;

    case 'buffer':
      return `-- 缓冲区查询
SELECT * FROM fields
WHERE ST_DWithin(
  geometry,
  ST_GeomFromText('POINT(${geometry.lng} ${geometry.lat})', 4326),
  ${bufferDistance}
);`;

    default:
      return '-- 请选择查询类型';
  }
}