package com.chinaweal.aiccs.aiccs.market.ai;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.chinaweal.aiccs.aiccs.abnormal.entity.EBaseinfo;
import com.chinaweal.aiccs.aiccs.abnormal.service.EBaseinfoService;
import com.chinaweal.aiccs.aiccs.market.ai.pipeline.PolygonFilterPipeline;
import com.chinaweal.aiccs.aiccs.market.ai.util.GeometryUtil;
import com.chinaweal.aiccs.aiccs.market.entity.*;
import com.chinaweal.aiccs.aiccs.market.entity.dto.AssignEntity;
import com.chinaweal.aiccs.aiccs.market.entity.dto.GeoCodes;
import com.chinaweal.aiccs.aiccs.market.entity.dto.SliceAssignLogDto;
import com.chinaweal.aiccs.aiccs.market.service.*;
import com.chinaweal.aiccs.common.constant.Constant;
import com.chinaweal.aiccs.common.enums.SliceTypeEnums;
import com.chinaweal.aiccs.common.util.AnalysisUtils;
import com.chinaweal.aiccs.common.util.StringUtils;
import com.chinaweal.aicorg.model.AICUser;
import com.chinaweal.youfool.framework.springboot.exception.custom.BusinessException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.ParseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 片区自动认领AI实现
 *
 * @author <a href="https://blog.lroyia.top">lroyia</a>
 * @since 2021/11/27 19:48
 **/
@Component
@Slf4j(topic = "claim")
public class AreaSliceClaimAIImpl implements AreaSliceClaimAI {

    @Autowired
    private AreaSlicePolygonService areaSlicePolygonService;
    @Autowired
    private AddressGeoLogService addressGeoLogService;
    @Autowired
    private AreaSliceService areaSliceService;
    @Autowired
    private BizOwnerAssignService bizOwnerAssignService;
    @Autowired
    private EBaseinfoService eBaseinfoService;
    @Resource
    private AddressAssignService addressAssignService;

    /**
     * 多边形额外页面过滤管道
     */
    private List<PolygonFilterPipeline> polygonFilterPipelines = null;
    /**
     * 使用最小权重
     */
    private final static Integer USE_LEVEL_WEIGHT = 2;

    /**
     * 地区编码匹配优先权重Map
     */
    private final static Map<String, Double> LEVEL_CODE_MAP = initLevelCodeMap();

    /**
     * 初始化地区编码匹配优先权重Map
     */
    private static Map<String, Double> initLevelCodeMap() {
        Map<String, Double> map = new HashMap<>();
        map.put("未知", -1d);
        map.put("国家", 0d);
        map.put("省", 0d);
        map.put("市", 0d);
        map.put("区县", 0d);
        map.put("开发区", 1d);
        map.put("乡镇", 2d);
        map.put("村庄", 2d);
        map.put("热点商圈", 3d);
        map.put("道路", 3d);
        map.put("道路交叉路口", 3d);
        map.put("兴趣点", 3d);
        map.put("门牌号", 4d);
        map.put("单元号", 4d);
        map.put("楼层", 4d);
        map.put("房间", 4d);
        map.put("公交地铁站点", 5d);
        map.put("门址", 5d);
        map.put("小巷", 5d);
        map.put("住宅区", 5d);
        return Collections.unmodifiableMap(map);
    }

    @Override
    @Transactional
    public BizOwnerAssign claimInScope(AssignEntity entity, String operator) {

        // 操作人
        if (StringUtils.isBlank(operator)) {
            operator = OPERATOR;
        }

        //个体户登记机关非6位的根据登记机关认领
        if ("9910".equals(entity.getEntType()) && entity.getRegOrg().length() > 6) {
            return assignIndivByRegorg(entity, operator);
        }

        // 1、是否已经认领, 或者被退还过
        SliceAssignLogDto assignLog = null;
        List<SliceAssignLogDto> claimLogList = bizOwnerAssignService.listBySliceInfo(null, entity.getPripid());
        if (CollectionUtils.isNotEmpty(claimLogList)) {
            for (SliceAssignLogDto each : claimLogList) {
                if (each.getRemDate() == null) {
                    if (assignLog == null || Integer.parseInt(assignLog.getSliceType()) < Integer.parseInt(each.getSliceType())) {
                        assignLog = each;
                    }
                }
            }
        }
        SliceTypeEnums typeEnums = null;
        if (assignLog != null) {
            typeEnums = SliceTypeEnums.getEnumsByCode(assignLog.getSliceType());
        }

        // 1、用户指定片区认领
        BizOwnerAssign bizOwnerAssign1 = assignByAddress(assignLog, typeEnums, entity, operator, SliceTypeEnums.SPEC_SLICE);
        if (bizOwnerAssign1 != null) {
            return bizOwnerAssign1;
        }


        // 2、所公共片区认领
        if (!SliceTypeEnums.STATION_PUBLIC.equals(typeEnums)) {
            BizOwnerAssign assign = assignByAddress(assignLog, typeEnums, entity, operator, SliceTypeEnums.STATION_PUBLIC);
            if (assign != null) {
                return assign;
            }
        }

        // 3、公共片区认领
        // 根据登记机关获取区公共片区
        BizOwnerAssign bizOwnerAssign = assignRegionPublic(entity, operator);
        // 根据地址获取区公共片区
//        if (bizOwnerAssign == null) {
//            bizOwnerAssign = assignRegionPublicAddress(entity, operator);
//        }
        return bizOwnerAssign;
    }

    @Override
    @Transactional
    public BizOwnerAssign claimByAddress(EBaseinfo each, AreaSlice slice, String orgNumber, String operator) throws BusinessException {
        eBaseinfoService.clearRegulatory(each.getPripid());

        List<SliceAssignLogDto> claimLogList = bizOwnerAssignService.listBySliceInfo(null, each.getPripid());
        if (CollectionUtils.isNotEmpty(claimLogList)) {
            for (SliceAssignLogDto atom : claimLogList) {
                if (atom.getRemDate() == null) {
                    bizOwnerAssignService.updateById(new BizOwnerAssign()
                            .setAssignId(atom.getAssignId())
                            .setRemDate(LocalDateTime.now())
                            .setRemReason("认领到其他所，现根据地址批量调整到相应片区")
                            .setRemType("1")
                            .setRemOperatorName(operator)
                            .setUpdateTime(LocalDateTime.now()));
                }
            }

        }
        bizOwnerAssignService.areaClaim(slice.getPSliceId(), each.getPripid(), operator);
        bizOwnerAssignService.areaClaim(slice.getSliceId(), each.getPripid(), operator);

        return new BizOwnerAssign();
    }

    @Override
    public Map<String, Object> claimByFile(List<Map<String, String>> handleList, AICUser user) throws BusinessException {
        Map<String, Object> resultMap = new HashMap<>();
        List<Map<String, String>> noCliaimList = new ArrayList<>();
        BizOwnerAssign bizOwnerAssign = new BizOwnerAssign();
        for (Map<String, String> each : handleList) {
            EBaseinfo base = eBaseinfoService.getOne(new LambdaQueryWrapper<EBaseinfo>()
                    .eq(EBaseinfo::getUniscid, each.get("uniscid"))
                    .eq(EBaseinfo::getRegstate, "1"));

            if (base == null) {
                noCliaimList.add(each);
                continue;
            }

            if (!user.getRegionID().substring(0, 6).equals(base.getRegorg().substring(0, 6))) {
                each.put("reason", "不为同一登记机关");
                noCliaimList.add(each);
                continue;
            }

            //区局片区
            AreaSlice regionSlice = areaSliceService.getOne(new LambdaQueryWrapper<AreaSlice>()
                    .eq(AreaSlice::getSliceType, "0")
                    .eq(AreaSlice::getState, "1")
                    .eq(AreaSlice::getIsDeleted, "0")
                    .like(AreaSlice::getSliceName, each.get("region")));
            if (regionSlice == null) {
                throw new BusinessException(each.get("uniscid") + each.get("region") + "找不到区公共片区");
            }

            //所片区
            AreaSlice slice = areaSliceService.getOne(new LambdaQueryWrapper<AreaSlice>()
                    .eq(AreaSlice::getSliceType, "1")
                    .eq(AreaSlice::getState, "1")
                    .eq(AreaSlice::getIsDeleted, "0")
                    .like(AreaSlice::getSliceName, each.get("slice")));
            if (slice == null) {
                throw new BusinessException(each.get("uniscid") + each.get("region") + "找不到所公共片区");
            }

            if (!regionSlice.getSliceId().equals(slice.getPSliceId())) {
                throw new BusinessException(each.get("uniscid") + each.get("region") + each.get("slice") + "该所不属于该片区");
            }

            eBaseinfoService.clearRegulatory(base.getPripid());
            List<SliceAssignLogDto> claimLogList = bizOwnerAssignService.listBySliceInfo(null, base.getPripid());
            if (CollectionUtils.isNotEmpty(claimLogList)) {
                for (SliceAssignLogDto atom : claimLogList) {
                    if (atom.getRemDate() == null) {
                        bizOwnerAssignService.updateById(new BizOwnerAssign()
                                .setAssignId(atom.getAssignId())
                                .setRemDate(LocalDateTime.now())
                                .setRemReason("认领到其他所，现根据文件导入调整到相应片区")
                                .setRemType("1")
                                .setRemOperatorName(user.getName())
                                .setUpdateTime(LocalDateTime.now()));
                    }
                }
            }
            bizOwnerAssignService.areaClaim(regionSlice.getSliceId(), base.getPripid(), "AreaSliceClaimAI");
            bizOwnerAssign = bizOwnerAssignService.areaClaim(slice.getSliceId(), base.getPripid(), "AreaSliceClaimAI");
        }
        resultMap.put("claimNum", handleList.size() - noCliaimList.size());
        resultMap.put("noClaimNum", noCliaimList.size());
        resultMap.put("noClaimList", noCliaimList);
        return resultMap;
    }

    /**
     * 个体户根据登记机关认领
     */
    private BizOwnerAssign assignIndivByRegorg(AssignEntity entity, String operator) {
        String regOrg = entity.getRegOrg();
        if (StringUtils.isBlank(regOrg)) {
            return null;
        }

        // 1、是否已被所/区公共片区认领
        Boolean suoFlag = false;
        Boolean quFlag = false;
        List<SliceAssignLogDto> claimLogList = bizOwnerAssignService.listBySliceInfo(null, entity.getPripid());
        if (CollectionUtils.isNotEmpty(claimLogList)) {
            for (SliceAssignLogDto each : claimLogList) {
                if (each.getRemDate() == null && SliceTypeEnums.STATION_PUBLIC.equals(each.getSliceType())) {
                    suoFlag = true;
                } else if (each.getRemDate() == null && SliceTypeEnums.REGION_PUBLIC.equals(each.getSliceType())) {
                    quFlag = true;
                }
            }
        }

        //根据个体户登记机关查找片区
        List<AreaSlice> list = areaSliceService.list(new LambdaQueryWrapper<AreaSlice>()
                .eq(AreaSlice::getSliceType, SliceTypeEnums.STATION_PUBLIC.getCode())
                .eq(AreaSlice::getOrgNumber, entity.getRegOrg())
                .eq(AreaSlice::getIsDeleted, '0')
                .eq(AreaSlice::getState, '1'));
        if (list.size() != 1) {
            return null;
        }
        //认领到所和区
        BizOwnerAssign bizOwnerAssign = null;
        if (!quFlag) bizOwnerAssign = assignRegionPublic(entity, operator);
        if (!suoFlag)
            bizOwnerAssign = bizOwnerAssignService.areaClaim(list.get(0).getSliceId(), entity.getPripid(), operator);
        //bizOwnerAssignService.areaClaim(list.get(0).getPSliceId(), entity.getPripid(), operator);
        return bizOwnerAssign;
    }

    /**
     * 认领区公共片区
     *
     * @param entity   主体信息
     * @param operator 操作人
     * @return 操作结果
     * @author lroyia
     * @since 2021年12月13日 11:25:01
     */
    private BizOwnerAssign assignRegionPublic(AssignEntity entity, String operator) {
        String regOrg = entity.getRegOrg();
        if (StringUtils.isBlank(regOrg)) {
            return null;
        }
        if (regOrg.length() > 6) {
            regOrg = regOrg.substring(0, 6);
        }
        List<AreaSlice> list = areaSliceService.list(new LambdaQueryWrapper<AreaSlice>()
                .eq(AreaSlice::getSliceType, SliceTypeEnums.REGION_PUBLIC.getCode())
                .likeRight(AreaSlice::getOrgNumber, regOrg));
        if (list.size() != 1) {
            return null;
        }
        return bizOwnerAssignService.areaClaim(list.get(0).getSliceId(), entity.getPripid(), operator);
    }

    /**
     * 根据地址认领区公共片区
     *
     * @param entity   主体信息
     * @param operator 操作人
     * @return 操作结果
     * @author lroyia
     * @since 2021年12月13日 11:25:01
     */
    public BizOwnerAssign assignRegionPublicAddress(AssignEntity entity, String operator) {
        String regionGZ = AnalysisUtils.getRegionGZ(entity.getAddress());
        if (regionGZ == null) {
            return null;
        }
        List<String> codeList = new ArrayList<>();
        for (Map.Entry<String, String> entry : Constant.NM_REGION_CODE.entrySet()) {
            if (entry.getValue().equals(regionGZ)) {
                codeList.add(entry.getKey());
                break;
            }
        }
        if (codeList.size() == 0) {
            return null;
        }
        for (String code : codeList) {
            List<AreaSlice> list = areaSliceService.list(new LambdaQueryWrapper<AreaSlice>()
                    .eq(AreaSlice::getSliceType, SliceTypeEnums.REGION_PUBLIC.getCode())
                    .likeRight(AreaSlice::getOrgNumber, code));
            if (list.size() != 1) {
                continue;
            }
            return bizOwnerAssignService.areaClaim(list.get(0).getSliceId(), entity.getPripid(), operator);
        }
        throw new BusinessException("找不到合适的区公共片区");
    }

    /**
     * 认领所公共片区
     *
     * @param publicLog 公共片区认领记录
     * @param entity    主体信息
     * @param operator  认领人
     * @return 认领结果
     * @author lroyia
     * @since 2021年12月13日 09:33:52
     */
    private BizOwnerAssign assignStationPublic(BizOwnerAssign publicLog, AssignEntity entity, String operator) {
        String regulatory = entity.getRegulatory();
        if (StringUtils.isBlank(regulatory)) {
            if (entity.getRegOrg() != null && entity.getRegOrg().length() > 6) {
                regulatory = entity.getRegOrg();
            } else {
                return null;
            }
        }
        List<AreaSlice> list = areaSliceService.list(new LambdaQueryWrapper<AreaSlice>()
                .eq(AreaSlice::getSliceType, SliceTypeEnums.STATION_PUBLIC.getCode())
                .likeRight(AreaSlice::getOrgNumber, regulatory));
        if (list.size() != 1) {
            return null;
        }
        return bizOwnerAssignService.areaClaim(list.get(0).getSliceId(), entity.getPripid(), operator);
    }

    /**
     * 按照地址认领
     *
     * @param publicLog       所公共片区认领记录
     * @param curAssignType   已认领的类型
     * @param entity          主体信息
     * @param operator        认领人
     * @param assignSliceType 要根据地址认领的片区类型（限制所和片）
     * @return 认领结果
     * @author lroyia
     * @since 2021年12月13日 09:32:20
     */
    private BizOwnerAssign assignByAddress(BizOwnerAssign publicLog, SliceTypeEnums curAssignType, AssignEntity entity, String operator, SliceTypeEnums assignSliceType) {
        /*
         * 1、是否已认领，或者被退还过
         * 2、地址是否有效
         * 3、获取地理位置坐标点
         * 4、查找近距离片区，找出范围内的片区
         * 5、产生认领记录并保存
         */
        String address = entity.getAddress();
        String pripid = entity.getPripid();
        // 2、地址是否有效
        // 3、获取地理位置坐标点
        Point point = getPointByAddress(address);
        if (point == null) {
            log.error("地址：{}，找不到有效可用的地理坐标", address);
            return null;
        }

        // 4、查找近距离片区，找出范围内的片区
        AreaSlicePolygon inPolygon = null;
        String pSliceId = null;
        List<AreaSlicePolygon> polygonList;
        if (curAssignType != null && curAssignType.equals(SliceTypeEnums.STATION_PUBLIC) && publicLog != null) {
            pSliceId = publicLog.getSliceId();
        }
        polygonList = areaSlicePolygonService.getPolygonInDistance(point.getX(), point.getY(), null, null, pSliceId, assignSliceType);
        if (CollectionUtils.isNotEmpty(polygonList)) {
            List<AreaSlicePolygon> filterList = new ArrayList<>();
            for (AreaSlicePolygon each : polygonList) {
                try {
                    if (pointInPolygon(point, (Polygon) GeometryUtil.readWKB(each.getPolygonData()))) {
                        filterList.add(each);
                    }
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            // 额外业务过滤
            if (CollectionUtils.isNotEmpty(polygonFilterPipelines)) {
                for (PolygonFilterPipeline each : polygonFilterPipelines) {
                    filterList = each.filter(point, filterList);
                }
            }
            // 获取结果
            if (CollectionUtils.isNotEmpty(filterList)) {
                inPolygon = filterList.get(0);
            }
        }

        EBaseinfo eBaseinfo = eBaseinfoService.getOneByPripid(entity.getPripid());
        List<AddressAssign> addressList = addressAssignService.list(new LambdaQueryWrapper<AddressAssign>()
                .isNull(AddressAssign::getRemTime));

        //跑完地图后先通过地址库的地址匹配进行认领，无法认领时再用地图跑出来的监管所进行认领
        for (AddressAssign each : addressList) {
            if (eBaseinfo.getDom().contains(each.getAddress())) {
                if (!each.getType().equals("0")) {
                    String s = eBaseinfo.getDom().split(each.getAddress())[1].split("号")[0];
                    String regex = "[0-9]+";
                    Pattern pattern = Pattern.compile(regex);
                    Matcher matcher = pattern.matcher(s);
                    if (!matcher.matches()) {
                        continue;
                    }
                    int number = Integer.parseInt(s) % 2;//判断单双
                    if (each.getType().equals("1") && number == 0) {
                        continue;
                    } else if (each.getType().equals("2") && number == 1) {
                        continue;
                    }
                }
                AreaSlice slice = areaSliceService.getOne(new LambdaQueryWrapper<AreaSlice>()
                        .eq(AreaSlice::getOrgNumber, each.getOrgNumber())
                        .eq(AreaSlice::getSliceType, "1")
                        .eq(AreaSlice::getState, "1")
                        .eq(AreaSlice::getIsDeleted, "0"));
                if (slice != null) {
                    BizOwnerAssign assign1 = claimByAddress(eBaseinfo, slice, each.getOrgNumber(), each.getOperator());
                    if (assign1 != null) {
                        return assign1;
                    }
                }
            }
        }

        // 5、产生认领记录并保存
        if (inPolygon != null) {
            //登记机关是否与区片区的码值一致 assignSliceType
            if (!validateRegionOrg(entity, inPolygon.getSliceId())) {
                return null;
            }

            // 补充公共片区的认领
            if (curAssignType == null) {
                AreaSlice curSlice = areaSliceService.getById(inPolygon.getSliceId());
                if (curSlice != null && StringUtils.isNotBlank(curSlice.getPSliceId())) {
                    AreaSlice station = areaSliceService.getById(curSlice.getPSliceId());

                    //登记机关是市局判断住所的行政区划
                    if ("440101".equals(entity.getRegOrg()) && StringUtils.isBlank(eBaseinfo.getDomdistrict())) {
                        return null;
                    } else if ("440101".equals(entity.getRegOrg()) && !curSlice.getOrgNumber().substring(0, 6).equals(eBaseinfo.getDomdistrict().substring(0, 6))) {
                        return assignForSJ(eBaseinfo);
                    }

                    if (station != null) {
                        if (SliceTypeEnums.STATION_PUBLIC.getCode().equals(station.getSliceType())) {
                            // 区公共片区补充认领
                            if (StringUtils.isNotBlank(station.getPSliceId())) {
                                bizOwnerAssignService.areaClaim(station.getPSliceId(), pripid, operator);
                            }
                            // 所公共片区补充认领
                            bizOwnerAssignService.areaClaim(station.getSliceId(), pripid, operator);
                        } else if (SliceTypeEnums.REGION_PUBLIC.getCode().equals(station.getSliceType())) {
                            // 区公共片区补充认领
                            bizOwnerAssignService.areaClaim(station.getSliceId(), pripid, operator);
                        }
                    }
                }
            } else if (curAssignType.equals(SliceTypeEnums.REGION_PUBLIC) && SliceTypeEnums.SPEC_SLICE.equals(assignSliceType)) {
                // 所公共片区补充认领
                AreaSlice curSlice = areaSliceService.getById(inPolygon.getSliceId());
                if (curSlice != null && StringUtils.isNotBlank(curSlice.getPSliceId())) {
                    bizOwnerAssignService.areaClaim(curSlice.getPSliceId(), pripid, operator);
                }
            }
            return bizOwnerAssignService.areaClaim(inPolygon.getSliceId(), pripid, operator);
        }
        return publicLog;
    }

    /**
     * 如地址定位的监管所与地址所属行政区划一致，则认领到监管所;如果地址定位的监管所与地址所属行政区划不一致，
     * 则认领到行政区划公共片区，地址所属行政区划如果为空的话，则认领到市公共片区
     */
    private BizOwnerAssign assignForSJ(EBaseinfo eBaseinfo) {
        if (StringUtils.isNotBlank(eBaseinfo.getDomdistrict())) {
            AreaSlice sliceQ = areaSliceService.getOne(new LambdaQueryWrapper<AreaSlice>()
                    .eq(AreaSlice::getOrgNumber, eBaseinfo.getDomdistrict().substring(0, 6))
                    .eq(AreaSlice::getSliceType, "0")
                    .eq(AreaSlice::getState, "1")
                    .eq(AreaSlice::getIsDeleted, "0"));
            BizOwnerAssign bizOwnerAssign = null;
            bizOwnerAssign = bizOwnerAssignService.areaClaim(sliceQ.getSliceId(), eBaseinfo.getPripid(), "AreaSliceClaimAI");
            if (eBaseinfo.getDomdistrict().length() == 8) {
                AreaSlice sliceS = areaSliceService.getOne(new LambdaQueryWrapper<AreaSlice>()
                        .eq(AreaSlice::getOrgNumber, eBaseinfo.getDomdistrict())
                        .eq(AreaSlice::getSliceType, "1")
                        .eq(AreaSlice::getState, "1")
                        .eq(AreaSlice::getIsDeleted, "0"));
                bizOwnerAssign = bizOwnerAssignService.areaClaim(sliceS.getSliceId(), eBaseinfo.getPripid(), "AreaSliceClaimAI");
            }
            return bizOwnerAssign;
        }
        return null;
    }

    /**
     * 校验登记机关是否与区片区的码值一致
     */
    private boolean validateRegionOrg(AssignEntity entity, String sliceid) {
        Boolean flag = true;
        if (!"440129".equals(entity.getRegOrg().substring(0, 6)) && !"440101".equals(entity.getRegOrg())) {
            AreaSlice judgeSlice = areaSliceService.getById(sliceid);
            if (!entity.getRegOrg().substring(0, 6).equals(judgeSlice.getOrgNumber().substring(0, 6))) {
                flag = false;
            }
        }
        return flag;
    }

    @Override
    @Transactional
    public BizOwnerAssign claimToAreaSlice(String address, String pripid, String sliceId, String operator) {
        /*
         * 1、检查主体是否已被认领
         * 2、已被认领
         * 2.1、直接调整认领结果
         * 3、没有被认领
         * 3.1、认领
         * 3.2、获取地址的地理坐标，获取不到则直接结束方法，返回认领结果
         * 3.3、判断是否已经在该片区范围内，是则直接结束
         * 3.4、将片区的围点向该地理坐标放大并保存
         */
        // 操作人
        if (StringUtils.isBlank(operator)) {
            operator = OPERATOR;
        }
        // 1、是否已经认领
        BizOwnerAssign claimLog = bizOwnerAssignService.getOne(new LambdaQueryWrapper<BizOwnerAssign>()
                .eq(BizOwnerAssign::getPripid, pripid).isNull(BizOwnerAssign::getRemDate), false);
        // 2、已被认领
        if (claimLog != null) {
            // 2.1、直接调整认领结果
            claimLog.setUpdateTime(LocalDateTime.now())
                    .setUpdater(OPERATOR)
                    .setSliceId(sliceId);
            bizOwnerAssignService.updateById(claimLog);
            return claimLog;
        }
        // 3、没有被认领
        // 3.1、执行认领
        claimLog = bizOwnerAssignService.areaClaim(sliceId, pripid, operator);

        // 3.2、获取地址的地理坐标，获取不到则直接结束方法，返回认领结果
        Point point = null;
        try {
            point = getPointByAddress(address);
        } catch (BusinessException e) {
            log.error("地址：{}，找不到有效的地理坐标", address, e);
        }
        if (point == null) {
            return claimLog;
        }

        // 3.3、判断是否已经在该片区范围内，是则直接结束
        List<AreaSlicePolygon> list = areaSlicePolygonService.list(new LambdaQueryWrapper<AreaSlicePolygon>().eq(AreaSlicePolygon::getSliceId, sliceId));
        if (CollectionUtils.isEmpty(list)) {
            return claimLog;
        }

        // 取出距离最近的多边形
        AreaSlicePolygon areaSlicePolygon = null;
        Double distance = null;
        for (AreaSlicePolygon each : list) {
            try {
                each.setPolygon((Polygon) GeometryUtil.readWKB(each.getPolygonData()));
                // 3.3、判断是否已经在该片区范围内，是则直接结束
                if (pointInPolygon(point, each.getPolygon())) {
                    return claimLog;
                }
                if (areaSlicePolygon == null) {
                    areaSlicePolygon = each;
                    distance = each.getPolygon().distance(point);
                } else {
                    double dis = each.getPolygon().distance(point);
                    if (dis < distance) {
                        distance = dis;
                        areaSlicePolygon = each;
                    }
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        if (areaSlicePolygon == null) {
            return claimLog;
        }
        // 3.4、将片区的围点向该地理坐标放大

        /*
         * 放大方案：
         * 1、找出多边形中所有边中，边的质心（中心点）与目标点距离最短的两条边
         * 2、将最近的一条边的两端点与目标点连线
         * 3、如果两连线与另一条边不相交，则将这两条连线替换掉原本的这条边，形成新的多边形。
         * 4、反则，取另一条边的两端点进行连线替换掉该边形成新的四边形
         */
        // 1、找出多边形中所有边中，边的质心（中心点）与目标点距离最短的两条边
        Polygon polygon = areaSlicePolygon.getPolygon();
        Coordinate[] coordinates = polygon.getCoordinates();
        Map<String, Integer> orderMap = new HashMap<>();
        Double firstDistance = null, secondDistance = null;
        Coordinate firstCoorS = null, firstCoorE = null, secondCoorS = null, secondCoorE = null;
        for (int i = 0; i < coordinates.length - 1; i++) {
            Coordinate each = coordinates[i];
            Coordinate next = coordinates[i + 1];
            // 记录原坐标顺序
            orderMap.put(each.x + Constant.COMMA + each.y, i);
            LineString eachLine = GeometryUtil.createLine(each, next);
            double curDistance = eachLine.getCentroid().distance(point);
            if (firstDistance == null) {
                firstDistance = secondDistance = curDistance;
                firstCoorS = secondCoorS = each;
                firstCoorE = secondCoorE = next;
            } else {
                if (firstDistance < curDistance) {
                    if (secondDistance > curDistance) {
                        secondDistance = curDistance;
                        secondCoorS = each;
                        secondCoorE = next;
                    }
                } else {
                    secondDistance = firstDistance;
                    firstDistance = curDistance;
                    secondCoorS = firstCoorS;
                    secondCoorE = firstCoorE;
                    firstCoorS = each;
                    firstCoorE = next;
                }
            }
        }
        LineString secondLine = GeometryUtil.createLine(secondCoorS, secondCoorE);
        // 2、将最近的一条边的两段点与目标点连线
        LineString newLine1 = GeometryUtil.createLine(firstCoorS, point.getCoordinate());
        LineString newLine2 = GeometryUtil.createLine(point.getCoordinate(), firstCoorE);
        boolean useFirst = newLine1.disjoint(secondLine) && newLine2.disjoint(secondLine);
        // 3、如果两连线与另一条边不相交，则将这两条连线替换掉原本的这条边，形成新的多边形。
        // 4、反则，取另一条边的两端点进行连线替换掉该边形成新的四边形
        if (!useFirst) {
            newLine1 = GeometryUtil.createLine(secondCoorS, point.getCoordinate());
            newLine2 = GeometryUtil.createLine(point.getCoordinate(), secondCoorE);
        }

        // 创建新的多边形
        ArrayList<Coordinate> newCoordinateList = new ArrayList<>();
        boolean concat = true;
        Coordinate concatCoordinate = newLine1.getStartPoint().getCoordinate();
        for (int i = 0; i < coordinates.length; i++) {
            Coordinate each = coordinates[i];
            newCoordinateList.add(each);
            if (concat) {
                if (each.equals(concatCoordinate)) {
                    Coordinate endPoint = newLine2.getEndPoint().getCoordinate();
                    newCoordinateList.add(point.getCoordinate());
                    newCoordinateList.add(endPoint);
                    i = orderMap.get(endPoint.x + Constant.COMMA + endPoint.y);
                    concat = false;
                }
            }
        }
        Coordinate[] coordinates1 = new Coordinate[newCoordinateList.size()];
        for (int i = 0; i < newCoordinateList.size(); i++) {
            coordinates1[i] = newCoordinateList.get(i);
        }
        Polygon newPolygon = GeometryUtil.createPolygon(coordinates1);
        // 更新多边形信息
        areaSlicePolygonService.updatePolygonByWKT(newPolygon.toText(), areaSlicePolygon.getGId());
        return claimLog;
    }


    /**
     * 地址是否是有效地址（可以用正则取出街道名称的就当作是有效地址）
     *
     * @param address 地址
     * @return 是否有效
     * @author lroyia
     * @since 2021年11月27日 19:52:24
     */
    private boolean validateAddress(String address) {
        return AnalysisUtils.getStreetNameByAddress(address) != null;
    }

    /**
     * 点射线是否与线段（边）有交点
     * <a href="https://juejin.cn/post/6937940742388056095">地理围栏算法解析</a>
     *
     * @param point  射线点
     * @param sPoint 线段开始点
     * @param ePoint 线段结束点
     * @return 判断结果
     * @author lroyia
     * @since 2021年11月28日 10:06:36
     */
    private boolean isRayIntersectsSegment(Point point, Point sPoint, Point ePoint) {
        return isRayIntersectsSegment(point, sPoint.getCoordinate(), ePoint.getCoordinate());
    }

    /**
     * 点射线是否与线段（边）有交点
     * <a href="https://juejin.cn/post/6937940742388056095">地理围栏算法解析</a>
     *
     * @param point  射线点
     * @param sPoint 线段开始点（坐标）
     * @param ePoint 线段结束点（坐标）
     * @return 判断结果
     * @author lroyia
     * @since 2021年11月28日 10:06:36
     */
    private boolean isRayIntersectsSegment(Point point, Coordinate sPoint, Coordinate ePoint) {
        //排除 与射线平行、重合、是一个点的情况
        if (sPoint.y == ePoint.y) {
            return false;
        }
        // 排除 线段在射线上边
        if (sPoint.y > point.getY() && ePoint.y > point.getY()) {
            return false;
        }
        // 排除 线段在射线下边
        if (sPoint.y < point.getY() && ePoint.y < point.getY()) {
            return false;
        }
        // 排除 交点为下端点
        if (sPoint.y == point.getY() && ePoint.y > point.getY()) {
            return false;
        }
        // 排除 交点为上端点
        if (ePoint.y == point.getY() && sPoint.y > point.getY()) {
            return false;
        }
        // 排除 线段在射线左边
        if (sPoint.x < point.getX() && ePoint.x < point.getX()) {
            return false;
        }
        // 求交，相似多边形性质
        double xseg = ePoint.x + (point.getY() - ePoint.y) / (sPoint.y - ePoint.y) * (sPoint.x - ePoint.x);
        // 交点在射线起点的左侧
        if (xseg < point.getX()) {
            return false;
        }
        // 排除上述情况之后真正有效的点
        return true;
    }

    /**
     * 点在多边形内的判断
     *
     * @param point   点
     * @param polygon 多边形
     * @return 判断结果
     * @author lroyia
     * @since 2021年11月28日 11:16:08
     */
    private boolean pointInPolygon(Point point, Polygon polygon) {
        // 先判断点是否在外包矩形内
        if (!isPoiWithinBox(point, polygon)) {
            return false;
        }
        // 交点个数
        int count = 0;
        // 逐个点进行判断
        Coordinate[] coordinates = polygon.getCoordinates(); // 构成的长方形的点集
        for (int i = 0; i < coordinates.length - 1; i++) {
            Coordinate sPoi = coordinates[i];
            Coordinate ePoi = coordinates[i + 1];
            if (isRayIntersectsSegment(point, sPoi, ePoi)) {
                count++;
            }
        }
        return count % 2 != 0;
    }

    /**
     * 点是否在多变形的外围长方形内
     *
     * @param point   点
     * @param polygon 多边形
     * @return 判断结果
     * @author lroyia
     * @since 2021年11月28日 11:08:29
     */
    private boolean isPoiWithinBox(Point point, Polygon polygon) {
        Double maxX, maxY, minX, minY;
        List<Double> xList = new ArrayList<>();
        List<Double> yList = new ArrayList<>();
        for (Coordinate coordinate : polygon.getCoordinates()) {
            xList.add(coordinate.x);
            yList.add(coordinate.y);
        }
        xList.sort(Comparator.naturalOrder());
        yList.sort(Comparator.naturalOrder());
        maxX = xList.get(xList.size() - 1);
        minX = xList.get(0);
        maxY = yList.get(xList.size() - 1);
        minY = yList.get(0);
        return point.getX() >= minX && point.getX() <= maxX && point.getY() >= minY && point.getY() <= maxY;
    }

    /**
     * 根据地址获取地理位置坐标点
     *
     * @param address 地址
     * @return 地理位置坐标点
     * @author lroyia
     * @since 2021年11月28日 12:03:34
     */
    @Override
    public Point getPointByAddress(String address) {
        // 1、地址是否有效
//        String streetNameByAddress = AnalysisUtils.getStreetNameByAddress(address);
//        if (streetNameByAddress == null) {
//            throw new BusinessException("非法地址");
//        }

        // 2、查看地址API编码记录，看看有没有已有的坐标
        // 取出街道前的部分
//        String streetPrefix = address.substring(address.indexOf(streetNameByAddress));
        // 取出记录
        List<AddressGeoLog> addressGeoLog = addressGeoLogService.list(new LambdaQueryWrapper<AddressGeoLog>()
//                .ge(AddressGeoLog::getAddress, streetPrefix)
                        .eq(AddressGeoLog::getAddress, address)
//                        .ge(AddressGeoLog::getLevelWeight, USE_LEVEL_WEIGHT)
                        .orderByDesc(AddressGeoLog::getAddress)
        );
        Point point = null;
        if (CollectionUtils.isNotEmpty(addressGeoLog)) {
            AddressGeoLog geo = addressGeoLog.get(0);
            if (geo.getLevelWeight() >= USE_LEVEL_WEIGHT) {
                point = GeometryUtil.createPoint(geo.getPox().doubleValue(), geo.getPoy().doubleValue());
            }
        }

        // 3、没有地址API编码记录，则请求高德地图，获取编码地址
        if (point == null) {
            List<GeoCodes> geoCodes = addressGeoLogService.getGeoCodesByGeo(address, null);
            if (CollectionUtils.isEmpty(geoCodes)) {
                log.error("高德地图找不到该地址的地理编码: {}", address);
                return null;
            }
            // 按levelWeight倒序排列
            geoCodes.sort((a, b) -> LEVEL_CODE_MAP.get(b.getLevel()).compareTo(LEVEL_CODE_MAP.get(a.getLevel())));
            GeoCodes codes = geoCodes.get(0);
            Double value = LEVEL_CODE_MAP.get(codes.getLevel());
            if (value == null) {
                log.error("高德地图响应数据异常: {}", JSON.toJSONString(geoCodes));
                return null;
            }
            double levelWeight = value;
            // 保存高德地图解析记录
            String[] split = codes.getLocation().split(Constant.COMMA);
            double x = Double.parseDouble(split[0]);
            double y = Double.parseDouble(split[1]);
            addressGeoLogService.save(new AddressGeoLog()
                    .setAddress(address)
                    .setLevelName(codes.getLevel())
                    .setLevelWeight(levelWeight)
                    .setPox(new BigDecimal(x))
                    .setPoy(new BigDecimal(y))
            );
            if (levelWeight < USE_LEVEL_WEIGHT) {
                log.error("地址级别不符，地址：{}, 级别：{}", address, levelWeight);
                return null;
            }
            // 获取经纬度
            point = GeometryUtil.createPoint(x, y);
        }
        return point;
    }

}
