package cn.timer.api.controller.insure;

import cn.timer.api.bean.insure.*;
import cn.timer.api.bean.yggl.YgglMainEmp;
import cn.timer.api.config.exception.CustomException;
import cn.timer.api.utils.HttpUtils;
import cn.timer.api.utils.ResultUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mysql.cj.util.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @Description TODO
 * @Author wgd
 * @Date 2022/3/3 8:45
 */
@Api(tags = "8.0回调接口")
@RestController
@Transactional
@RequestMapping(value = "/callBack/policy", produces = {"application/json"})
public class CallBackContorll {
    private static final Logger log = LoggerFactory.getLogger(CallBackContorll.class);
    @Value("${insure.appid}")
    private String appid;
    @Value("${insure.secret}")
    private String secret;
    /*保全测试用*/
    @Value("${insure.appidq}")
    private String appidq;
    @Value("${insure.secretq}")
    private String secretq;

    @Value("${insure.getPolicyUrl}")
    private String getPolicyUrl;

    @Value("${BASE_API_URL}")
    private String base_api_url;

    /*保全支付*/
    @Value("${insure.batchToPayUrl}")
    private String batchToPayUrl;


    /*支付回调*/
    @Value("${pay_page}")
    private String payPage;

    @PostMapping(value = "/insuredCallBack")
    @ApiOperation(value = "6.投保核保回调-弃用", httpMethod = "POST", notes = "投保申请回调")
    private Map insuredCallBack(HttpServletRequest request, @RequestParam String pid, @RequestParam String sign, @RequestParam String timestamp) throws IOException {
        Map map = Maps.newHashMap();
        map.put("status", "error");
        if (StringUtils.isNullOrEmpty(pid) || StringUtils.isNullOrEmpty(sign) && StringUtils.isNullOrEmpty(timestamp)) {
            return map;
        }
        if (!pid.equals(appidq)) {
            return map;
        }
        InputStream is = null;
        is = request.getInputStream();
        StringBuilder sb = new StringBuilder();
        byte[] b = new byte[4096];
        for (int n; (n = is.read(b)) != -1; ) {
            sb.append(new String(b, 0, n));
        }
        String value = DigestUtils.md5Hex(appidq + secretq + timestamp + sb.toString());
        if (!value.equals(sign)) {
            return map;
        }
        CallBack callBack = JSONObject.parseObject(sb.toString(), CallBack.class);
        Map trueMap = Maps.newHashMap();
        trueMap.put("status", "1");
        return trueMap;
    }

    @PostMapping(value = "/addpPayCallBack")
    @ApiOperation(value = "11.增员核保回调", httpMethod = "POST", notes = "增员核保回调")
    @Transactional(rollbackFor = Exception.class)
    public Map addpPayCallBack(HttpServletRequest request, @RequestParam String pid, @RequestParam String sign, @RequestParam String timestamp)  {
        /*核保*/
        /*如果是在线支付的话*/
        Map map = Maps.newHashMap();
        map.put("status", "error");
        if (StringUtils.isNullOrEmpty(pid) || StringUtils.isNullOrEmpty(sign) && StringUtils.isNullOrEmpty(timestamp)) {
            return map;
        }
        if (!pid.equals(appidq)) {
            return map;
        }
        try {
            InputStream is = null;
            is = request.getInputStream();
            StringBuilder sb = new StringBuilder();
            byte[] b = new byte[4096];
            for (int n; (n = is.read(b)) != -1; ) {
                sb.append(new String(b, 0, n));
            }
            String value = DigestUtils.md5Hex(pid + secretq + timestamp + sb.toString());
            if (!value.equals(sign)) {
                throw new CustomException("增员核保回调验签失败");
            }
            CallBack callBack = JSONObject.parseObject(sb.toString(), CallBack.class);
            log.info("增员核保回调参数:- {}",JSON.toJSONString(callBack));
            if (callBack.getCallback_type().equals("1")) {
                List<InsureUser> insureUserList = InsureUser.builder().build().selectList(new QueryWrapper<InsureUser>().lambda().eq(InsureUser::getTransId, callBack.getOrder_import_info().getThird_uuid()));

                InsurePolicy insurePolicy = InsurePolicy.builder().id(insureUserList.get(0).getPolicyId()).build().selectById();
                if (callBack.getStatus().equals("1")) {
                    if (insureUserList.size() > 0) {
                        insurePolicy.setStatus("4");/*设置为支付状态*/
                        insurePolicy.setUpdateTime(new Date());
                        InsurePay insurePay = InsurePay.builder().payStatus(1).
                                serialNumber(callBack.getOrder_import_info().getUuid()).policyId(insurePolicy.getId()).amount(Double.parseDouble(callBack.getOrder_import_info().getTotal_money())).build();
                        insurePay.insert();
                        insurePolicy.setPayId(insurePay.getId());
                        insurePolicy.updateById();

                    }
                    insureUserList.stream().forEach(v->{
                        v.setBatchNo(callBack.getOrder_import_info().getUuid());
                        v.updateById();
                    });
                    InsureLog.builder().type(7)
                            .requestData(sb.toString()).createTime(new Date()).requestType(1).returnBody(JSONObject.toJSONString(callBack)).requestPath(base_api_url + "/callBack/policy/addpPayCallBack")
                            .returnCode(callBack.getStatus()).returnMsg("核保通过").policyId(insurePolicy.getId()).build().insert();
                } else {
                    insureUserList.stream().forEach(v -> {
                        v.setStatus("2");
                        v.setInsureStatus(2);
                        v.updateById();
                    });
                    insurePolicy.setStatus("1");
                    insurePolicy.updateById();
                    List<Map> errMap = callBack.getOrder_import_info().getErr_list();
                    String errorMsg = "";
                    if (errMap.size() > 0) {
                        for (int i = 0; i < errMap.size(); i++) {
                            errorMsg = errorMsg + ("姓名:" + errMap.get(i).get("name").toString() + ",错误:" + errMap.get(i).get("err_content").toString() + ',');
                        }
                    } else {
                        errorMsg = callBack.getErr_msg();
                    }
                    //TODO 写入日志
                    InsureLog.builder().type(7)
                            .requestData(sb.toString()).createTime(new Date()).requestType(1).returnBody(JSONObject.toJSONString(callBack)).requestPath(base_api_url + "/callBack/policy/addpPayCallBack")
                            .returnCode(callBack.getStatus()).returnMsg(errorMsg).policyId(insurePolicy.getId()).build().insert();
                }
            }
        } catch (IndexOutOfBoundsException e){
            log.error("增员核保回调异常:无法查找到该订单",e);
            return map;
        }
        catch (Exception e) {
            log.error("增员核保回调异常:",e);
            throw new CustomException("增员核保回调异常");
        } finally {
            Map trueMap = Maps.newHashMap();
            trueMap.put("status", "1");
            return trueMap;
        }
    }

    @PostMapping(value = "/CallBack")
    @ApiOperation(value = "7.保全增员申请回调", httpMethod = "POST", notes = "投保申请回调")
    @Transactional(rollbackFor = Exception.class)
    public Map callBack(HttpServletRequest request, @RequestParam String pid, @RequestParam String sign, @RequestParam String timestamp)  {
        Map map = Maps.newHashMap();
        map.put("status", "error");
        if (StringUtils.isNullOrEmpty(pid) || StringUtils.isNullOrEmpty(sign) && StringUtils.isNullOrEmpty(timestamp)) {
            return map;
        }
        if (!pid.equals(appidq)) {
            return map;
        }
        try {
            InputStream is = null;
            is = request.getInputStream();
            StringBuilder sb = new StringBuilder();
            byte[] b = new byte[4096];
            for (int n; (n = is.read(b)) != -1; ) {
                sb.append(new String(b, 0, n));
            }
            String value = DigestUtils.md5Hex(pid + secretq + timestamp + sb.toString());
            if (!value.equals(sign)) {
                throw new CustomException("保全增员申请回调验签失败");
            }
            Map paramsMap = Maps.newHashMap();
            paramsMap.put("pid", pid);
            paramsMap.put("timestamp", timestamp);
            paramsMap.put("sign", sign);
            CallBack callBack = JSONObject.parseObject(sb.toString(), CallBack.class);
            log.info("保全增员申请回调:- {}",JSON.toJSONString(callBack));
            List<InsureUser> list = InsureUser.builder().build().selectList(new QueryWrapper<InsureUser>().lambda().eq(InsureUser::getTransId, callBack.getOrder_import_info().getThird_uuid()));
            if(list.size()<=0){
                list = InsureUser.builder().build().selectList(new QueryWrapper<InsureUser>().lambda().eq(InsureUser::getBatchNo, callBack.getOrder_import_info().getUuid()));
            }
            InsurePolicy insurePolicy = InsurePolicy.builder().build().selectOne(new QueryWrapper<InsurePolicy>().lambda().eq(InsurePolicy::getPolicyNo, list.get(0).getPolicyNo()));
            if (callBack.getStatus().equals("1")) {
                list.forEach(i -> {
                    i.setInsureStatus(1);
                    i.setStatus("1");
                    i.updateById();
                    YgglMainEmp.builder().isInsure(1).build().update(new QueryWrapper<YgglMainEmp>().lambda().eq(YgglMainEmp::getId, i.getUserId()));
                });
                List<InsureUser> oldlist = InsureUser.builder().build().selectList(new QueryWrapper<InsureUser>().lambda().eq(InsureUser::getReplaceTransId, callBack.getOrder_import_info().getUuid()));
                if (oldlist != null && oldlist.size() > 0) {
                    oldlist.forEach(i -> {
                        i.setInsureStatus(4);
                        i.setStatus("2");
                        i.updateById();
                        YgglMainEmp.builder().isInsure(0).build().update(new QueryWrapper<YgglMainEmp>().lambda().eq(YgglMainEmp::getId, i.getUserId()));
                    });
                }
                if (oldlist == null || oldlist.size() == 0) {
                    insurePolicy.setTotalPremium(String.valueOf(Double.valueOf(insurePolicy.getTotalPremium()) + Double.valueOf(callBack.getOrder_import_info().getTotal_money())));
                }
                insurePolicy.setPolicyFile(callBack.getOrder_import_info().getEndorsement_file());
                InsureLog.builder().requestParam(JSONObject.toJSONString(paramsMap)).type(7)
                        .requestData(sb.toString()).createTime(new Date()).requestType(1).returnBody(JSONObject.toJSONString(callBack)).requestPath(base_api_url + "/callBack/policy/CallBack")
                        .returnCode(callBack.getStatus()).returnMsg("保单增员-更新成功").policyId(insurePolicy.getId()).build().insert();
            } else {
                String errorMsg = "";
                InsureLog insureLog = InsureLog.builder().build().selectOne(new QueryWrapper<InsureLog>().lambda().eq(InsureLog::getTransId, callBack.getOrder_import_info().getThird_uuid()));
                if(callBack.getOrder_import_info().getErr_list()!=null) {
                    List<Map> errMap = callBack.getOrder_import_info().getErr_list();
                    if (errMap.size() > 0) {
                        for (int i = 0; i < errMap.size(); i++) {
                            errorMsg = errorMsg + ("姓名:" + errMap.get(i).get("name").toString() + ",错误:" + errMap.get(i).get("err_content").toString() + ',');
                        }
                    } else {
                        errorMsg = callBack.getErr_msg();
                    }
                }
                /*在保司系统点击取消申报时候会回调申报失败*/
                if(StringUtils.isNullOrEmpty(callBack.getOrder_import_info().getSingle_serial_no())){
                    list.forEach(i -> {
                        i.setInsureStatus(2);
                        i.setStatus("2");
                        i.updateById();
                        YgglMainEmp.builder().isInsure(0).build().update(new QueryWrapper<YgglMainEmp>().lambda().eq(YgglMainEmp::getId, i.getUserId()));
                    });
                }
                //TODO 写入日志
                InsureLog.builder().requestParam(JSONObject.toJSONString(paramsMap)).type(7)
                        .requestData(sb.toString()).createTime(new Date()).requestType(1).returnBody(JSONObject.toJSONString(callBack)).requestPath(base_api_url + "/callBack/policy/CallBack")
                        .returnCode(callBack.getStatus()).returnMsg(errorMsg).policyId(insurePolicy.getId()).build().insert();
            }
            /*无论此次申请成功还是失败这笔单都需要重新申报*/
            insurePolicy.setUpdateTime(new Date());
            insurePolicy.setStatus("1");
            insurePolicy.updateById();

        } catch (IndexOutOfBoundsException e){
            log.error("保全增员申请回调:无法查找到该订单",e);
            return map;
        }
        catch (Exception e) {
            log.error("保全增员申请回调异常:",e);
            throw new CustomException("保全增员申请回调异常");
            //TODO 写入日志
//            InsureLog.builder().requestParam(JSONObject.toJSONString(paramsMap)).type(7)
//                    .requestData(sb.toString()).createTime(new Date()).requestType(1).returnBody(JSONObject.toJSONString(callBack)).requestPath(base_api_url + "/callBack/policy/CallBack")
//                    .returnCode(callBack.getStatus()).returnMsg(errorMsg).policyId(insurePolicy.getId()).build().insert();
        } finally {
            Map trueMap = Maps.newHashMap();
            trueMap.put("status", "1");
            return trueMap;
        }


    }

    @GetMapping(value = "/payStatus")
    @ApiOperation(value = "8.支付完成跳转", httpMethod = "GET", notes = "用于支付时跳回我们系统更新状态")
    @Transactional(rollbackFor = Exception.class)
    public ModelAndView callBackPayStatus(HttpServletRequest request, @RequestParam Integer policyId)  {
        InsurePolicy insurePolicy = InsurePolicy.builder().id(policyId).build().selectById();
        InsurePay insurePay = InsurePay.builder().id(insurePolicy.getPayId()).build().selectById();
        insurePay.setPayTime(new Date());
        insurePay.updateById();
        InsureLog.builder().type(7).createTime(new Date()).requestType(2).requestPath(base_api_url + "/callBack/policy/payStatus?policyId=" + policyId)
                .returnCode("suc").returnMsg("用户已支付" + insurePay.getAmount() + "元,等待更新保单状态").policyId(policyId).build().insert();
        ModelAndView mav = new ModelAndView();
        mav.setViewName("redirect:" + payPage + "/#/payPage?amount="+insurePay.getAmount());
        return mav;
    }

    @PostMapping(value = "/payCallBack")
    @ApiOperation(value = "9.投保支付收银台回调", httpMethod = "POST", notes = "支付完成跳转")
    @Transactional(rollbackFor = Exception.class)
    public Map payCallBack(HttpServletRequest request, @RequestParam String pid, @RequestParam String sign, @RequestParam String timestamp)  {
        try {
            InputStream is = null;
            is = request.getInputStream();
            StringBuilder sb = new StringBuilder();
            byte[] b = new byte[4096];
            for (int n; (n = is.read(b)) != -1; ) {
                sb.append(new String(b, 0, n));
            }
            String value = DigestUtils.md5Hex(pid + secret + timestamp + sb.toString());
            if (!value.equals(sign)) {
                throw new CustomException("投保支付收银台回调验签失败");
            }
            PayCallBack callBack = JSONObject.parseObject(sb.toString(), PayCallBack.class);
            InsurePay insurePay = InsurePay.builder().build().selectOne(new QueryWrapper<InsurePay>().lambda().eq(InsurePay::getSerialNumber, callBack.getSerial_number()));
            insurePay.setAmount(Double.valueOf(callBack.getAmount()));
            insurePay.setPayStatus(Integer.parseInt(callBack.getPay_status()));
            insurePay.setPaySerialNo(callBack.getPay_serial_no());
            insurePay.setSerialNumber(callBack.getSerial_number());
            insurePay.setPayType(callBack.getPay_type());
            insurePay.updateById();
            InsureLog.builder().type(7).createTime(new Date()).requestType(1).requestPath(base_api_url + "/callBack/policy/payCallBack")
                    .returnCode("suc").returnMsg("确认支付成功,支付方式:" + insurePay.getPayType() + ",支付金额:" + callBack.getAmount()).policyId(insurePay.getPolicyId()).build().insert();
            /*调用出单接口更新保单状态*/
            Map paramsMap = Maps.newHashMap();
            paramsMap.put("pid", pid);
            paramsMap.put("timestamp", timestamp);
            paramsMap.put("sign", sign);
            Map bodyMap = Maps.newHashMap();
            bodyMap.put("quotation_id", callBack.getSerial_number());
            String data = HttpUtils.sendPost(getPolicyUrl, InsureContorll.setParams(JSONObject.toJSONString(bodyMap), appid, secret), bodyMap);
            Map dataMap = JSONObject.parseObject(data, Map.class);
            if (dataMap.size() > 0) {
                if (dataMap.get("errcode").toString().equals("suc") || dataMap.get("errcode").toString().equals("e25")) {
                    //TODO 如果是E25则将保单设为出单中,更新交由保单出单回调做,设为出单状态是因为之后可以手动校验
                    InsurePolicy insurePolicy = InsurePolicy.builder().id(insurePay.getPolicyId()).build().selectById();
                    insurePolicy.setStatus("2");
                    insurePolicy.setUpdateTime(new Date());
                    insurePolicy.updateById();
                }
                InsureLog.builder().requestParam(JSONObject.toJSONString(InsureContorll.setParams(JSONObject.toJSONString(bodyMap), appid, secret))).type(7)
                        .requestData(JSONObject.toJSONString(bodyMap)).createTime(new Date()).requestType(1).returnBody(data).requestPath(getPolicyUrl)
                        .returnCode(dataMap.get("errcode").toString()).policyId(insurePay.getPolicyId()).returnMsg(dataMap.get("errmsg").toString()).build().insert();
            }
        } catch (Exception e) {
            log.error("投保支付收银台回调异常:",e);
            throw new CustomException("投保支付收银台回调异常");
        } finally {
            Map map = Maps.newHashMap();
            map.put("status", "1");
            return map;
        }
    }

    @PostMapping(value = "/issueCallback")
    @ApiOperation(value = "10.保单出单回调", httpMethod = "POST", notes = "支付完成跳转")
    @Transactional(rollbackFor = Exception.class)
    public Map issueCallback(HttpServletRequest request, @RequestParam String pid, @RequestParam String sign, @RequestParam String timestamp)  {
        try {
            InputStream is = null;
            is = request.getInputStream();
            StringBuilder sb = new StringBuilder();
            byte[] b = new byte[4096];
            for (int n; (n = is.read(b)) != -1; ) {
                sb.append(new String(b, 0, n));
            }
            String value = DigestUtils.md5Hex(pid + secret + timestamp + sb.toString());
            if (!value.equals(sign)) {
                throw new CustomException("保单出单回调验签失败");
            }
            PolicyCallBack callBack = JSONObject.parseObject(sb.toString(), PolicyCallBack.class);
            InsurePay insurePay = InsurePay.builder().build().selectOne(new QueryWrapper<InsurePay>().lambda().eq(InsurePay::getSerialNumber, callBack.getSerial_number()));
            InsurePolicy insurePolicy = InsurePolicy.builder().id(insurePay.getPolicyId()).build().selectById();
            List<InsureUser> userList = InsureUser.builder().build().selectList(new QueryWrapper<InsureUser>().lambda().eq(InsureUser::getPolicyId, insurePolicy.getId()));
            insurePolicy.setUpdateTime(new Date());
            if (callBack.getStatus().equals("1")) {
                insurePay.setPayStatus(2);
                insurePolicy.setPolicyNo(callBack.getPolicy_no());
                insurePolicy.setTotalPremium(callBack.getTotal_premium());
                insurePolicy.setPolicyFile(callBack.getPolicy_file());
                insurePolicy.setStatus("1");
                insurePolicy.setKitUrl(callBack.getKit_url());
                userList.forEach(u -> {
                    u.setStatus("1");
                    u.setInsureStatus(1);
                    u.updateById();
                });
            } else {
                insurePay.setPayStatus(3);
                insurePolicy.setStatus("3");
                userList.forEach(u -> {
                    u.setStatus("2");
                    u.setInsureStatus(2);
                    u.updateById();
                });
            }
            insurePolicy.updateById();
            insurePay.updateById();
            InsureLog.builder().type(7).createTime(new Date()).requestType(1).returnBody(sb.toString()).requestPath(getPolicyUrl)
                    .returnCode(callBack.getStatus()).policyId(insurePay.getPolicyId()).returnMsg(callBack.getErr_msg()).build().insert();
        } catch (Exception e) {
            log.error("保单出单回调:",e);
            throw new CustomException("保单出单回调");
        } finally {
            Map map = Maps.newHashMap();
            map.put("status", "1");
            return map;
        }
    }

    @PostMapping(value = "/batchPayCallback")
    @ApiOperation(value = "增员支付回调", httpMethod = "POST", notes = "增员支付回调")
    @Transactional(rollbackFor = Exception.class)
    public Map batchPayCallback(HttpServletRequest request,@RequestParam String pid, @RequestParam String sign, @RequestParam String timestamp)  {

        try {
            InputStream is = null;
            is = request.getInputStream();
            StringBuilder sb = new StringBuilder();
            byte[] b = new byte[4096];
            for (int n; (n = is.read(b)) != -1; ) {
                sb.append(new String(b, 0, n));
            }
            String value = DigestUtils.md5Hex(pid + secretq + timestamp + sb.toString());
            if (!value.equals(sign)) {
                throw new CustomException("增员支付回调验签失败");
            }
            BatchPayCallBack callBack = JSONObject.parseObject(sb.toString(), BatchPayCallBack.class);
            InsurePay insurePay = InsurePay.builder().build().selectOne(new QueryWrapper<InsurePay>().lambda().eq(InsurePay::getSerialNumber, callBack.getOrder_import_uuid()));
            if (insurePay != null) {
                insurePay.setPayStatus(Integer.parseInt(callBack.getPay_status()));
                insurePay.setAmount(Double.parseDouble(callBack.getPay_money()));
                insurePay.setPayType(callBack.getMethod());
                insurePay.updateById();
            }
            InsureLog.builder().type(7).createTime(new Date()).requestType(1).returnBody(sb.toString()).requestPath(base_api_url + "/callBack/policy/batchPayCallback")
                    .returnCode(callBack.getPay_status()).policyId(insurePay.getPolicyId()).returnMsg("确认支付成功,支付方式:" + insurePay.getPayType() + ",支付金额:" + insurePay.getAmount()).build().insert();
        } catch (Exception e) {
            log.error("增员支付回调异常:",e);
            throw new CustomException("增员支付回调异常");
        } finally {
            Map map = Maps.newHashMap();
            map.put("status", "1");
            return map;
        }
    }
}