import type { MapLocationPoint, MapPolygon, MapPolyline } from '@/types/Map';

/** GeoJSON 坐标 [经度, 纬度(, 可选高度)] */
export type GeoJsonPosition = [number, number] | [number, number, number];

export type GeoJsonGeometry =
  | { type: 'Polygon'; coordinates: GeoJsonPosition[][] }
  | { type: 'MultiPolygon'; coordinates: GeoJsonPosition[][][] }
  | { type: 'LineString'; coordinates: GeoJsonPosition[] }
  | { type: 'MultiLineString'; coordinates: GeoJsonPosition[][] }
  | { type: 'GeometryCollection'; geometries: GeoJsonGeometry[] }
  | { type: string; coordinates?: unknown; geometries?: unknown };

export interface GeoJsonFeature {
  type: 'Feature';
  properties?: Record<string, unknown> | null;
  geometry: GeoJsonGeometry | null;
}

export interface GeoJsonFeatureCollection {
  type: 'FeatureCollection';
  features: GeoJsonFeature[];
}

export interface GeoJsonToWechatMapOptions {
  fillColor?: string;
  strokeColor?: string;
  strokeWidth?: number;
  /** LineString / MultiLineString 使用的颜色 */
  lineColor?: string;
  lineWidth?: number;
  level?: MapPolygon['level'];
  /** 仅转换满足的要素（例如按 adcode 过滤） */
  featureFilter?: (feature: GeoJsonFeature) => boolean;
}

const defaultOptions: Required<
  Pick<
    GeoJsonToWechatMapOptions,
    'fillColor' | 'strokeColor' | 'strokeWidth' | 'lineColor' | 'lineWidth' | 'level'
  >
> = {
  fillColor: '#3388ff33',
  strokeColor: '#3388ff',
  strokeWidth: 1,
  lineColor: '#3388ff',
  lineWidth: 2,
  level: 'abovelabels',
};

function lngLatToPoint(lngLat: GeoJsonPosition): MapLocationPoint {
  return { longitude: lngLat[0], latitude: lngLat[1] };
}

/** GeoJSON 多边形环通常为闭合环；若未闭合则补全，满足 map 组件闭合多边形要求 */
function ringToClosedPolygonPoints(ring: GeoJsonPosition[]): MapLocationPoint[] {
  if (ring.length < 3) return [];
  const pts = ring.map(lngLatToPoint);
  const first = pts[0];
  const last = pts[pts.length - 1];
  if (first.longitude !== last.longitude || first.latitude !== last.latitude) {
    pts.push({ latitude: first.latitude, longitude: first.longitude });
  }
  return pts;
}

function appendPolygonOuterRing(
  rings: GeoJsonPosition[][] | undefined,
  o: typeof defaultOptions,
  polygons: MapPolygon[]
): void {
  if (!rings?.length) return;
  const outer = rings[0];
  const points = ringToClosedPolygonPoints(outer);
  if (points.length < 4) return;
  polygons.push({
    points,
    fillColor: o.fillColor,
    strokeColor: o.strokeColor,
    strokeWidth: o.strokeWidth,
    level: o.level,
  });
}

function appendLine(coords: GeoJsonPosition[] | undefined, o: typeof defaultOptions, polylines: MapPolyline[]): void {
  if (!coords?.length) return;
  const points = coords.map(lngLatToPoint);
  if (points.length < 2) return;
  polylines.push({
    points,
    color: o.lineColor,
    width: o.lineWidth,
    level: o.level,
  });
}

function appendFromGeometry(geometry: GeoJsonGeometry | null, o: typeof defaultOptions, polygons: MapPolygon[], polylines: MapPolyline[]): void {
  if (!geometry) return;
  switch (geometry.type) {
    case 'Polygon':
      appendPolygonOuterRing(geometry.coordinates as GeoJsonPosition[][], o, polygons);
      break;
    case 'MultiPolygon':
      for (const polygonCoords of (geometry.coordinates as GeoJsonPosition[][][])) {
        appendPolygonOuterRing(polygonCoords, o, polygons);
      }
      break;
    case 'LineString':
      appendLine(geometry.coordinates as GeoJsonPosition[], o, polylines);
      break;
    case 'MultiLineString':
      for (const line of (geometry.coordinates as GeoJsonPosition[][])) {
        appendLine(line as GeoJsonPosition[], o, polylines);
      }
      break;
    case 'GeometryCollection':
      for (const g of (geometry.geometries as GeoJsonGeometry[])) {
        appendFromGeometry(g, o, polygons, polylines);
      }
      break;
    default:
      break;
  }
}

function mergeOptions(options?: GeoJsonToWechatMapOptions): typeof defaultOptions {
  return {
    ...defaultOptions,
    ...options,
    level: options?.level ?? defaultOptions.level,
  };
}

/**
 * 将 GeoJSON（FeatureCollection 或单个 Feature）转为微信小程序 map 的 polygons / polyline 数据源。
 * - 坐标系：与数据一致（国内区划数据多为 GCJ-02，与微信 map 一致）。
 * - GeoJSON 坐标顺序为 [longitude, latitude]。
 * - Polygon / MultiPolygon：仅取外环填充并描边；洞内环（hole）小程序 map 无法用单 polygon 表达，当前版本忽略。
 */
export function geoJsonToWechatMapShapes(
  data: unknown,
  options?: GeoJsonToWechatMapOptions
): { polygons: MapPolygon[]; polylines: MapPolyline[] } {
  const o = mergeOptions(options);
  const polygons: MapPolygon[] = [];
  const polylines: MapPolyline[] = [];

  if (!data || typeof data !== 'object') {
    return { polygons, polylines };
  }

  const root = data as Record<string, unknown>;

  if (root.type === 'FeatureCollection' && Array.isArray(root.features)) {
    for (const f of root.features as GeoJsonFeature[]) {
      if (options?.featureFilter && !options.featureFilter(f)) continue;
      appendFromGeometry(f.geometry, o, polygons, polylines);
    }
  } else if (root.type === 'Feature') {
    const f = root as unknown as GeoJsonFeature;
    if (options?.featureFilter && !options.featureFilter(f)) {
      return { polygons, polylines };
    }
    appendFromGeometry(f.geometry, o, polygons, polylines);
  }

  return { polygons, polylines };
}
