package com.skyversation.poiaddr.addquery;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.skyversation.poiaddr.bean.AddressResult;
import com.skyversation.poiaddr.bean.GeoJsonBean;
import com.skyversation.poiaddr.bean.WDToken;
import com.skyversation.poiaddr.service.AreaService;
import com.skyversation.poiaddr.util.net.AddressNetTools;
import com.skyversation.poiaddr.util.status.AddressLevel;
import com.skyversation.poiaddr.util.status.AddressResultEnum;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

public class AddressQueryEngine {

    private static AddressQueryEngine instance = new AddressQueryEngine();

    private AddressQueryEngine() {
    }

    public synchronized static AddressQueryEngine getInstance() {
        if (instance == null) {
            instance = new AddressQueryEngine();
        }
        return instance;
    }

    private WDToken wdToken = null;

    /***
     * 单条地名搜索通用方法，搜索顺序  1. 武大吉奥； 2. 市四中心； 3. 高德搜索； 4. 高德高级搜索；
     * @param addrs
     * @return
     */
    public AddressResult commonSearchByName(List<String> addrs) {
        if (addrs == null || addrs.size() < 1) {
            return null;
        }
        AddressResult addressResult = new AddressResult();
        for (String addr : addrs) {
            //  创建请求
            addressResult = sj_szxSearchByName(addr);
            if (addressResult != null) {
                addressResult.setCode(AddressResultEnum.SZX_SUCCESS);
                addressResult.setMessage("成功");
                return getCjWgWgwByLoc(addressResult);
            }
        }
        addressResult.setCode(AddressResultEnum.RESULT_NULL);
        addressResult.setMessage("失败");
        return addressResult;
    }

    /***
     * 武大吉奥单条地名搜索
     * @param address
     */
    public AddressResult wdjaSearchByName(String address) {
        System.out.println("<<<<<<<<<<----------开始武大吉奥地名地址搜索------------>>>>>>>>>>>>>");
        if (wdToken == null || System.currentTimeMillis() - wdToken.getTime() > 36000000) {
            wdToken = AddressTools.getInstance().getWDToken(Constant.WD_USER_NAME, Constant.WD_USER_PWD);
            if (wdToken == null) {
                return null;
            }
            wdToken.setTime(System.currentTimeMillis());
        }
        if (wdToken == null)
            return AddressTools.getInstance().faildQuery(AddressResultEnum.NO_TOKEN, "token获取失败");
        String token = wdToken.getToken();

        String url = Constant.GET_ADDRESS_MEG_URL + "?token=" + token + "&addr=" + address;
        String body = null;
        try {
            body = AddressNetTools.getInstance().wdSendGetRequest(url);
        } catch (Exception e) {
            e.toString();
        }
        if (body == null || body.equals("null") || !StringUtils.hasText(body))
            return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
        try {
            JSONObject json = JSONObject.parseObject(body);
            return TransfromDataTool.wdResultToResult(json);
        } catch (Exception e) {
            return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
        }
    }

    /***
     * 市四中心地名搜索
     * @param address
     * @return
     */
    public AddressResult szxSearchByName(String address) {
        ResponseEntity response = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, Constant.SZX_URL + "&region=" + Constant.getAMAP_CITY_CODE() + "&query=" + address, null, null, 10);
        if (response != null) {
            String body = response.getBody() + "";
            if (!StringUtils.hasText(body))
                return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
            try {
                return TransfromDataTool.szxResultToResult(JSONObject.parseObject(body),address);
            } catch (Exception e) {
                System.err.println(e);
                return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
            }
        } else {
            System.out.println("地址[" + address + "]未查询到数据！");
            return null;
        }
    }

    public String getSigns(String timestamp, String appCode, String UUid, String appSecret) {
//        先拼接
        String signData = timestamp + appCode + UUid;
//        生成sign
        String sign = "";
        try {
            Mac sha256 = Mac.getInstance("HmacSHA256");
            sha256.init(new SecretKeySpec(appSecret.getBytes(), "HmacSHA256"));
            byte[] result = sha256.doFinal(signData.getBytes());
            sign = Base64.encodeBase64String(result);
        } catch (Exception e) {
            System.err.println("sign 生成异常：" + e);
        }
        return sign;
    }

    public AddressResult sj_szxSearchByName(String address) {
//      鉴权
        Map<String, String> headerMap = new HashMap<>();
        String timestamp = System.currentTimeMillis() + "";
        String appCode = "7unv4vbwqxnq4a7m9h";
        String UUid = UUID.randomUUID().toString();
        String appSecret = "w6tawvf4k3ck4ikij9";
        headerMap.put("x-timestamp", timestamp);
        headerMap.put("x-appcode", appCode);
        headerMap.put("x-uuid", UUid);
//      签名
        headerMap.put("x-sign", getSigns(timestamp, appCode, UUid, appSecret));
        JSONObject params = new JSONObject();
        JSONArray requestJson = new JSONArray();
        JSONObject paramObject = new JSONObject();
        paramObject.put("name", "query");
        paramObject.put("position", "QUERY");
        paramObject.put("value", address);
        requestJson.add(paramObject);
//      判断是否是关键字版搜索服务
        if(Constant.SJ_SZX_SEARCH_BY_NAME.contains("1742459783686")){
            JSONObject paramObject2 = new JSONObject();
            paramObject2.put("name","region");
            paramObject2.put("position", "QUERY");
            paramObject2.put("value", Constant.getAMAP_CITY_CODE());
            requestJson.add(paramObject2);
            JSONObject paramObject3 = new JSONObject();
            paramObject3.put("name","page_size");
            paramObject3.put("position", "QUERY");
            paramObject3.put("value", "10");
            requestJson.add(paramObject3);
        }
        params.put("requestJson", requestJson);
//      发起请求
        ResponseEntity response = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.POST, Constant.SJ_SZX_SEARCH_BY_NAME, params, headerMap, 1);
        if (response != null && response.hasBody()) {
            String body = response.getBody() + "";
            if (!StringUtils.hasText(body))
                return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
            try {
                if (body.indexOf("[") != 0) {
                    return TransfromDataTool.szxResultToResult(JSONObject.parseObject(body),address);
//                    System.out.println("请求地址：" + address + ";返回结果错误：" + body);
//                    return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "请求地址：" + address + ";返回结果错误：" + body);
                } else {
//                  将得分最高的结果映射到实体类
                    return TransfromDataTool.szxResultToResult2(com.skyversation.poiaddr.util.AddressTools.getInstance().findBestMatch(address, JSONArray.parseArray(body), "address"));
                }
            } catch (Exception e) {
                System.err.println("请求地址：" + address + ";返回结果：" + body + ";处理异常：" + e);
                return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
            }
        } else {
            System.out.println("地址[" + address + "]未查询到数据！");
            return null;
        }
    }

    /***
     * 高德普通地名搜索
     * @param address
     * @return
     */
    public AddressResult gdSearchByName(String address) {
        String geoUrl = Constant.AMAP_GEO_URL + "?key=" + Constant.AMAP_KEY + "&address=" + address + "&city=" + Constant.getAMAP_CITY_CODE() + "&output=JSON";
        ResponseEntity response = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, geoUrl, null, null, 0);
        if (response != null && response.getBody() != null) {
            String body = response.getBody() + "";
            if (!StringUtils.hasText(body))
                return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
            try {
                JSONObject json = JSONObject.parseObject(body);
                return TransfromDataTool.gdResultToResult(json);
            } catch (Exception e) {
                System.err.println(e);
                return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
            }
        } else {
            return null;
        }
    }

    /***
     * 高德高级地名搜索
     * @param address
     * @return
     */
    public AddressResult gdV3SearchByName(String address) {
        String geoUrl =
                Constant.AMAP_SEARCH_NAME_V3 + "?key=" + Constant.AMAP_KEY + "&types=" + Constant.AMAP_SEARCH_TYPES +
                        "&keywords=" + address + "&city=" + Constant.getAMAP_CITY_CODE() + "&offset=20";
        ResponseEntity responseEntity = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, geoUrl, null, null, 0);
        if (responseEntity.hasBody()) {
            String body = responseEntity.getBody() + "";
            if (!StringUtils.hasText(body)) {
                return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
            }
            JSONArray pois = new JSONArray();
            JSONObject json = JSONObject.parseObject(body);
            if (json.containsKey("pois")) {
                pois.addAll(json.getJSONArray("pois"));
            }
            if (json.containsKey("count")) {
                int count = Integer.parseInt(json.get("count") + "");
                if (count > 20) {
                    for (int i = 2; i < (count % 20 + 1); i++) {
                        geoUrl =
                                Constant.AMAP_SEARCH_NAME_V3 + "?key=" + Constant.AMAP_KEY + "&types=" + Constant.AMAP_SEARCH_TYPES +
                                        "&keywords=" + address + "&city=" + Constant.getAMAP_CITY_CODE() + "&offset=20&page=" + i;
                        ResponseEntity responseEntity2 = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, geoUrl, null, null, 0);
                        if (responseEntity2.hasBody()) {
                            body = responseEntity2.getBody() + "";
                        }
                        if (!StringUtils.hasText(body)) {
                            return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_AGAIN_NULL, "多次搜索无结果");
                        }
                        JSONObject json2 = JSONObject.parseObject(body);
                        if (json2.containsKey(pois)) {
                            pois.addAll(JSONObject.parseObject(body).getJSONArray("pois"));
                        }
                    }
                }
            }
            try {
                return TransfromDataTool.gdV3ResultToResult(pois);
            } catch (Exception e) {
                System.err.println(e);
                return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
            }
        } else {
            return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_AGAIN_NULL, "高德V3分页查询无结果");
        }
    }


    /***
     * 根据搜索地址，赋值村居、网格、微格网信息
     * @param result
     * @return
     */
    public AddressResult getCjWgWgwByLoc(AddressResult result) {
        if (result == null || result.getData() == null || result.getData().size() < 1) {
            return result;
        } else {
            AreaService areaService = AreaService.getInstance();
            for (AddressResult.ContentBean content : result.getData()) {
                if ((content.getAdname() == null || content.getAdname().isEmpty() || content.getCityname() == null || content.getCityname().isEmpty()) && content.getLat() != null && content.getLon() != null) {
                    GeoJsonBean adBean = areaService.isInadPolygon(content.getLon(), content.getLat());
                    content.setAdJson(adBean == null ? new JSONObject() : adBean.getProperties());
                    GeoJsonBean townBean = areaService.isInTownPolygon(content.getLon(), content.getLat());
                    content.setTownJson(townBean == null ? new JSONObject() : townBean.getProperties());
                }
            }
            return result;
        }
    }

    //
    public static boolean isNotEmptyOrBlank(String str) {
        return str != null && !str.trim().isEmpty();
    }

    /**
     * 1. 搜索到结果就返回第一个；
     * 2. 搜索到结果去除行政区划后，存在包含关系，则使用；
     * 3. 搜索到结果，数字进行分词，数字匹配则使用；
     * 4. 搜索到结果，数字与文本均匹配，则使用；
     * 5. 结果与基准数据完全一致，则使用。
     *
     * @param result
     * @param level
     * @return
     */
    public AddressResult.ContentBean verificaData(AddressResult result, AddressLevel level, String addr) {
        switch (level) {
            case LEVEL_1: {
                if (result != null && result.getData() != null && result.getData().size() > 0) {
                    return result.getData().get(0);
                } else {
                    return null;
                }
            }
//          2. 搜索到结果去除行政区划后，存在包含关系，则使用；
            case LEVEL_CONTAINS_2: {
                if (result != null && result.getData() != null && result.getData().size() > 0) {
                    List<AddressResult.ContentBean> contentBean = result.getData();
                    for (AddressResult.ContentBean contentBean1 : contentBean) {
                        if (contentBean1.getAddress() != null) {
                            String address = townReplaceAll(addressReplaceAll(contentBean1.getAddress()));
                            String address2 = townReplaceAll(addressReplaceAll(addr));
                            if (isNotEmptyOrBlank(address) && address.contains(address2)) {
                                return contentBean1;
                            }
                        }
                    }
                } else {
                    return null;
                }
            }
//          3. 搜索到结果，数字进行分词，数字匹配则使用；
            case LEVLE_NUMBER_3: {
                if (result != null && result.getData() != null && result.getData().size() > 0) {
                    List<AddressResult.ContentBean> contentBean = result.getData();
                    for (AddressResult.ContentBean contentBean1 : contentBean) {
                        if (contentBean1.getAddress() != null) {
                            Set<String> address = tokenizeString(contentBean1.getAddress()).get(1);
                            if (address != null && address.size() > 1) {
                                Set<String> address2 = tokenizeString(addr).get(1);
                                int addressSize = address.size();
                                for (String addr2 : address2) {
                                    if (address.contains(addr2)) {
                                        addressSize--;
                                        if (addressSize == 0) {
                                            return contentBean1;
                                        }
                                    }
                                }
                            }
                        }
                    }
                } else {
                    return null;
                }
            }
//          4. 搜索到结果，数字与文本均匹配，则使用；
            case LEVEL_NUMBER_TEXT_4: {
                if (result != null && result.getData() != null && result.getData().size() > 0) {
                    List<AddressResult.ContentBean> contentBean = result.getData();
                    for (AddressResult.ContentBean contentBean1 : contentBean) {
                        if (contentBean1.getAddress() != null) {
                            Set<String> addressString = tokenizeString(contentBean1.getAddress()).get(0);
                            Set<String> addressNumber = tokenizeString(contentBean1.getAddress()).get(1);
                            Set<String> address2String = tokenizeString(addr).get(0);
                            Set<String> address2Number = tokenizeString(addr).get(1);
                            if (addressString != null && addressString.size() > 1) {
                                int addressStrSize = addressString.size();
                                for (String addr2str : address2String) {
                                    if (addressString.contains(addr2str)) {
                                        addressStrSize--;
                                        if (addressStrSize == 0) {
                                            int addressNumSize = addressNumber.size();
                                            for (String addr2Num : address2Number) {
                                                if (addressNumber.contains(addr2Num)) {
                                                    addressNumSize--;
                                                    if (addressNumSize == 0) {
                                                        return contentBean1;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                } else {
                    return null;
                }
            }
//          结果与基准数据完全一致，则使用。
            case LEVEL_TOTAL_CONTAINS_5: {
                if (result != null && result.getData() != null && result.getData().size() > 0) {
                    List<AddressResult.ContentBean> contentBean = result.getData();
                    for (AddressResult.ContentBean contentBean1 : contentBean) {
                        if (contentBean1.getAddress() != null) {
                            String address = addressReplaceAll(contentBean1.getAddress());
                            String address2 = addressReplaceAll(addr);
                            if (isNotEmptyOrBlank(address) && address.equals(address2)) {
                                return contentBean1;
                            }
                        }
                    }
                } else {
                    return null;
                }
            }
            default: {
                return null;
            }
        }
    }

    public static String addressReplaceAll(String address) {
        return address.replaceAll("上海市", "").replaceAll(Constant.getArea() + "区", "").replaceAll("-", "").replaceAll("_", "").replaceAll("/", "").replaceAll(" ", "").replaceAll(",", "").replaceAll("\\.", "").replaceAll("，", "").replaceAll("。", "").replaceAll("\\+", "").replaceAll("\\*", "");
    }

    public static String townReplaceAll(String address) {
        String[] towns = Constant.getTowns();
        for (String town : towns) {
            address = address.replaceAll(town, "");
        }
        return address.replaceAll("号", "0").replaceAll("弄", "0").replaceAll("室", "0").replaceAll("户", "0").replaceAll("单元", "0").replaceAll("幢", "0");
    }

    /**
     * 数字和文字分词返回List<Set<String>>
     *
     * @param input
     * @return
     */
    public static List<Set<String>> tokenizeString(String input) {
        input = townReplaceAll(input);
        // 初始化两个集合，一个用于存储非数字字符串，一个用于存储数字字符串
        Set<String> nonNumberSet = new HashSet<>();
        Set<String> numberSet = new HashSet<>();

        StringBuilder currentToken = new StringBuilder();

        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (Character.isDigit(c)) {
                // 如果当前字符是数字
                if (currentToken.length() > 0 && !Character.isDigit(currentToken.charAt(0))) {
                    // 如果之前的 token 是非数字，将其添加到非数字集合中
                    nonNumberSet.add(currentToken.toString());
                    currentToken.setLength(0);
                }
                currentToken.append(c);
            } else {
                // 如果当前字符不是数字
                if (currentToken.length() > 0 && Character.isDigit(currentToken.charAt(0))) {
                    // 如果之前的 token 是数字，将其添加到数字集合中
                    numberSet.add(currentToken.toString());
                    currentToken.setLength(0);
                }
                if (!Character.isWhitespace(c)) {
                    currentToken.append(c);
                }
            }
        }

        // 处理最后一个 token
        if (currentToken.length() > 0) {
            if (Character.isDigit(currentToken.charAt(0))) {
                numberSet.add(currentToken.toString());
            } else {
                nonNumberSet.add(currentToken.toString());
            }
        }

        // 将两个集合添加到列表中
        List<Set<String>> result = new ArrayList<>();
        result.add(nonNumberSet);
        result.add(numberSet);

        return result;
    }
}
