package org.dromara.utils;

import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.constant.GeoJsonConstant;
import org.dromara.facility.domain.dto.geojson.FacFeatureByPoint;
import org.dromara.facility.domain.dto.geojson.FacGeometry;
import org.dromara.facility.domain.dto.geojson.FacGeometryByPoint;
import org.locationtech.jts.geom.*;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author lcj
 * @date 2025/4/24 11:48
 */
public class JSTUtils {

    private static final GeometryFactory geometryFactory = new GeometryFactory();

    /**
     * 获取最近点的名称
     *
     * @param target      目标点
     * @param nameGeoJson 点对象列表
     * @return 最近点的名称
     */
    public static String findNearestText(List<Double> target, List<FacFeatureByPoint> nameGeoJson) {
        Point targetPoint = geometryFactory.createPoint(new Coordinate(target.get(0), target.get(1)));
        FacFeatureByPoint nearestFeature = null;
        double minDistance = Double.MAX_VALUE;
        for (FacFeatureByPoint feature : nameGeoJson) {
            FacGeometryByPoint geometry = feature.getGeometry();
            List<Double> coords = geometry.getCoordinates();
            if (coords != null && coords.size() == 2) {
                Point currentPoint = geometryFactory.createPoint(new Coordinate(coords.get(0), coords.get(1)));
                double distance = targetPoint.distance(currentPoint);
                if (distance < minDistance) {
                    minDistance = distance;
                    nearestFeature = feature;
                }
            }
        }
        if (nearestFeature != null && nearestFeature.getProperties() != null) {
            return nearestFeature.getProperties().getText();
        }
        return null; // 如果没找到合适的点
    }

    /**
     * 点是否在平面内
     *
     * @param planeLists 平面坐标
     * @param pointList  点坐标
     * @return 点是否在平面内
     */
    public static Boolean pointIsWithInPlane(List<List<Double>> planeLists, List<Double> pointList) {
        // 构建平面
        Coordinate[] coordinates = getPlaneCoordinate(planeLists);
        Polygon polygon = geometryFactory.createPolygon(coordinates);
        // 构建待判断点
        Point point = geometryFactory.createPoint(new Coordinate(pointList.get(0), pointList.get(1)));
        // 判断是否在多边形内
        return polygon.contains(point);
    }

    /**
     * 获取平面内点列表集合
     *
     * @param planeLists 平面坐标列表
     * @param pointLists 点坐标列表集合
     * @return 平面内点坐标列表集合
     */
    public static List<List<Double>> getPointInPlaneList(List<List<Double>> planeLists, List<List<Double>> pointLists) {
        // 构建平面
        Coordinate[] coordinates = getPlaneCoordinate(planeLists);
        LinearRing shell = geometryFactory.createLinearRing(coordinates);
        Polygon polygon = geometryFactory.createPolygon(shell);
        // 获取平面内点结合
        return pointLists.stream().filter(pointList -> {
            // 构建待判断点
            Point point = geometryFactory.createPoint(new Coordinate(pointList.get(0), pointList.get(1)));
            // 判断是否在多边形内
            return polygon.contains(point);
        }).toList();
    }

    /**
     * 平面是否在平面内
     *
     * @param referencePlane 参考平面
     * @param comparePlane   比较平面
     * @return 平面是否在平面内
     */
    public static Boolean planeIsWithInPlane(List<List<Double>> referencePlane, List<List<Double>> comparePlane) {
        // 构建参考平面
        Coordinate[] referenceCoordinates = getPlaneCoordinate(referencePlane);
        Polygon referencePolygon = geometryFactory.createPolygon(referenceCoordinates);
        // 构建比较平面
        Coordinate[] compareCoordinates = getPlaneCoordinate(comparePlane);
        Polygon comparePolygon = geometryFactory.createPolygon(compareCoordinates);
        // 判断是否在多边形内
        return referencePolygon.contains(comparePolygon);
    }

    /**
     * 判断两个平面是否相交
     *
     * @param referencePlane 参考平面
     * @param comparePlane   待比较平面
     * @return 平面是否相交
     */
    public static Boolean arePolygonsIntersecting(List<List<Double>> referencePlane, List<List<Double>> comparePlane) {
        // 构建 Polygon A（参考面）
        Coordinate[] coordsA = referencePlane.stream()
            .map(p -> new Coordinate(p.getFirst(), p.get(1)))
            .toArray(Coordinate[]::new);
        Polygon polygonA = geometryFactory.createPolygon(coordsA);
        // 构建 Polygon B（比较面）
        Coordinate[] coordsB = comparePlane.stream()
            .map(p -> new Coordinate(p.getFirst(), p.get(1)))
            .toArray(Coordinate[]::new);
        Polygon polygonB = geometryFactory.createPolygon(coordsB);
        // 使用 JTS 判断是否相交
        return polygonA.intersects(polygonB);
    }

    /**
     * 获取平面坐标数组
     *
     * @param planeLists 平面坐标列表
     * @return 平面坐标数组
     */
    public static Coordinate[] getPlaneCoordinate(List<List<Double>> planeLists) {
        return planeLists.stream().map(planeList ->
                new Coordinate(planeList.getFirst(), planeList.get(1)))
            .toList().toArray(new Coordinate[0]);
    }

    /**
     * 获取二维坐标
     *
     * @param geometry 几何对象
     * @return 二维坐标
     */
    public static List<List<Double>> getTwoDimensionalCoordinates(FacGeometry geometry) {
        String type = geometry.getType();
        List<Object> coordinates = geometry.getCoordinates();
        return switch (type) {
            case GeoJsonConstant.POINT -> throw new ServiceException("点位无法创建方阵", HttpStatus.BAD_REQUEST);
            case GeoJsonConstant.LINE -> coordinates.stream()
                .filter(obj -> obj instanceof List<?>)
                .map(obj -> ((List<?>) obj).stream()
                    .filter(num -> num instanceof Number)
                    .map(num -> ((Number) num).doubleValue())
                    .collect(Collectors.toList()))
                .collect(Collectors.toList());
            case GeoJsonConstant.POLYGON -> coordinates.stream()
                .filter(obj -> obj instanceof List<?>)
                .flatMap(obj -> ((List<?>) obj).stream())
                .filter(pointObj -> pointObj instanceof List<?>)
                .map(pointObj -> ((List<?>) pointObj).stream()
                    .filter(num -> num instanceof Number)
                    .map(num -> ((Number) num).doubleValue())
                    .collect(Collectors.toList()))
                .collect(Collectors.toList());
            default -> throw new ServiceException("暂不支持该类型", HttpStatus.BAD_REQUEST);
        };
    }

    /**
     * 匹配最近的点，获取该点的名称
     *
     * @param polygon 平面
     * @param points  点列表集合
     * @return 最近点的名称
     */
    public static String findNearestPointText(List<List<Double>> polygon, List<FacFeatureByPoint> points) {
        if (polygon == null || polygon.size() < 3 || points == null || points.isEmpty()) {
            return null;
        }
        // 1. 构建 Polygon
        Coordinate[] polygonCoords = polygon.stream()
            .map(coord -> new Coordinate(coord.getFirst(), coord.get(1)))
            .toArray(Coordinate[]::new);
        Polygon jtsPolygon = geometryFactory.createPolygon(polygonCoords);
        // 2. 查找最近点
        FacFeatureByPoint nearestFeature = null;
        double minDistance = Double.MAX_VALUE;
        for (FacFeatureByPoint feature : points) {
            List<Double> coords = feature.getGeometry().getCoordinates();
            if (coords == null || coords.size() != 2) continue;
            Point point = geometryFactory.createPoint(new Coordinate(coords.get(0), coords.get(1)));
            double distance = point.distance(jtsPolygon);
            if (distance < minDistance) {
                minDistance = distance;
                nearestFeature = feature;
            }
        }
        // 3. 返回最近点的 text
        if (nearestFeature != null && nearestFeature.getProperties() != null) {
            return nearestFeature.getProperties().getText();
        }
        return null;
    }

}
