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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
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.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.InsuranceData;
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.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.RLock;
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.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

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) {

        TradeOrderRespVO orderInfo = tradeOrderMapper.getOrderInfo(orderId);
        VoyageDO voyage = voyageService.getVoyage(orderInfo.getVoyageId());

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

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

        //投保人信息
        insuranceOrderInfoDTO.setExternalOrderNo(orderInfo.getOrderNo());
        insuranceOrderInfoDTO.setExternalPolicyNumber(orderInfo.getId().toString());
        insuranceOrderInfoDTO.setTeamCode(orderInfo.getGroupNo());
        //TODO: 当前写死
        insuranceOrderInfoDTO.setProductNo("SIGCOE_ZBJC1");
        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("宜昌长江国际旅行社有限公司");
        insuranceOrderInfoDTO.setApplicantCertificateType(6);
        insuranceOrderInfoDTO.setApplicantCertificateNo("91420500737133035K");
        insuranceOrderInfoDTO.setApplicantBirthday("1990-01-01");
        insuranceOrderInfoDTO.setApplicantSex(1);

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

        //被保人对象列表, 其下可多位被保险人, 他们在同个订单下
        List<TradeVisitorRespVO> tradeVisitorRespVOS = tradeVisitorMapper.queryVisitorByOrderId(orderId);
        List<InsuredDTO> insuredList = new ArrayList<>();
        tradeVisitorRespVOS.forEach(tradeVisitorRespVO -> {
            InsuredDTO insuredDTO = new InsuredDTO();
            insuredDTO.setExternalPolicyNumber(tradeVisitorRespVO.getId().toString());
            insuredDTO.setInsuredName(tradeVisitorRespVO.getName());
            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());
        }
        // 开始投保
        commonResult = insuranceUtil.sendInsuranceApply(insuranceApplyReqDTO);
        if(!commonResult.isSuccess()) {
            throw exception0(commonResult.getCode(),commonResult.getMsg());
        }
        //保存投保信息
        InsuranceDO insuranceDO = new InsuranceDO();
        insuranceDO.setOrderId(orderId);
//            insuranceDO.setAgencyOrderId(applyReqVO.getAgencyOrderId());
        insuranceDO.setInsuranceStatus(InsuranceStatusEnum.INSURE.getValue());
        insuranceDO.setInsuredNum(insuredList.size());
//            insuranceDO.setHolderName(applyReqVO.getHolderName());
//            insuranceDO.setHolderNo(applyReqVO.getHolderNo());
//            insuranceDO.setRationType(applyReqVO.getRationType());
//            insuranceDO.setRiskCode(riskCode);
//            insuranceDO.setInsuranceEffectDate(LocalDateTime.ofInstant(applyReqVO.getTravelDate().toInstant(), ZoneId.systemDefault()));
        Long id = IdWorker.getId(insuranceDO);
        insuranceDO.setId(id);
        insuranceMapper.insert(insuranceDO);
        // 发送查询投保接口通知
        tradePublishUtils.publishInsuranceQueryMsg(orderInfo.getOrderNo(),0);
        return true;
    }

    /**
     * 处理系统证件枚举与保险证件枚举转换
     * @param credentialType
     * @return
     */
    private Integer transCredentialType(Integer credentialType) {
        Integer transCredentialType = 6; //默认为其他
        switch (credentialType) {
            case 0:
                transCredentialType = 1;
                break;
            case 1:
                transCredentialType = 3;
                break;
            case 2: case 3: case 4: case 6: case 7: case 8: case 9: case 99:
                transCredentialType = 6;
                break;
            case 5:
                transCredentialType = 5;
                break;
        }
        return transCredentialType;
    }

    /**
     * 退保
     *
     * @param orderId
     */
    @Override
    @Transactional
    public void applyCancelInsurance(Long orderId) {
        TradeOrderRespVO orderInfo = tradeOrderMapper.getOrderInfo(orderId);
        VoyageDO voyage = voyageService.getVoyage(orderInfo.getVoyageId());

        //获取投保记录，投保保单号
        InsuranceDO insuranceDO = insuranceMapper.selectByOrderId(orderId);

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

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

        insuranceOrderInfoDTO.setExternalOrderNo(orderInfo.getOrderNo());
        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(orderInfo.getId().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 orderId, String status) {
        InsuranceDO insuranceDO = insuranceMapper.selectByOrderId(orderId);
        insuranceDO.setAmount(amount);
        insuranceDO.setPolicyNo(policyNo);
        Integer insuranceStatus = InsuranceStatusEnum.INSURE.getValue();
        if("SUCCESS".equals(status)) {
            insuranceStatus = InsuranceStatusEnum.SUCCESS.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);
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(orderId);
        tradeOrderDO.setIsInsure(insuranceStatus);
        tradeOrderMapper.updateById(tradeOrderDO);
    }

    /**
     * 电子保单查询
     */
    @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 (insuranceResponse != null && "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;
    }

}
