package com.bonus.system.att.utils;

import com.alibaba.fastjson2.JSONObject;
import com.bonus.common.core.utils.SnCal;

import java.io.*;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 说明： 此工具类主要用于处理地址转换经纬度，经纬度转换成地址使用。
 * 修改一下关键部分内容即可使用
 * @author zys
 **/
public class AddressCoordinateFormatUtil{

    //PM43nB8eDNTBrXkQwGrTQFcmOni3Z9nO
    //LsWlA22jQshOOfiTRRe50XZYvczqj59K
    private final static String MAP_API_KEY ="LsWlA22jQshOOfiTRRe50XZYvczqj59K";

    private final static String MAP_API_SK ="xLNoAIfBmg04440mpeIXPwzR3qXaIPdJ";

    /**
     * 地址转经纬度：调用百度地图Api，传入地址名称，获取地址经纬度
     * @author zys
     * @param  address  地址名称
     * @return Map<String,BigDecimal> 返回 map。 map键说明：longitude 为经度。latitude为纬度。
     */
    public static Map<String,BigDecimal> addressToCoordinate(String address){
        //获取SN
        Map<String, String> paramsMap = new LinkedHashMap<>();
        paramsMap.put("address", address);
        paramsMap.put("output", "json");
        paramsMap.put("ak", MAP_API_KEY);
        String sn = null;
        try {
            sn = getLocationSn("/geocoding/v3/?",paramsMap);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        String encoderAddress = null;
        try {
            encoderAddress = URLEncoder.encode(address, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        String addressToCoordinateApiUrl = "http://api.map.baidu.com/geocoding/v3/?address="+encoderAddress+"&output=json&ak=" + MAP_API_KEY+"&sn="+sn;
        if (address.trim().isEmpty()){
//            throw new RuntimeException("地址转经纬度失败，未输入地址！");
            return null;
        }
        // 调用百度地图接口获取结果，
        // 返回结果类似于：
        // {"status":0,"result":{"location":{"lng":经度值,"lat":纬度值},"precise":0,"confidence":50,"comprehension":57,"level":"工业园区"}}
        // "status":0 成功状态
        String json = requestApi(addressToCoordinateApiUrl);
        if (json.trim().isEmpty()){
//            throw new RuntimeException("地址转经纬度失败，接口无返回数据："+json);
            return null;
        }
        // 结果转换为json对象
        JSONObject obj = JSONObject.parseObject(json);
        if (!"0".equals(obj.getString("status"))) {
//            throw new RuntimeException("地址转经纬度失败，接口返回状态值为失败："+json);
            return null;
        }
        // 经度
        BigDecimal lng = (BigDecimal)obj.getJSONObject("result").getJSONObject("location").get("lng");
        // 纬度
        BigDecimal lat = (BigDecimal)obj.getJSONObject("result").getJSONObject("location").get("lat");
        return new HashMap<String, BigDecimal>(){{
            put("longitude",lng );
            put("latitude",lat );
        }};
    }

    private static String getLocationSn(String method,Map<String, String> paramsMap) throws UnsupportedEncodingException {
        SnCal snCal = new SnCal();
        // 计算sn跟参数对出现顺序有关，get请求请使用LinkedHashMap保存<key,value>，该方法根据key的插入顺序排序；post请使用TreeMap保存<key,value>，该方法会自动将key按照字母a-z顺序排序。所以get请求可自定义参数顺序（sn参数必须在最后）发送请求，但是post请求必须按照字母a-z顺序填充body（sn参数必须在最后）。以get请求为例：http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak，paramsMap中先放入address，再放output，然后放ak，放入顺序必须跟get请求中对应参数的出现顺序保持一致。
        // 调用下面的toQueryString方法，对LinkedHashMap内所有value作utf8编码，拼接返回结果address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourak
        String paramsStr = snCal.toQueryString(paramsMap);
        // 对paramsStr前面拼接上/geocoder/v2/?，后面直接拼接yoursk得到/geocoder/v2/?address=%E7%99%BE%E5%BA%A6%E5%A4%A7%E5%8E%A6&output=json&ak=yourakyoursk
        String wholeStr = method + paramsStr + MAP_API_SK;
        // 对上面wholeStr再作utf8编码
        String tempStr = URLEncoder.encode(wholeStr, "UTF-8");
        // 调用下面的MD5方法得到最后的sn签名7de5a22212ffaa9e326444c75a58f9a0
        return snCal.MD5(tempStr);
    }

    /**
     * 经纬度转地址：调用百度地图Api，传入经纬度，获取地址名称
     * @author zys
     * @param  longitude 经度 Double
     * @param  latitude  纬度 Double
     * @return String 返回经纬度对应的地址名称。
     */
    public static String coordinateToAddress(Double longitude, Double latitude){

        //获取SN
        Map<String, String> paramsMap = new LinkedHashMap<>();
        String location = latitude+","+longitude;
        paramsMap.put("ak", MAP_API_KEY);
        paramsMap.put("output", "json");
        paramsMap.put("coordtype", "wgs84ll");
        paramsMap.put("extensions_poi", "0");
        paramsMap.put("location", location);
        String sn = null;
        try {
            sn = getLocationSn("/reverse_geocoding/v3?",paramsMap);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        String encoderAddress = null;
        try {
            encoderAddress = URLEncoder.encode(location, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        String coordinateToAddressApiUrl = "https://api.map.baidu.com/reverse_geocoding/v3?ak="+MAP_API_KEY+"&output=json&coordtype=wgs84ll&extensions_poi=0&location="+encoderAddress+"&sn="+sn;
        if (Objects.isNull(longitude) || Objects.isNull(latitude)){
            throw new RuntimeException("经纬度转地址失败，输入的经纬度不正确");
        }
        // 返回结果类似于：
        // {"status":0,"result":{"location":{"lng":经度值,"lat":纬度值},"formatted_address":"具体地址名称","edz":{"name":""},"business":"科技园,大冲","addressComponent":{"country":"中国","country_code":0,"country_code_iso":"CHN","country_code_iso2":"CN","province":"广东省","city":"深圳市","city_level":2,"district":"南山区","town":"粤海街道","town_code":"440305007","distance":"75","direction":"东北","adcode":"440305","street":"沙河西路","street_number":"2029-西门"},"pois":[],"roads":[],"poiRegions":[],"sematic_description":"","formatted_address_poi":"","cityCode":340}}
        // "status":0 成功状态
        String json = requestApi(coordinateToAddressApiUrl);
        if (json.trim().isEmpty()){
            throw new RuntimeException("经纬度转地址失败，接口无返回数据："+json);
        }
        // 结果转换为json对象
        JSONObject obj = JSONObject.parseObject(json);
        if (!"0".equals(obj.getString("status"))) {
            throw new RuntimeException("经纬度转地址失败，接口返回状态值为失败："+json);
        }
        JSONObject result= obj.getJSONObject("result");
        if (Objects.isNull(result)){
            throw new RuntimeException("经纬度转地址失败，接口返回数据不符合预期，无法获取到result节点："+json);
        }
        return result.getString("formatted_address");
    }

    /**
     * 经纬度转地址：调用百度地图Api，传入经纬度，获取地址名称
     * @author zys
     * @param  longitude 经度 Double
     * @param  latitude  纬度 Double
     * @return JSONObject 返回整个json,方便获取其他数据
     */
    public static JSONObject coordinateToAddress2(Double longitude, Double latitude){
        //获取SN
        Map<String, String> paramsMap = new LinkedHashMap<>();
        String location = latitude+","+longitude;
        paramsMap.put("ak", MAP_API_KEY);
        paramsMap.put("output", "json");
        paramsMap.put("coordtype", "wgs84ll");
        paramsMap.put("extensions_poi", "0");
        paramsMap.put("location", location);
        String sn = null;
        try {
            sn = getLocationSn("/reverse_geocoding/v3?",paramsMap);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        String encoderAddress = null;
        try {
            encoderAddress = URLEncoder.encode(location, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        String coordinateToAddressApiUrl = "https://api.map.baidu.com/reverse_geocoding/v3?ak="+MAP_API_KEY+"&output=json&coordtype=wgs84ll&extensions_poi=0&location="+encoderAddress+"&sn="+sn;
        if (Objects.isNull(longitude) || Objects.isNull(latitude)){
            throw new RuntimeException("经纬度转地址失败，输入的经纬度不正确");
        }
        // 返回结果类似于：
        // {"status":0,"result":{"location":{"lng":经度值,"lat":纬度值},"formatted_address":"具体地址名称","edz":{"name":""},"business":"科技园,大冲","addressComponent":{"country":"中国","country_code":0,"country_code_iso":"CHN","country_code_iso2":"CN","province":"广东省","city":"深圳市","city_level":2,"district":"南山区","town":"粤海街道","town_code":"440305007","distance":"75","direction":"东北","adcode":"440305","street":"沙河西路","street_number":"2029-西门"},"pois":[],"roads":[],"poiRegions":[],"sematic_description":"","formatted_address_poi":"","cityCode":340}}
        // "status":0 成功状态
        String json = requestApi(coordinateToAddressApiUrl);
        if (json.trim().isEmpty()){
            throw new RuntimeException("经纬度转地址失败，接口无返回数据："+json);
        }
        // 结果转换为json对象
        JSONObject obj = JSONObject.parseObject(json);
        if (!"0".equals(obj.getString("status"))) {
            throw new RuntimeException("经纬度转地址失败，接口返回状态值为失败："+json);
        }
        JSONObject result= obj.getJSONObject("result");
        if (Objects.isNull(result)){
            throw new RuntimeException("经纬度转地址失败，接口返回数据不符合预期，无法获取到result节点："+json);
        }
        return result;
    }

    /**
     * 请求百度地图接口
     * @author zys
     * @param  url  接口具体地址
     * @return String 返回json格式的字符串
     */
    private static String requestApi(String url) {
        StringBuilder json = new StringBuilder();
        try {
            URLConnection urlConnection = new URL(url).openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection .getInputStream(), StandardCharsets.UTF_8));
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                json.append(inputLine);
            }
            in.close();
        } catch (IOException ignored) {}
        return json.toString();
    }
}
