package com.sckw.core.utils;


import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * Author: donglang
 * Time: 2025-11-15
 * Des: 距离计算
 * Version: 1.0
 */

public class DistanceUtils {

    // 地球半径常量（不同单位）
    private static final double EARTH_RADIUS_KM = 6371;    // 千米（平均半径）
    private static final double EARTH_RADIUS_M = 6371000;  // 米（1km = 1000m）
    private static final double EARTH_RADIUS_MI = 3956;    // 英里（地球平均半径英里数）

    /**
     * 距离单位枚举
     */
    public enum Unit {
        KM("千米", EARTH_RADIUS_KM),    // 千米
        M("米", EARTH_RADIUS_M),        // 米
        MI("英里", EARTH_RADIUS_MI);    // 英里

        private final String desc;
        private final double radius;

        Unit(String desc, double radius) {
            this.desc = desc;
            this.radius = radius;
        }

        public double getRadius() {
            return radius;
        }

        public String getDesc() {
            return desc;
        }
    }

    /**
     * 计算两个经纬度之间的距离（默认单位：千米）
     *
     * @param lon1 第一个点的经度（范围：-180~180）
     * @param lat1 第一个点的纬度（范围：-90~90）
     * @param lon2 第二个点的经度（范围：-180~180）
     * @param lat2 第二个点的纬度（范围：-90~90）
     * @return 距离（保留2位小数，单位：千米）
     */
    public static double calculateDistance(Double lon1, Double lat1, Double lon2, Double lat2) {
        return calculateDistance(lon1, lat1, lon2, lat2, Unit.KM);
    }

    /**
     * 计算两个经纬度之间的距离（指定单位）
     *
     * @param lon1  第一个点的经度（范围：-180~180）
     * @param lat1  第一个点的纬度（范围：-90~90）
     * @param lon2  第二个点的经度（范围：-180~180）
     * @param lat2  第二个点的纬度（范围：-90~90）
     * @param unit  距离单位（KM/M/MI）
     * @return 距离（保留2位小数，单位：指定的unit）
     */
    public static double calculateDistance(Double lon1, Double lat1, Double lon2, Double lat2, Unit unit) {
        // 1. 校验参数是否为null
        if (lon1 == null || lat1 == null || lon2 == null || lat2 == null) {
            return 0.0;
        }

        // 2. 校验经纬度合法性
//        validateCoordinates(lon1, lat1);
//        validateCoordinates(lon2, lat2);

        // 3. 角度转弧度
        double radLon1 = Math.toRadians(lon1);
        double radLat1 = Math.toRadians(lat1);
        double radLon2 = Math.toRadians(lon2);
        double radLat2 = Math.toRadians(lat2);

        // 4. 计算经纬度差值
        double deltaLon = radLon2 - radLon1;
        double deltaLat = radLat2 - radLat1;

        // 5. 应用Haversine公式
        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        // 6. 计算距离并保留2位小数
        double distance = unit.getRadius() * c;
        return new BigDecimal(distance)
                .setScale(2, RoundingMode.HALF_UP)
                .doubleValue();
    }

    /**
     * 校验经纬度是否合法
     *
     * @param longitude 经度（范围：-180 ≤ longitude ≤ 180）
     * @param latitude  纬度（范围：-90 ≤ latitude ≤ 90）
     */
    private static void validateCoordinates(double longitude, double latitude) {
        if (longitude < -180 || longitude > 180) {
            throw new IllegalArgumentException("经度必须在[-180, 180]范围内，当前值：" + longitude);
        }
        if (latitude < -90 || latitude > 90) {
            throw new IllegalArgumentException("纬度必须在[-90, 90]范围内，当前值：" + latitude);
        }
    }

    // 测试示例
    public static void main(String[] args) {
        // 北京：116.403874°E, 39.914885°N
        // 上海：121.473701°E, 31.230416°N
        double lon1 = 116.403874;
        double lat1 = 39.914885;
        double lon2 = 121.473701;
        double lat2 = 31.230416;

        // 计算不同单位的距离
        double distanceKm = calculateDistance(lon1, lat1, lon2, lat2, Unit.KM);
        double distanceM = calculateDistance(lon1, lat1, lon2, lat2, Unit.M);
        double distanceMi = calculateDistance(lon1, lat1, lon2, lat2, Unit.MI);

        System.out.println("北京到上海的距离：");
        System.out.println(distanceKm + " " + Unit.KM.getDesc());  // 约 1067.99 千米
        System.out.println(distanceM + " " + Unit.M.getDesc());    // 约 1067990.00 米
        System.out.println(distanceMi + " " + Unit.MI.getDesc());  // 约 663.62 英里
    }




}
