package com.yc.ship.module.trade.service.insurance;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.yc.ship.framework.common.pojo.CommonResult;
import com.yc.ship.framework.common.pojo.PageResult;
import com.yc.ship.framework.common.util.collection.CollectionUtils;
import com.yc.ship.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yc.ship.framework.mybatis.core.util.MyBatisUtils;
import com.yc.ship.module.infra.api.config.ConfigApi;
import com.yc.ship.module.product.dal.dataobject.voyage.VoyageDO;
import com.yc.ship.module.product.service.voyage.VoyageService;
import com.yc.ship.module.trade.api.insurance.dto.*;
import com.yc.ship.module.trade.controller.admin.insurance.vo.InsurancePageReqVO;
import com.yc.ship.module.trade.controller.admin.insurance.vo.InsuranceRespVO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.TradeOrderRespVO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.TradeVisitorRespVO;
import com.yc.ship.module.trade.dal.dataobject.insurance.InsuranceDO;
import com.yc.ship.module.trade.dal.dataobject.order.TradeOrderDO;
import com.yc.ship.module.trade.dal.dataobject.order.TradeVisitorDO;
import com.yc.ship.module.trade.dal.mysql.insurance.InsuranceMapper;
import com.yc.ship.module.trade.dal.mysql.order.TradeOrderMapper;
import com.yc.ship.module.trade.dal.mysql.order.TradeVisitorMapper;
import com.yc.ship.module.trade.enums.InsuranceStatusEnum;
import com.yc.ship.module.trade.framework.mq.TradePublishUtils;
import com.yc.ship.module.trade.utils.InsuranceRequestHelper;
import com.yc.ship.module.trade.utils.InsuranceUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.yc.ship.module.trade.enums.ErrorCodeConstants.*;


/**
 * 阳光保险实现类
 *
 * @className: InsuranceServiceImpl
 * @author: le'yang
 * @date: 2025/3/21 16:12
 */
@Slf4j
@Service
@Validated
public class InsuranceServiceImpl implements InsuranceService {

    private static final String ENCRYPT_TYPE = "RSA";

    @Resource
    private InsuranceMapper insuranceMapper;
    @Resource
    private ConfigApi configApi;
    @Resource
    private RedissonClient redissonClient;

    @Resource
    private TradeOrderMapper tradeOrderMapper;

    @Resource
    private TradeVisitorMapper tradeVisitorMapper;

    @Resource
    private VoyageService voyageService;

    @Resource
    private InsuranceUtil insuranceUtil;

    @Resource
    private TradePublishUtils tradePublishUtils;

    @Value("${yudao.notify.insurance}")
    private String notifyUrl;


    private void validateInsuranceExists(Long id) {
        if (insuranceMapper.selectById(id) == null) {
            throw exception(INSURANCE_NOT_EXISTS);
        }
    }

    @Override
    public InsuranceDO getInsurance(Long id) {
        return insuranceMapper.selectById(id);
    }

    @Override
    public PageResult<InsuranceRespVO> getInsurancePage(InsurancePageReqVO pageReqVO) {
        IPage<InsuranceDO> iPage = MyBatisUtils.buildPage(pageReqVO);
        IPage<InsuranceRespVO> page = insuranceMapper.queryPage(iPage, pageReqVO);
        PageResult<InsuranceRespVO> result = new PageResult<>(page.getRecords(), page.getTotal());
        return result;
    }


    @Override
    public List<InsuranceDO> getInsuranceByOrder(List<Long> orderIds) {
        LambdaQueryWrapperX<InsuranceDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(InsuranceDO::getOrderId, orderIds);
        return insuranceMapper.selectList(queryWrapperX);
    }

    /**
     * 申请投保
     *
     * @param
     * @return
     */
    @Override
//    @Transactional
    public boolean applyInsurance(Long orderId) {

        TradeOrderDO orderInfo = tradeOrderMapper.selectById(orderId);
        Integer isInsure = orderInfo.getIsInsure();
        if(isInsure != null ) {
            if(isInsure == 1) {
                throw exception0(500, "该订单已投保成功");
            }else if(isInsure == 0) {
                throw exception0(500, "该订单正在投保中");
            }
        }
        VoyageDO voyage = voyageService.getVoyage(orderInfo.getVoyageId());
        InsuranceDO insuranceDO = new InsuranceDO();
        Long id = IdWorker.getId(insuranceDO);

        InsuranceApplyReqDTO insuranceApplyReqDTO = new InsuranceApplyReqDTO();
        insuranceApplyReqDTO.setService("applyTeam");
        insuranceApplyReqDTO.setReplyUrl(notifyUrl);
        insuranceApplyReqDTO.setOnlinePayInd("0");

        //订单对象, 一个订单下可以有一批被保险人
        InsuranceOrderInfoDTO insuranceOrderInfoDTO = new InsuranceOrderInfoDTO();

        //投保人信息
        insuranceOrderInfoDTO.setExternalOrderNo(id.toString());
        insuranceOrderInfoDTO.setExternalPolicyNumber(id.toString());
        insuranceOrderInfoDTO.setTeamCode(orderInfo.getGroupNo());
        //TODO: 当前写死
        insuranceOrderInfoDTO.setProductNo(configApi.getConfigValueByKey("insuranceConfig.product_no"));
        insuranceOrderInfoDTO.setBeginDate(DateUtil.format(voyage.getBoardingTime(), "yyyy-MM-dd"));
        insuranceOrderInfoDTO.setEndDate(DateUtil.format(voyage.getLeaveTime(), "yyyy-MM-dd"));
        insuranceOrderInfoDTO.setCurrencyCode("CNY");
        insuranceOrderInfoDTO.setBizType(0);
        insuranceOrderInfoDTO.setApplicantType(1);
        insuranceOrderInfoDTO.setApplicant(configApi.getConfigValueByKey("insuranceConfig.applicant"));
        insuranceOrderInfoDTO.setApplicantCertificateType(6);
        insuranceOrderInfoDTO.setApplicantCertificateNo(configApi.getConfigValueByKey("insuranceConfig.applicant_certificate_no"));
        insuranceOrderInfoDTO.setApplicantBirthday("1990-01-01");
        insuranceOrderInfoDTO.setApplicantSex(1);

        //销售人信息
        insuranceOrderInfoDTO.setSellCorp("营销中心");
        insuranceOrderInfoDTO.setSellDept("营销中心");
        insuranceOrderInfoDTO.setSellUser("营销中心");
        insuranceApplyReqDTO.setOrder(insuranceOrderInfoDTO);

        //被保人对象列表, 其下可多位被保险人, 他们在同个订单下
        List<TradeVisitorRespVO> tradeVisitorRespVOS = tradeVisitorMapper.queryVisitorByOrderId(orderId);
        if(tradeVisitorRespVOS == null || tradeVisitorRespVOS.size() == 0) {
            log.error("订单{}下没有被保人信息, 投保失败", orderId);
            throw exception(INSURANCE_NO_INSURED);
        }
        List<InsuredDTO> insuredList = new ArrayList<>();
        tradeVisitorRespVOS.forEach(tradeVisitorRespVO -> {
            InsuredDTO insuredDTO = new InsuredDTO();
            insuredDTO.setExternalPolicyNumber(tradeVisitorRespVO.getId().toString());
            String name = tradeVisitorRespVO.getName();
            insuredDTO.setInsuredName(name);
            if(9 == tradeVisitorRespVO.getCredentialType()) {
                if(PinyinUtil.isChinese(name.charAt(0))) {
                    String pinyin = PinyinUtil.getPinyin(name, " ");
                    insuredDTO.setInsuredName(pinyin);
                    insuredDTO.setFirstName(pinyin.substring(0, pinyin.indexOf(" ")));
                    insuredDTO.setLastName(pinyin.substring(pinyin.indexOf(" ") + 1));
                }else {
                    insuredDTO.setInsuredName(name);
                    if(name.contains("/")) {
                        insuredDTO.setFirstName(name.substring(0, name.indexOf("/")));
                        insuredDTO.setLastName(name.substring(name.indexOf("/") + 1));
                    }else {
                        insuredDTO.setFirstName(name.substring(0, name.indexOf(" ")));
                        insuredDTO.setLastName(name.substring(name.indexOf(" ") + 1));
                    }
                }
            }
            insuredDTO.setCertificateType(transCredentialType(tradeVisitorRespVO.getCredentialType()));
            insuredDTO.setCertificateNo(tradeVisitorRespVO.getCredentialNo());
            insuredDTO.setBirthday(tradeVisitorRespVO.getBirthday());
            insuredDTO.setSex(tradeVisitorRespVO.getGender());
            insuredDTO.setRelation(2); //不是保单持有人，只是被保险人
            insuredList.add(insuredDTO);
        });
        insuranceApplyReqDTO.setInsureds(insuredList);

//        //验证投保信息
//        CommonResult commonResult = insuranceUtil.validateInsuranceRequest(insuranceApplyReqDTO);
//        if (!commonResult.isSuccess()) {
//            throw exception0(commonResult.getCode(), commonResult.getMsg());
//        }
        // 开始投保
        insuranceDO.setOrderId(orderId);
        insuranceDO.setInsuranceStatus(InsuranceStatusEnum.INSURE.getValue());
        insuranceDO.setInsuredNum(insuredList.size());
        insuranceDO.setInsuranceNo(orderInfo.getOrderNo());
        insuranceDO.setTenantId(voyage.getTenantId());
        insuranceDO.setRationType(insuranceOrderInfoDTO.getProductNo());
        insuranceDO.setInsuranceEffectDate(voyage.getBoardingTime());
        insuranceDO.setId(id);
        CommonResult commonResult = insuranceUtil.sendInsuranceApply(insuranceApplyReqDTO);
        if (!commonResult.isSuccess()) {
            log.error("投保失败:{}", commonResult.getMsg());
            InsuranceDO insuranceDO1 = insuranceMapper.selectByOrderId(orderId);
            if(insuranceDO1 != null) {
                insuranceDO1.setInsuranceStatus(InsuranceStatusEnum.FAIL.getValue());
                insuranceDO1.setResMsg(commonResult.getMsg());
                insuranceMapper.updateById(insuranceDO1);
            }else {
                insuranceDO.setInsuranceStatus(InsuranceStatusEnum.FAIL.getValue());
                insuranceDO.setResMsg(commonResult.getMsg());
                insuranceMapper.insert(insuranceDO);
            }
            tradeOrderMapper.update(new LambdaUpdateWrapper<TradeOrderDO>().set(TradeOrderDO::getIsInsure, InsuranceStatusEnum.FAIL.getValue()).eq(TradeOrderDO::getId, orderId));
            throw exception0(commonResult.getCode(), commonResult.getMsg());
        }
        //保存投保信息
        insuranceMapper.deleteByOrderId(orderId);
        insuranceDO.setResMsg(String.valueOf(commonResult.getCheckedData()));

        insuranceMapper.insert(insuranceDO);
        // 发送查询投保接口通知
        tradePublishUtils.publishInsuranceQueryMsg(id.toString());
        return true;
    }

    /**
     * 处理系统证件枚举与保险证件枚举转换
     *
     * @param credentialType
     * @return
     */
    private Integer transCredentialType(Integer credentialType) {
//        证件类型                    系统            保险
//        身份证                       0                 1
//        外国人永居证                  2                     16
//        回乡证                       7                 5
//        台胞证                       8                26
//        外籍护照                     9                 8
//        户口本                       10                18
        // 保险类型 1身份证  17 港澳台居民居住证（18位8开头的身份证号码，例如830000198308030005） 8 外国护照 5 港澳居民来往内地通行证（H开头） 26 台湾居民来往内地通行证*8位数字
        Integer transCredentialType = 6; //默认为其他
        switch (credentialType) {
            case 0: //身份证
                transCredentialType = 1;
                break;
            case 2:
                transCredentialType = 16;
                break;
            case 7:
                transCredentialType = 5;
                break;
            case 8: // 外籍护照
                transCredentialType = 26; // 保险外国护照
                break;
            case 9:
                transCredentialType = 8;
                break;
            case 10:
                transCredentialType = 18; // 保险身份证
                break;
            case 11:
                transCredentialType = 17; // 港澳台居民居住证
            default:
                break;

        }
        return transCredentialType;
    }

    /**
     * 退保
     *
     */
    @Override
    @Transactional
    public void applyCancelInsurance(Long id) {
        InsuranceDO insuranceDO = insuranceMapper.selectById(id);
        if(insuranceDO == null){
            throw exception(INSURANCE_NOT_EXISTS);
        }
        TradeOrderRespVO orderInfo = tradeOrderMapper.getOrderInfo(insuranceDO.getOrderId());
        if(orderInfo == null){
            throw exception(ORDER_NOT_EXISTS);
        }
        VoyageDO voyage = voyageService.getVoyage(orderInfo.getVoyageId());

        InsuranceCancelReqDTO insuranceCancelReqDTO = new InsuranceCancelReqDTO();
        insuranceCancelReqDTO.setService("cancel");
        insuranceCancelReqDTO.setReplyUrl(notifyUrl);

        //订单对象, 一个订单下可以有一批被保险人
        InsuranceOrderInfoCancelDTO insuranceOrderInfoDTO = new InsuranceOrderInfoCancelDTO();

        insuranceOrderInfoDTO.setExternalOrderNo(id.toString());
        insuranceOrderInfoDTO.setBeginDate(DateUtil.format(voyage.getBoardingTime(), "yyyy-MM-dd"));
        insuranceOrderInfoDTO.setEndDate(DateUtil.format(voyage.getLeaveTime(), "yyyy-MM-dd"));
        insuranceCancelReqDTO.setOrder(insuranceOrderInfoDTO);

        List<InsuredCancelDTO> insuredList = new ArrayList<>();
        InsuredCancelDTO insuredDTO = new InsuredCancelDTO();
        insuredDTO.setExternalPolicyNumber(id.toString());
        insuredDTO.setPolicyNo(insuranceDO.getPolicyNo());
        insuredList.add(insuredDTO);
        insuranceCancelReqDTO.setInsureds(insuredList);

        // 开始退保
        CommonResult commonResult = insuranceUtil.sendInsuranceCancel(insuranceCancelReqDTO);
        if (!commonResult.isSuccess()) {
            throw exception0(commonResult.getCode(), commonResult.getMsg());
        }
    }

    /**
     * 电子保单查询
     */
    @Override
    @Transactional
    public void queryEpolicyAll() {
        List<InsuranceDO> list = insuranceMapper.selectList(new LambdaQueryWrapperX<InsuranceDO>()
                .eq(InsuranceDO::getInsuranceStatus, 1).isNull(InsuranceDO::getElectronicPolicy).gt(InsuranceDO::getCreateTime, DateUtil.yesterday()));
        for (InsuranceDO insuranceDO : list) {
            queryEpolicyList(insuranceDO.getId());
        }
    }

    @Override
    public InsuranceDO getByOrderId(Long orderId) {
        return insuranceMapper.selectByOrderId(orderId);
    }

    @Override
    @Transactional
    public void handleInsuranceQuery(BigDecimal amount, String policyNo, Long id, String status, String service, String msg) {
        InsuranceDO insuranceDO = insuranceMapper.selectById(id);
        if(insuranceDO == null){
            log.error("handleInsuranceQuery》insuranceDO is null{}", id);
            return;
        }
        insuranceDO.setPremium(amount);
        insuranceDO.setPolicyNo(policyNo);
        insuranceDO.setResMsg(msg);
        Integer insuranceStatus = InsuranceStatusEnum.INSURE.getValue();
        if ("SUCCESS".equals(status)) {
            if("applyTeam".equals(service)) {
                insuranceDO.setInsuranceStatus(InsuranceStatusEnum.SUCCESS.getValue());
            }else if ("cancelTeam".equals(service)) {
                insuranceDO.setInsuranceStatus(InsuranceStatusEnum.CANCELLED.getValue());
            }
        } else if ("FAIL".equals(status)) {
            insuranceStatus = InsuranceStatusEnum.FAIL.getValue();
        } else if ("PROCESSING".equals(status)) {
            insuranceStatus = InsuranceStatusEnum.INSURE.getValue();
        }
        insuranceDO.setInsuranceStatus(insuranceStatus);
        insuranceMapper.updateById(insuranceDO);
        tradeOrderMapper.update(new LambdaUpdateWrapper<TradeOrderDO>().set(TradeOrderDO::getIsInsure, insuranceStatus).eq(TradeOrderDO::getOrderNo, insuranceDO.getInsuranceNo()));
    }

    @Override
    public void applyInsuranceNew(Long id) {
        TradeVisitorDO tradeVisitorDO = tradeVisitorMapper.selectById(id);
        Integer isInsure = tradeVisitorDO.getIsInsure();
        if(isInsure != null ) {
            if(isInsure == InsuranceStatusEnum.SUCCESS.getValue()) {
                throw exception0(500, "该订单已投保成功");
            }else if(isInsure == InsuranceStatusEnum.INSURE.getValue()) {
                throw exception0(500, "该订单正在投保中");
            }
        }
        TradeOrderDO orderDO = tradeOrderMapper.selectById(tradeVisitorDO.getOrderId());
        VoyageDO voyage = voyageService.getVoyage(orderDO.getVoyageId());
        InsuranceDO insuranceDO = new InsuranceDO();
        Long insuranceId = IdWorker.getId(insuranceDO);

        InsuranceApplyReqDTO insuranceApplyReqDTO = new InsuranceApplyReqDTO();
        // 个保 - apply; 团保 - applyTeam
        insuranceApplyReqDTO.setService("apply");
        // 回调地址
        insuranceApplyReqDTO.setReplyUrl(notifyUrl);
        // 使用在线支付时填1， 不使用在线支付填0
        insuranceApplyReqDTO.setOnlinePayInd("0");

        //订单对象, 一个订单下可以有一批被保险人
        InsuranceOrderInfoDTO insuranceOrderInfoDTO = new InsuranceOrderInfoDTO();

        //投保人信息
        insuranceOrderInfoDTO.setExternalOrderNo(insuranceId.toString());
        insuranceOrderInfoDTO.setExternalPolicyNumber(insuranceId.toString());
        //TODO: 当前写死
        insuranceOrderInfoDTO.setProductNo(configApi.getConfigValueByKey("insuranceConfig.product_no"));
        insuranceOrderInfoDTO.setBeginDate(DateUtil.format(voyage.getBoardingTime(), "yyyy-MM-dd"));
        insuranceOrderInfoDTO.setEndDate(DateUtil.format(voyage.getLeaveTime(), "yyyy-MM-dd"));
        insuranceOrderInfoDTO.setCurrencyCode("CNY");
        insuranceOrderInfoDTO.setBizType(0);
        insuranceOrderInfoDTO.setApplicantType(1);
        insuranceOrderInfoDTO.setApplicant(configApi.getConfigValueByKey("insuranceConfig.applicant"));
        insuranceOrderInfoDTO.setApplicantCertificateType(6);
        insuranceOrderInfoDTO.setApplicantCertificateNo(configApi.getConfigValueByKey("insuranceConfig.applicant_certificate_no"));
        insuranceOrderInfoDTO.setApplicantBirthday("1990-01-01");
        insuranceOrderInfoDTO.setApplicantSex(1);

        //销售人信息
        insuranceOrderInfoDTO.setSellCorp("营销中心");
        insuranceOrderInfoDTO.setSellDept("营销中心");
        insuranceOrderInfoDTO.setSellUser("营销中心");
        insuranceApplyReqDTO.setOrder(insuranceOrderInfoDTO);

        //被保人对象列表, 其下可多位被保险人, 他们在同个订单下
        List<InsuredDTO> insuredList = new ArrayList<>();
        InsuredDTO insuredDTO = new InsuredDTO();
        insuredDTO.setExternalPolicyNumber(insuranceId.toString());
        String name = tradeVisitorDO.getName();
        insuredDTO.setInsuredName(name);
        if(9 == tradeVisitorDO.getCredentialType()) {
            if(PinyinUtil.isChinese(name.charAt(0))) {
                String pinyin = PinyinUtil.getPinyin(name, " ");
                insuredDTO.setInsuredName(pinyin);
                insuredDTO.setFirstName(pinyin.substring(0, pinyin.indexOf(" ")));
                insuredDTO.setLastName(pinyin.substring(pinyin.indexOf(" ") + 1));
            }else {
                insuredDTO.setInsuredName(name);
                if(name.contains("/")) {
                    insuredDTO.setFirstName(name.substring(0, name.indexOf("/")));
                    insuredDTO.setLastName(name.substring(name.indexOf("/") + 1));
                }else {
                    insuredDTO.setFirstName(name.substring(0, name.indexOf(" ")));
                    insuredDTO.setLastName(name.substring(name.indexOf(" ") + 1));
                }
            }
        }
        insuredDTO.setCertificateType(transCredentialType(tradeVisitorDO.getCredentialType()));
        insuredDTO.setCertificateNo(tradeVisitorDO.getCredentialNo());
        insuredDTO.setBirthday(tradeVisitorDO.getBirthday());
        insuredDTO.setSex(tradeVisitorDO.getGender());
        insuredDTO.setRelation(2); //不是保单持有人，只是被保险人
        insuredList.add(insuredDTO);
        insuranceApplyReqDTO.setInsureds(insuredList);

//        //验证投保信息
//        CommonResult commonResult = insuranceUtil.validateInsuranceRequest(insuranceApplyReqDTO);
//        if (!commonResult.isSuccess()) {
//            throw exception0(commonResult.getCode(), commonResult.getMsg());
//        }
        // 开始投保
        insuranceDO.setOrderId(orderDO.getId());
        insuranceDO.setVisitorId(tradeVisitorDO.getId());
        insuranceDO.setInsuranceStatus(InsuranceStatusEnum.INSURE.getValue());
        insuranceDO.setInsuredNum(1);
        insuranceDO.setInsuranceNo(orderDO.getOrderNo());
        insuranceDO.setTenantId(voyage.getTenantId());
        insuranceDO.setRationType(insuranceOrderInfoDTO.getProductNo());
        insuranceDO.setInsuranceEffectDate(voyage.getBoardingTime());
        insuranceDO.setId(insuranceId);
        // 处理投保逻辑
        CommonResult commonResult = handleInsuranceApply(id, voyage, insuranceDO, tradeVisitorDO, insuranceApplyReqDTO);
        tradeVisitorDO.setIsInsure(InsuranceStatusEnum.INSURE.getValue());
        tradeVisitorMapper.updateById(tradeVisitorDO);
        //保存投保信息
        insuranceMapper.deleteByVisitorId(id);
        insuranceDO.setResMsg(String.valueOf(commonResult.getCheckedData()));
        insuranceMapper.insert(insuranceDO);
    }

    /**
     * 处理投保
     * @param id
     * @param voyage
     * @param insuranceDO
     * @param tradeVisitorDO
     * @param insuranceApplyReqDTO
     * @return
     */
    private CommonResult handleInsuranceApply(Long id, VoyageDO voyage, InsuranceDO insuranceDO, TradeVisitorDO tradeVisitorDO, InsuranceApplyReqDTO insuranceApplyReqDTO) {
        CommonResult commonResult = insuranceUtil.sendInsuranceApply(insuranceApplyReqDTO);
        if (!commonResult.isSuccess()) {
            InsuranceDO insuranceDO1 = insuranceMapper.selectByVisitorId(id);
            String msg = commonResult.getMsg();
            log.error("投保失败:{}", msg);
            if (msg.contains("重复投保")) {
                String insuranceId1 = msg.substring(msg.indexOf("此前已有投保单")+7, msg.indexOf("投保成功")).trim();
                String policyNo = msg.substring(msg.indexOf("对应的保单号是")+7, msg.indexOf("\"}]}"));
                // 判断一下是不是同一个航次开始
                InsuranceDO insuranceDO2 = insuranceMapper.selectById(insuranceId1);
                if(insuranceDO2 != null) {
                    Long orderId = insuranceDO2.getOrderId();
                    TradeOrderDO orderDO = tradeOrderMapper.selectById(orderId);
                    // 不是同一个航次，但是上一个航次的离船时间和当前航次的登船日期为同一天，所以保险开始日期要往后延一天，再次投保
                    if(orderDO != null && !Objects.equals(voyage.getId(), orderDO.getVoyageId())) {
                        LocalDateTime boardingTime = voyage.getBoardingTime();
                        LocalDateTime offset = LocalDateTimeUtil.offset(boardingTime, 1, ChronoUnit.DAYS);
                        insuranceApplyReqDTO.getOrder().setBeginDate(DateUtil.format(offset, "yyyy-MM-dd"));
                        // 递归去投保，直到成功
                        commonResult = handleInsuranceApply(id, voyage, insuranceDO, tradeVisitorDO, insuranceApplyReqDTO);
                        return commonResult;
                    }
                }
                // 判断是否同一个航次结束
                if(insuranceDO1 != null) {
                    insuranceDO1.setInsuranceStatus(InsuranceStatusEnum.SUCCESS.getValue());
                    insuranceMapper.deleteById(insuranceDO1.getId());
                    insuranceDO1.setId(Long.valueOf(insuranceId1));
                    insuranceDO1.setPolicyNo(policyNo);
                    insuranceMapper.insert(insuranceDO1);
                }else {
                    insuranceDO.setInsuranceStatus(InsuranceStatusEnum.SUCCESS.getValue());
                    insuranceDO.setId(Long.valueOf(insuranceId1));
                    insuranceDO.setPolicyNo(policyNo);
                    insuranceMapper.insert(insuranceDO);
                }
                tradeVisitorDO.setIsInsure(InsuranceStatusEnum.SUCCESS.getValue());
                tradeVisitorMapper.updateById(tradeVisitorDO);
            } else {
                if(insuranceDO1 != null) {
                    insuranceDO1.setInsuranceStatus(InsuranceStatusEnum.FAIL.getValue());
                    insuranceDO1.setResMsg(msg);
                    insuranceMapper.updateById(insuranceDO1);
                }else {
                    insuranceDO.setInsuranceStatus(InsuranceStatusEnum.FAIL.getValue());
                    insuranceDO.setResMsg(msg);
                    insuranceMapper.insert(insuranceDO);
                }
                tradeVisitorDO.setIsInsure(InsuranceStatusEnum.FAIL.getValue());
                tradeVisitorDO.setInsureMsg(msg);
                tradeVisitorMapper.updateById(tradeVisitorDO);
            }
            throw exception0(commonResult.getCode(), msg);
        }
        return commonResult;
    }

    @Override
    public List<InsuranceDO> getListByVisitorIds(List<Long> visitorIds) {
        if(CollectionUtils.isAnyEmpty(visitorIds)) {
            return Collections.emptyList();
        }
        return insuranceMapper.selectListByVisitorIds(visitorIds);
    }

    @Override
    public String queryInsurance(Long id) {
        CommonResult commonResult = insuranceUtil.queryInsurance(id.toString());
        if (!commonResult.isSuccess()) {
            throw exception0(commonResult.getCode(), commonResult.getMsg());
        } else if (commonResult.isSuccess()) {
            String str = (String) commonResult.getCheckedData();
//            JSONObject.
        }

        return null;
    }

    @Override
    public void queryInsuranceByOrderId(Long id) {
        InsuranceDO insuranceDO = insuranceMapper.selectById(id);
        CommonResult commonResult = insuranceUtil.queryInsurance(id.toString());
        if (!commonResult.isSuccess()) {
            throw exception0(commonResult.getCode(), commonResult.getMsg());
        }
        //{"phase":"INSURE","externalOrderNo":"tys-20261001-YC-14",
        // "paiedAmount":"10.00","total":"1","insuredCount":"1","insureProcessingCount":"0",
        // "cancelledCount":"0","cancelProcessingCount":"0","refundedAmount":"0.0",
        // "cipher":"26e2e422d370653274ee74ef3edc7f93","policies":[{"externalPolicyNumber":"2037220248460828673",
        // "service":"applyTeam","status":"SUCCESS","msg":"投保成功","policyNo":"HW61927008L7JB7J8Q00"}]}
        String checkedData = (String) commonResult.getCheckedData();
        com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(checkedData);
        BigDecimal paiedAmount = jsonObject.getBigDecimal("paiedAmount");
        JSONArray policies = jsonObject.getJSONArray("policies");
        if(policies == null) {
            return;
        }
        com.alibaba.fastjson.JSONObject policy = policies.getJSONObject(0);
        if(policy == null) {
            return;
        }
        String status = policy.getString("status");
        String service = policy.getString("service");
        String msg = policy.getString("msg");
        if("PROCESSING".equals(status)) {
            log.error("保险查询结果，投保中，继续查询"+insuranceDO.getInsuranceNo());
        } else if("FAIL".equals(status)) {
            log.error("保险查询结果，投保失败，不再查询"+insuranceDO.getInsuranceNo());
            Long externalPolicyNumber = policy.getLong("externalPolicyNumber");
            handleInsuranceQuery(paiedAmount, "", externalPolicyNumber, status, service, msg);
        } else if("SUCCESS".equals(status)) {
            log.error("保险查询结果，投保成功，不再查询"+insuranceDO.getInsuranceNo());
            Long externalPolicyNumber = policy.getLong("externalPolicyNumber");
            String policyNo = policy.getString("policyNo");
            handleInsuranceQuery(paiedAmount, policyNo, externalPolicyNumber, status, service, msg);
        }
    }


    @Override
    public void queryInsuranceQuey() {
        List<InsuranceDO> list = insuranceMapper.selectList(new LambdaQueryWrapper<InsuranceDO>().ne(InsuranceDO::getInsuranceStatus, 1));
        for (InsuranceDO insuranceDO : list) {
//            CommonResult commonResult = insuranceUtil.queryInsuranceNew(insuranceDO.getInsuranceNo(), insuranceDO.getOrderId());
            CommonResult commonResult = insuranceUtil.queryInsurance(insuranceDO.getId().toString());
            if (!commonResult.isSuccess()) {
                throw exception0(commonResult.getCode(), commonResult.getMsg());
            }
            //{"phase":"INSURE","externalOrderNo":"tys-20261001-YC-14",
            // "paiedAmount":"10.00","total":"1","insuredCount":"1","insureProcessingCount":"0",
            // "cancelledCount":"0","cancelProcessingCount":"0","refundedAmount":"0.0",
            // "cipher":"26e2e422d370653274ee74ef3edc7f93","policies":[{"externalPolicyNumber":"2037220248460828673",
            // "service":"applyTeam","status":"SUCCESS","msg":"投保成功","policyNo":"HW61927008L7JB7J8Q00"}]}
            String checkedData = (String) commonResult.getCheckedData();
            com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(checkedData);
            BigDecimal paiedAmount = jsonObject.getBigDecimal("paiedAmount");
            JSONArray policies = jsonObject.getJSONArray("policies");
            if(policies == null) {
                continue;
            }
            com.alibaba.fastjson.JSONObject policy = policies.getJSONObject(0);
            if(policy == null) {
                continue;
            }
            String status = policy.getString("status");
            String service = policy.getString("service");
            String msg = policy.getString("msg");
            if("PROCESSING".equals(status)) {
                log.error("保险查询结果，投保中，继续查询"+insuranceDO.getInsuranceNo());
            } else if("FAIL".equals(status)) {
                log.error("保险查询结果，投保失败，不再查询"+insuranceDO.getInsuranceNo());
                Long externalPolicyNumber = policy.getLong("externalPolicyNumber");
                handleInsuranceQuery(paiedAmount, "", externalPolicyNumber, status, service, msg);
            } else if("SUCCESS".equals(status)) {
                log.error("保险查询结果，投保成功，不再查询"+insuranceDO.getInsuranceNo());
                Long externalPolicyNumber = policy.getLong("externalPolicyNumber");
                String policyNo = policy.getString("policyNo");
                handleInsuranceQuery(paiedAmount, policyNo, externalPolicyNumber, status, service, msg);
            }
        }
    }

    /**
     * 电子保单查询
     */
    @Override
    @Transactional
    public void queryEpolicyList(Long id) {
        try {
            InsuranceDO insurance = getInsurance(id);
            JSONObject head = this.getCommonHead(insurance.getRationType(), insurance.getRiskCode(), "QUERYEPOLICY");
            head.set("orderNo", insurance.getInsuranceNo());

            List<JSONObject> queryEpolicyList = new ArrayList<>();
            JSONObject queryEpolicy = new JSONObject();
            //投保时传入的投保人姓名
            queryEpolicy.set("holderName", insurance.getHolderName());
            //投保时传入的投保人证件号码
            queryEpolicy.set("holderIdNo", insurance.getHolderNo());
            //投保成功时返回的保单号码
            queryEpolicy.set("policyNo", insurance.getPolicyNo());
            //是否需要电子保单，Y需要
            queryEpolicy.set("isNeedBD", "Y");
            queryEpolicy.set("returnType", "1");
            queryEpolicyList.add(queryEpolicy);

            JSONObject queryEpolicyRequest = new JSONObject();
            queryEpolicyRequest.set("head", head);
            queryEpolicyRequest.set("queryEpolicyList", queryEpolicyList);

            JSONObject insuranceRequest = new JSONObject();
            insuranceRequest.set("appKey", configApi.getConfigValueByKey("insurance.appKey"));
            insuranceRequest.set("timestamp", System.currentTimeMillis() + "");
            insuranceRequest.set("serviceName", "QUERYEPOLICY");
            insuranceRequest.set("version", configApi.getConfigValueByKey("insurance.version"));
            insuranceRequest.set("encryptType", ENCRYPT_TYPE);
            insuranceRequest.set("bizContent", JSONUtil.toJsonStr(queryEpolicyRequest));
            String publicKey = configApi.getConfigValueByKey("insurance.publicKey");
            String privateKey = configApi.getConfigValueByKey("insurance.privateKey");
            //加密加签
            InsuranceRequestHelper.encryptAndSign(insuranceRequest, publicKey, privateKey);
            String url = configApi.getConfigValueByKey("insurance.url");
            log.error("电子保险查询 data={}", JSONUtil.toJsonStr(insuranceRequest));
            String result = HttpUtil.createPost(url + "/QUERYEPOLICY").contentType("application/json").body(JSONUtil.toJsonStr(insuranceRequest)).timeout(15000).execute().body();
            log.error("电子保险查询 result={}", result);
            JSONObject insuranceResponse = JSONUtil.parseObj(result);

            InsuranceRequestHelper.checkSignAndDecrypt(insuranceResponse, publicKey, privateKey);
            // 0:失败 1:成功
            if ("200".equals(insuranceResponse.get("code"))) {
                String data = insuranceResponse.get("data").toString();
                if (data != null) {
                    Map queryEpolicyResult = JSONUtil.toBean(data, Map.class);
                    if (queryEpolicyResult != null && queryEpolicyResult.get("epolicyResults") != null) {
                        List<Map> epolicyResults = MapUtil.get(queryEpolicyResult, "epolicyResults", List.class);
                        List<Map<String, Object>> epolicyList = MapUtil.get(epolicyResults.get(0), "epolicyList", List.class);
                        if (CollUtil.isNotEmpty(epolicyList)) {
                            Map<String, Object> epolicyDtlMap = epolicyList.get(0);
                            if (epolicyDtlMap != null && "success".equalsIgnoreCase(MapUtil.getStr(epolicyDtlMap, "code"))) {
                                LambdaUpdateWrapper<InsuranceDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
                                lambdaUpdateWrapper.set(InsuranceDO::getElectronicPolicy, MapUtil.getStr(epolicyDtlMap, "epolicyLink"));
                                lambdaUpdateWrapper.set(InsuranceDO::getInsuranceStatus, InsuranceStatusEnum.SUCCESS.getValue());
                                lambdaUpdateWrapper.eq(InsuranceDO::getId, insurance.getId());
                                insuranceMapper.update(lambdaUpdateWrapper);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("电子保单查询error:{}", e.getMessage());
        }
    }

    private JSONObject getCommonHead(String rationType, String riskCode, String type) {
        JSONObject head = new JSONObject();
        head.set("sysFlag", configApi.getConfigValueByKey("insurance.sysflag"));
        head.set("taCode", configApi.getConfigValueByKey("insurance.taCode"));
        head.set("sendTime", DateUtil.format(new Date(), "yyyyMMddhhmmss"));
        head.set("version", configApi.getConfigValueByKey("insurance.version"));
        head.set("type", type);
        head.set("rationType", rationType);
        head.set("riskCode", riskCode);
        return head;
    }

}
