package shop.alien.second.config;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import shop.alien.entity.store.EssentialCityCode;
import shop.alien.mapper.EssentialCityCodeMapper;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * 高德地图工具类
 *
 * @author ssk
 * @version 1.0.0
 * @discription 打死你个龟孙
 * @since 1937-7-7
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class GaoDeMapUtil {

    @Value("${gaode.key}")
    private String key;

    @Value("${gaode.geoUrl}")
    private String geoUrl;

    @Value("${gaode.geoListUrl}")
    private String geoListUrl;

    @Value("${gaode.getDistrict}")
    private String getDistrict;

    @Value("${gaode.distanceUrl}")
    private String distanceUrl;

    @Value("${gaode.distanceTypeUrl}")
    private String distanceTypeUrl;

    @Value("${gaode.nearUrl}")
    private String nearUrl;

    @Value("${gaode.subwayUrl}")
    private String subwayUrl;

    @Value("${gaode.addressUrl}")
    private String addressUrl;

    //private final EssentialCityCodeMapper essentialCityCodeMapper;

    /**
     * 地址转经纬度
     *
     * @param address 地址
     * @return 经纬度
     */
    public String getLonAndLatByAddress(String address) {
        String formattedUrl = String.format(geoUrl, address, key);
        JSONObject obj = getResponse(formattedUrl);
        if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
            JSONArray geocodesArray = obj.getJSONArray("geocodes");
            if (geocodesArray != null && !geocodesArray.isEmpty()) {
                JSONObject jobJSON = geocodesArray.getJSONObject(0);
                return jobJSON.getString("location");
            } else {
                log.error("AddressLocationUtil.getLonAndLatByAddress ERROR 未找到与地址匹配的经纬度信息");
                return "ERROR 未找到与地址匹配的经纬度信息";
            }
        } else {
            log.error("AddressLocationUtil.getLonAndLatByAddress ERROR 地址转换经纬度失败");
            return "ERROR 地址转换经纬度失败";
        }
    }

    /**
     * 地址转经纬度
     *
     * @param address 地址
     * @return 经纬度
     */
    public JSONObject getInputPrompt(String address, String city) {
        //如果按照城市查询
        String cityCode = "";
        if (StringUtils.isNotEmpty(city)) {
//            EssentialCityCode essentialCityCode = essentialCityCodeMapper.selectOne(new LambdaQueryWrapper<EssentialCityCode>().eq(EssentialCityCode::getAreaName, city));
//            if (null != essentialCityCode) {
//                cityCode = essentialCityCode.getCityCode().toString();
//            }
        }
        String formattedUrl = String.format(geoListUrl, address, key, cityCode);
        JSONObject obj = getResponse(formattedUrl);
        return obj;
    }

    /**
     * 地址转经纬度
     * cityCode：城市编码
     *
     * @return
     */
    public JSONObject getDistrict(String cityCode) {
        String formattedUrl = String.format(getDistrict, key, cityCode);
        JSONObject obj = getResponse(formattedUrl);
        return obj;
    }

    /**
     * 计算两个经纬度的距离
     *
     * @param longitudeOne 经度1
     * @param latitudeOne  纬度1
     * @param longitudeTwo 经度2
     * @param latitudeTwo  纬度2
     * @return distance
     */
    public Double getDistance(String longitudeOne, String latitudeOne, String longitudeTwo, String latitudeTwo) {
        try {
            String urlString = String.format(distanceUrl, key, longitudeOne, latitudeOne, longitudeTwo, latitudeTwo);
            JSONObject obj = getResponse(urlString);
            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
                JSONArray resultsArray = obj.getJSONArray("results");
                if (resultsArray != null && !resultsArray.isEmpty()) {
                    JSONObject resultJSON = resultsArray.getJSONObject(0);
                    return Double.parseDouble(resultJSON.getString("distance"));
                } else {
                    log.error("AddressLocationUtil.getDistance ERROR 计算距离失败，结果为空");
                    return null;
                }
            } else {
                log.error("AddressLocationUtil.getDistance ERROR 计算距离失败");
                return null;
            }
        } catch (Exception e) {
            log.error("AddressLocationUtil.getDistance ERROR {}", e.getMessage());
            return null;
        }
    }

    /**
     * 计算两个经纬度的距离
     *
     * @param longitudeOne 经度1
     * @param latitudeOne  纬度1
     * @param longitudeTwo 经度2
     * @param latitudeTwo  纬度2
     * @return distance
     */
    public Double getDistanceStraightLine(String longitudeOne, String latitudeOne, String longitudeTwo, String latitudeTwo) {
        try {
            String urlString = String.format(distanceTypeUrl, key, longitudeOne, latitudeOne, longitudeTwo, latitudeTwo, 0);
            JSONObject obj = getResponse(urlString);
            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
                JSONArray resultsArray = obj.getJSONArray("results");
                if (resultsArray != null && !resultsArray.isEmpty()) {
                    JSONObject resultJSON = resultsArray.getJSONObject(0);
                    return Double.parseDouble(resultJSON.getString("distance"));
                } else {
                    log.error("AddressLocationUtil.getDistance ERROR 计算距离失败，结果为空");
                    return null;
                }
            } else {
                log.error("AddressLocationUtil.getDistance ERROR 计算距离失败");
                return null;
            }
        } catch (Exception e) {
            log.error("AddressLocationUtil.getDistance ERROR {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取附近商家、建筑等信息
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @return
     */
    public JSONArray getNear(String longitude, String latitude) {
        try {
            String urlString = String.format(nearUrl, longitude, latitude, key, 10000, 20, 1);
            JSONObject obj = getResponse(urlString);
            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
                JSONArray resultsArray = obj.getJSONArray("pois");
                return resultsArray;
            }
        } catch (Exception e) {
            log.error("AddressLocationUtil.getNear ERROR {}", e.getMessage());
        }
        return new JSONArray();
    }

    /**
     * 获取10公里范围内的商场
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @return 商场列表
     */
    public JSONArray getNearbyShoppingMalls(String longitude, String latitude) {
        try {
            // 使用高德地图POI搜索接口，搜索类型为商场(060100)
            String urlString = String.format(nearUrl, longitude, latitude, key, 10000, 20, 1) + "&types=060100";
            JSONObject obj = getResponse(urlString);
            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
                JSONArray resultsArray = obj.getJSONArray("pois");
                return resultsArray;
            }
        } catch (Exception e) {
            log.error("GaoDeMapUtil.getNearbyShoppingMalls ERROR {}", e.getMessage());
        }
        return new JSONArray();
    }

    public JSONObject getNearbySubway(String longitude, String latitude) {
        try {
            // 使用高德地图POI搜索接口，搜索类型为地铁站(010100)
            String encodedKeywords = URLEncoder.encode("地铁站", "UTF-8");
            String urlString = String.format(
                    subwayUrl,
                    key, latitude, longitude, encodedKeywords, 1000, 150500);
            JSONObject obj = getResponse(urlString);
            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
                JSONObject pois = obj.getJSONArray("pois").getJSONObject(0);
                return pois;
            }
        } catch (Exception e) {
            log.error("GaoDeMapUtil.getNearbySubway ERROR {}", e.getMessage());
        }
        return new JSONObject();
    }

    /**
     * 通过经纬度获取地址信息
     *
     * @param longitude
     * @param latitude
     * @param radius
     * @param extensions
     * @return
     */
    public Map<String, String> getAddressByLonAndLat(String longitude, String latitude, String radius, String extensions) {
        String formattedUrl = String.format(addressUrl, longitude, latitude, key, radius, extensions);
        JSONObject obj = getResponse(formattedUrl);
        try {
            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
                Map<String, String> address = new HashMap<>();
                JSONObject regeocodeObject = obj.getJSONObject("regeocode");
                JSONObject addressComponent = regeocodeObject.getJSONObject("addressComponent");
                address.put("city", addressComponent.getString("city"));
                address.put("district", addressComponent.getString("district"));
                address.put("province", addressComponent.getString("province"));
                JSONObject streetNumber = addressComponent.getJSONObject("streetNumber");
                address.put("street", streetNumber.getString("street"));
                address.put("streetNumber", streetNumber.getString("number"));
                JSONObject pois = regeocodeObject.getJSONArray("pois").getJSONObject(0);
                address.put("poiName", pois.getString("name"));
                return address;
            }
        } catch (Exception e) {
            log.error("GaoDeMapUtil.getAddressByLonAndLat ERROR {}", e.getMessage());
        }
        return new HashMap<>();
    }

    /**
     * 根据经纬度获取当前城市与城市半径
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @return Map包含城市名称、城市编码、城市中心点、城市半径(米)
     */
    public Map<String, Object> getCityAndRadiusByLonAndLat(String longitude, String latitude) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 1. 通过逆地理编码获取城市信息
            String formattedUrl = String.format(addressUrl, longitude, latitude, key, "1000", "base");
            JSONObject geoObj = getResponse(formattedUrl);
            
            if (null == geoObj || !"1".equals(String.valueOf(geoObj.get("status")))) {
                log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat 逆地理编码失败");
                return result;
            }
            
            JSONObject regeocodeObject = geoObj.getJSONObject("regeocode");
            JSONObject addressComponent = regeocodeObject.getJSONObject("addressComponent");
            
            String cityName = addressComponent.getString("city");
            String adcode = addressComponent.getString("adcode");  // 区县级别代码
            String province = addressComponent.getString("province");
            
            // 判断是否是直辖市
            boolean isMunicipality = StringUtils.isEmpty(cityName) || "[]".equals(cityName);
            if (isMunicipality) {
                cityName = province;
            }
            
            // 获取城市级别的行政区划代码
            String cityCode = getCityLevelCode(adcode, isMunicipality, province);
            
            result.put("city", cityName);
            result.put("province", province);
            result.put("cityCode", cityCode);
            
            // 2. 通过行政区域查询获取城市边界信息
            // 注意：必须添加 extensions=all 和 subdistrict=0 参数才能获取到polyline边界数据
            String districtUrl = String.format(getDistrict, key, cityCode) + "&extensions=all&subdistrict=0";
            JSONObject districtObj = getResponse(districtUrl);
            
            if (null != districtObj && "1".equals(String.valueOf(districtObj.get("status")))) {
                JSONArray districts = districtObj.getJSONArray("districts");
                if (districts != null && !districts.isEmpty()) {
                    JSONObject district = districts.getJSONObject(0);
                    String center = district.getString("center");
                    result.put("center", center);
                    
                    // 3. 计算城市半径：获取边界坐标，计算中心点到边界的最大距离
                    String polyline = district.getString("polyline");
                    if (StringUtils.isNotEmpty(polyline) && !"|".equals(polyline)) {
                        Double radius = calculateCityRadius(center, polyline);
                        result.put("radius", radius);
                        // 保留两位小数
                        result.put("radiusKm", Math.round(radius / 10.0) / 100.0);
                    } else {
                        // 如果没有边界数据，返回默认半径
                        result.put("radius", 50000.0);  // 默认50公里
                        result.put("radiusKm", 50.00);
                        log.warn("GaoDeMapUtil.getCityAndRadiusByLonAndLat 城市{}没有边界数据，使用默认半径", cityName);
                    }
                } else {
                    log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat 未查询到城市行政区域信息");
                }
            } else {
                log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat 行政区域查询失败");
            }
            
        } catch (Exception e) {
            log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat ERROR {}", e.getMessage(), e);
        }
        return result;
    }

    /**
     * 获取城市级别的行政区划代码
     * 
     * @param adcode 区县级别的行政区划代码
     * @param isMunicipality 是否是直辖市
     * @param province 省份名称
     * @return 城市级别的行政区划代码
     */
    private String getCityLevelCode(String adcode, boolean isMunicipality, String province) {
        if (StringUtils.isEmpty(adcode) || adcode.length() != 6) {
            return adcode;
        }
        
        // 直辖市（北京、上海、天津、重庆）：使用省级代码（前2位+0000）
        if (isMunicipality || "北京市".equals(province) || "上海市".equals(province) 
                || "天津市".equals(province) || "重庆市".equals(province)) {
            return adcode.substring(0, 2) + "0000";
        }
        
        // 普通地级市：使用市级代码（前4位+00）
        return adcode.substring(0, 4) + "00";
    }

    /**
     * 计算城市半径：计算中心点到边界的最大直线距离
     *
     * @param center   中心点坐标 "经度,纬度"
     * @param polyline 边界坐标串 "经度,纬度;经度,纬度;..." 或 "经度,纬度;经度,纬度|经度,纬度;经度,纬度"
     * @return 城市半径（米）
     */
    private Double calculateCityRadius(String center, String polyline) {
        try {
            String[] centerCoords = center.split(",");
            double centerLon = Double.parseDouble(centerCoords[0]);
            double centerLat = Double.parseDouble(centerCoords[1]);
            
            // 处理多个区域的情况（用|分隔）
            String[] areas = polyline.split("\\|");
            double maxDistance = 0.0;
            
            for (String area : areas) {
                String[] points = area.split(";");
                // 采样边界点，避免计算量过大（每隔10个点采样一次）
                int step = Math.max(1, points.length / 50);
                for (int i = 0; i < points.length; i += step) {
                    String[] pointCoords = points[i].split(",");
                    if (pointCoords.length >= 2) {
                        double pointLon = Double.parseDouble(pointCoords[0]);
                        double pointLat = Double.parseDouble(pointCoords[1]);
                        
                        // 使用本地Haversine公式计算直线距离（不调用API，速度快）
                        double distance = haversineDistance(centerLon, centerLat, pointLon, pointLat);
                        if (distance > maxDistance) {
                            maxDistance = distance;
                        }
                    }
                }
            }
            
            return maxDistance;
        } catch (Exception e) {
            log.error("GaoDeMapUtil.calculateCityRadius ERROR {}", e.getMessage());
            return 50000.0; // 默认50公里
        }
    }

    /**
     * 使用Haversine公式计算两点间的直线距离（本地计算，不调用API）
     *
     * @param lon1 起点经度
     * @param lat1 起点纬度
     * @param lon2 终点经度
     * @param lat2 终点纬度
     * @return 距离（米）
     */
    private double haversineDistance(double lon1, double lat1, double lon2, double lat2) {
        final double EARTH_RADIUS = 6371000; // 地球半径（米）
        
        // 转换为弧度
        double lat1Rad = Math.toRadians(lat1);
        double lat2Rad = Math.toRadians(lat2);
        double deltaLat = Math.toRadians(lat2 - lat1);
        double deltaLon = Math.toRadians(lon2 - lon1);
        
        // Haversine公式
        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 * c;
    }

    /**
     * 创建请求
     *
     * @param serverUrl 请求地址
     * @return JSONObject
     */
    private JSONObject getResponse(String serverUrl) {
        StringBuilder result = new StringBuilder();
        try {
            URL url = new URL(serverUrl);
            URLConnection conn = url.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            in.close();
        } catch (Exception e) {
            log.error("AddressLocationUtil.getResponse ERROR {}", e.getMessage());
            return null;
        }
        return JSONObject.parseObject(result.toString());
    }
}
