package com.ylx.common.utils;

import cn.hutool.core.util.ObjectUtil;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * Haversine公式计算两点经纬度球面直线距离工具类
 * 所有坐标入参统一使用 BigDecimal，保证数据库高精度存储兼容
 */
public final class DistanceUtil {

    // 地球平均半径，单位：米
    private static final double EARTH_RADIUS_M = 6_371_000D;
    // 角度转弧度系数
    private static final double TO_RADIANS = Math.PI / 180.0D;
    // 米转公里换算值
    private static final BigDecimal METER_TO_KM = new BigDecimal("1000");
    // 经纬度极值常量
    private static final BigDecimal MAX_LAT = new BigDecimal("90");
    private static final BigDecimal MAX_LON = new BigDecimal("180");
    private static final BigDecimal ZERO = BigDecimal.ZERO;

    // 私有构造，禁止实例化工具类
    private DistanceUtil() {
        throw new AssertionError("工具类不可实例化");
    }

    // ===================== 对外业务方法 =====================
    /**
     * 获取距离（单位：米，四舍五入整数字符串）
     * @param userLat 用户纬度
     * @param userLon 用户经度
     * @param shopLat 门店纬度
     * @param shopLon 门店经度
     * @return 有效距离返回米数字，坐标为空/0/超出范围 返回 "未知"
     */
    public static String formatDistance(BigDecimal userLat, BigDecimal userLon,
                                        BigDecimal shopLat, BigDecimal shopLon) {
        if (!isAllCoordinateValid(userLat, userLon, shopLat, shopLon)) {
            return "未知";
        }
        double meter = getDistance(userLat.doubleValue(), userLon.doubleValue(),
                shopLat.doubleValue(), shopLon.doubleValue());
        return String.valueOf(Math.round(meter));
    }

    /**
     * 获取距离（单位：公里，保留2位小数）
     * @param userLat 用户纬度
     * @param userLon 用户经度
     * @param shopLat 门店纬度
     * @param shopLon 门店经度
     * @return 公里字符串，坐标非法返回 "未知"
     */
    public static BigDecimal formatDistanceInKilometers(BigDecimal userLat, BigDecimal userLon,
                                                    BigDecimal shopLat, BigDecimal shopLon) {
        if (!isAllCoordinateValid(userLat, userLon, shopLat, shopLon)) {
            return null;
        }
        double meterVal = getDistance(userLat.doubleValue(), userLon.doubleValue(),
                shopLat.doubleValue(), shopLon.doubleValue());
        BigDecimal meter = new BigDecimal(meterVal);
        return meter.divide(METER_TO_KM, 2, RoundingMode.HALF_UP);
    }

    /**
     * 前端友好展示距离
     * 小于1000米：xx米；大于等于1000米：xx.xx公里；坐标异常：未知
     */
    public static String formatDisplay(BigDecimal userLat, BigDecimal userLon,
                                       BigDecimal shopLat, BigDecimal shopLon) {
        String meterStr = formatDistance(userLat, userLon, shopLat, shopLon);
        if ("未知".equals(meterStr)) {
            return "未知";
        }
        long meter = Long.parseLong(meterStr);
        if (meter < METER_TO_KM.longValue()) {
            return meter + "米";
        }
        BigDecimal meterNum = new BigDecimal(meter);
        BigDecimal km = meterNum.divide(METER_TO_KM, 2, RoundingMode.HALF_UP);
        return km + "公里";
    }

    /**
     * 底层计算：返回两点球面距离（单位米）
     */
    public static double getDistance(double lat1, double lon1, double lat2, double lon2) {
        double lat1Rad = lat1 * TO_RADIANS;
        double lon1Rad = lon1 * TO_RADIANS;
        double lat2Rad = lat2 * TO_RADIANS;
        double lon2Rad = lon2 * TO_RADIANS;

        double deltaLat = lat2Rad - lat1Rad;
        double deltaLon = lon2Rad - lon1Rad;

        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
                + Math.cos(lat1Rad) * Math.cos(lat2Rad)
                * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return EARTH_RADIUS_M * c;
    }

    // ===================== 坐标校验私有方法 =====================
    /**
     * 批量校验四个坐标：非空、不等于0、经纬度范围合法
     */
    private static boolean isAllCoordinateValid(BigDecimal lat1, BigDecimal lon1,
                                                BigDecimal lat2, BigDecimal lon2) {
        // 任意坐标为null
        if (ObjectUtil.hasNull(lat1, lon1, lat2, lon2)) {
            return false;
        }
        // 任意坐标数值等于0
        if (lat1.compareTo(ZERO) == 0
                || lon1.compareTo(ZERO) == 0
                || lat2.compareTo(ZERO) == 0
                || lon2.compareTo(ZERO) == 0) {
            return false;
        }
        // 两组坐标分别校验范围
        return isSingleCoordinateValid(lat1, lon1) && isSingleCoordinateValid(lat2, lon2);
    }

    /**
     * 校验单组经纬度范围：纬度[-90,90] 经度[-180,180]
     */
    private static boolean isSingleCoordinateValid(BigDecimal lat, BigDecimal lon) {
        return lat.abs().compareTo(MAX_LAT) <= 0
                && lon.abs().compareTo(MAX_LON) <= 0;
    }
}