package com.cool.store.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.cool.store.constants.CommonConstants;
import com.cool.store.context.CurrentUserHolder;
import com.cool.store.context.LoginUserInfo;
import com.cool.store.dao.EnterpriseUserDAO;
import com.cool.store.dao.HyInterviewDAO;
import com.cool.store.dao.HyPartnerBaseInfoDAO;
import com.cool.store.dto.log.CreateQualifyVerifyDTO;
import com.cool.store.dto.log.LogBasicDTO;
import com.cool.store.dto.mdm.AccessTokenDTO;
import com.cool.store.dto.response.MDMResultDTO;
import com.cool.store.entity.*;
import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.enums.InspectionTyeEnum;
import com.cool.store.enums.OperateTypeEnum;
import com.cool.store.enums.WorkflowStatusEnum;
import com.cool.store.exception.ApiException;
import com.cool.store.exception.ServiceException;
import com.cool.store.mapper.*;
import com.cool.store.oss.OSSServer;
import com.cool.store.request.*;
import com.cool.store.request.data.flow.KeyText;
import com.cool.store.request.data.flow.SkrRelshipProve;
import com.cool.store.service.FlowService;
import com.cool.store.service.HyPartnerLineInfoService;
import com.cool.store.service.LogService;
import com.cool.store.service.SmsService;
import com.cool.store.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;

/**
 * @Author: young.yu
 * @Date: 2023-06-14  14:22
 * @Description:
 */
@Slf4j
@Service
public class FlowServiceImpl implements FlowService {

    @Value("${hs.mdm.baseUrl:null}")
    private String mdmBaseUrl;

    @Value("${hs.mdm.appkey:null}")
    private String mdmAppKey;

    @Value("${hs.mdm.appsec:null}")
    private String mdmAppSec;

    @Autowired
    private RedisUtilPool redisUtilPool;

    @Autowired
    private HyPartnerLineInfoMapper hyPartnerLineInfoMapper;

    @Autowired
    private EnterpriseUserDAO enterpriseUserDAO;

    @Autowired
    private DingdingUserMapper dingdingUserMapper;

    @Autowired
    private HyPartnerInterviewMapper hyPartnerInterviewMapper;

    @Autowired
    private HyPartnerCertificationInfoMapper hyPartnerCertificationInfoMapper;

    @Autowired
    private OSSServer ossServer;

    @Autowired
    private HyInterviewDAO interviewDAO;

    @Autowired
    private LogService logService;

    @Autowired
    private HyPartnerLineInfoService hyPartnerLineInfoService;
    @Autowired
    private SmsService smsService;

    @Autowired
    private HyPartnerBaseInfoDAO hyPartnerBaseInfoDAO;

    @Autowired
    private HyInspectionMapper inspectionMapper;

    @Override
    @Transactional
    public void createQualifyVerify(CreateQualifyVerifyReq request) throws ApiException, IOException {
        //根据面试id获取面试信息
        HyPartnerInterviewDO hyPartnerInterviewDO = hyPartnerInterviewMapper.selectByPrimaryKeySelective(request.getInterviewId());
        if (Objects.isNull(hyPartnerInterviewDO)) {
            throw new ServiceException(ErrorCodeEnum.INTERVIEW_NOT_EXIST);
        }
        //根据面试状态限制
        if (!hyPartnerInterviewDO.getStatus().equals(Integer.parseInt(WorkflowStatusEnum.INTERVIEW_4.getCode()))) {
            throw new ApiException(ErrorCodeEnum.INTERVIEW_STATUS_ERROR);
        }
        //1.发起加盟商资质审核
        RpcCreateQualifyVerifyReq rpcRequest = new RpcCreateQualifyVerifyReq();
        RpcCreateQualifyVerifyReq.Data dataBody = new RpcCreateQualifyVerifyReq().new Data();
        KeyText fraSource = new KeyText();
        fraSource.setKey("HSAYPartner");
        fraSource.setText("沪上阿姨合伙人");
        dataBody.setFraSource(fraSource);
        //copy properties
        BeanUtil.copyProperties(request, dataBody);
        //日期格式问题
        if (request.getWantSignTime().endsWith("00:00:00")) {
            dataBody.setIntendedSignDate(request.getWantSignTime());
        }
        dataBody.setIntendedSignDate(request.getWantSignTime() + " 00:00:00");
        dataBody.setAmtDeposit(request.getSecurityFund());
        dataBody.setIntendedSigner(request.getIntentionSignerUsername());
        dataBody.setIntendedSignerTel(request.getIntentionSignerMobile());
        dataBody.setSignerEdu(request.getIntentionEdu());
        dataBody.setSkrName(request.getRealControlUsername());
        dataBody.setSkrIDCard(request.getRealControlIdcard());
        dataBody.setSkrRelship(request.getSignerRealControlRelation());
        dataBody.setSkrRelshipOther(request.getSignerOtherRealControlRelation());
        dataBody.setAmtJoin(request.getPartnerFee());
        dataBody.setAmtTechSer(request.getTechnicalServiceFee());
        dataBody.setAmtIntended(request.getIntentionMoney());
        rpcRequest.setData(dataBody);

        //获取授权码
        String authCode = null;
        if (!StringUtils.isBlank(request.getLineId())) {
            //根据线索id获取线索信息
            HyPartnerLineInfoDO hyPartnerLineInfoDO = hyPartnerLineInfoMapper.selectByPrimaryKeySelective(Long.valueOf(request.getLineId()));
            if (!Objects.isNull(hyPartnerLineInfoDO)) {
                String investmentManagerId = hyPartnerLineInfoDO.getInvestmentManager();
                String developmentDirectorId = request.getDevtDirectorId();
                EnterpriseUserDO investmentManager = enterpriseUserDAO.getUserInfoById(investmentManagerId);
                EnterpriseUserDO developmentDirector = enterpriseUserDAO.getUserInfoById(developmentDirectorId);
                //招商经理信息（客户经理）
                if (!Objects.isNull(investmentManager)) {
                    String jobNumber = investmentManager.getJobnumber();
                    String mobile = investmentManager.getMobile();
                    DingdingUserDO dingdingUserDO = dingdingUserMapper.selectDingDingUserByMobile(mobile);
                    if (Objects.isNull(dingdingUserDO) || StringUtils.isEmpty(dingdingUserDO.getUserid())) {
                        throw new ServiceException(ErrorCodeEnum.DINGDING_USER_NOT_EXIST);
                    }
                    rpcRequest.setDingUserId(dingdingUserDO.getUserid());
                    authCode = generateAuthCode(jobNumber);
                    rpcRequest.getData().setAuthCode(authCode);
                    rpcRequest.getData().setUserCustDingUserId(dingdingUserDO.getUserid());
                }
                //开发主管信息
                if (!Objects.isNull(developmentDirector)) {
                    String mobile = developmentDirector.getMobile();
                    DingdingUserDO dingdingUserDO = dingdingUserMapper.selectDingDingUserByMobile(mobile);
                    if (Objects.isNull(dingdingUserDO) || StringUtils.isEmpty(dingdingUserDO.getUserid())) {
                        throw new ServiceException(ErrorCodeEnum.DINGDING_USER_NOT_EXIST);
                    }
                    rpcRequest.getData().setUserSiteDevDingUserId(dingdingUserDO.getUserid());
                }
            }

        }
        //通过 rpc 请求审核系统获取返回数据
        //上传证明文件数据
        if (StringUtils.isNotEmpty(request.getSignerRealControlRelationCert())) {
            List<String> certFileList = Arrays.asList(request.getSignerRealControlRelationCert().split(","));
            List<SkrRelshipProve> relshipProves = OSSFileToMDMFile(certFileList);
            rpcRequest.getData().setSkrRelshipProve(relshipProves);
        }
        Map<String, String> qualifyVerifyRespData = JSON.parseObject(createQualifyVerify(rpcRequest), new TypeReference<HashMap<String,String>>() {});

        //2.更新审核信息
        HyPartnerCertificationInfoDO partnerCertificationInfoDO = new HyPartnerCertificationInfoDO();
        partnerCertificationInfoDO.setPartnerId(request.getPartnerId());
        partnerCertificationInfoDO.setPartnerLineId(Long.valueOf(request.getLineId()));
        partnerCertificationInfoDO.setPartnerInterviewId(Long.valueOf(request.getInterviewId()));
        partnerCertificationInfoDO.setCertificationInfoRecordJson(JSONObject.toJSONString(request));
        partnerCertificationInfoDO.setCreateTime(new Date());
        partnerCertificationInfoDO.setUpdateTime(new Date());
        //set 资质审核流程id
        partnerCertificationInfoDO.setQualifyVerifyId(qualifyVerifyRespData.get("id"));
        //set 意向合同编号
        partnerCertificationInfoDO.setIntentionContractNo(qualifyVerifyRespData.get("sequenceNo"));
        hyPartnerCertificationInfoMapper.insertSelective(partnerCertificationInfoDO);

        //3.更新面试信息
        //更新面试状态
        interviewDAO.updateInterviewWorkflowStatus(request.getInterviewPlanId(), WorkflowStatusEnum.INTERVIEW_5);
        hyPartnerInterviewDO.setUpdateTime(new Date());
        //注意将 hyPartnerInterviewDO 的 status 设置为 null，否则又会修改回 4
        hyPartnerInterviewDO.setStatus(null);
        //获取当前操作人并添加面试总结/记录信息
        LoginUserInfo operator = CurrentUserHolder.getUser();
        hyPartnerInterviewDO.setRecorder(operator.getUserId());
        hyPartnerInterviewDO.setRecordTime(new Date());
        hyPartnerInterviewDO.setSummary(request.getSummary());
        hyPartnerInterviewDO.setApproveTime(new Date());
        hyPartnerInterviewDO.setAuthCode(authCode);
        hyPartnerInterviewMapper.updateByPrimaryKeySelective(hyPartnerInterviewDO);

        //4. 更新线索信息（开发主管）
        if (!StringUtils.isEmpty(request.getDevtDirectorId())) {
            HyPartnerLineInfoDO hyPartnerLineInfoDO = new HyPartnerLineInfoDO();
            hyPartnerLineInfoDO.setId(Long.parseLong(request.getLineId()));
            hyPartnerLineInfoDO.setDevelopmentDirector(request.getDevtDirectorId());
            hyPartnerLineInfoMapper.updateByPrimaryKeySelective(hyPartnerLineInfoDO);
        }
        //记录日志
        CreateQualifyVerifyDTO log = CreateQualifyVerifyDTO.builder().mobile(operator.getMobile()).operateUserId(operator.getUserId()).operateUsername(operator.getName()).operateTime(DateUtil.format(new Date(), CoolDateUtils.DATE_FORMAT_SEC))
                .summary(request.getSummary()).qualiVerifyContent(JSON.toJSONString(partnerCertificationInfoDO)).build();
        logService.recordBizLog(operator,hyPartnerInterviewDO.getPartnerLineId(), OperateTypeEnum.CREATE_QUALIFYVERIFY,log);
    }

    @Override
    public void qualificationCallback(QualificationCallbackReq request) throws ApiException {
        log.info("MDM800审批成功回调，request{}", JSONObject.toJSONString(request));
        //1. 信息是否完整
        if (null == request.getInstanceStatus() || "".equals(request.getInstanceStatus())) {
            log.error("MDM回调入参缺失，request{}", JSON.toJSONString(request));
            throw new ServiceException("MDM回调错误！");
        }
        //根据审核流程 id 获取面试会议相关 id
//        String interviewId = hyPartnerCertificationInfoMapper.getInterviewIdByQualifyVerifyId(request.getInstanceId());
//        String interviewPlanId = hyPartnerCertificationInfoMapper.getInterviewPlanIdByQualifyVerifyId(request.getInstanceId());
        HyPartnerInterviewDO hyPartnerInterviewDO = hyPartnerInterviewMapper.getInterviewInfoByQualifyVerifyId(request.getInstanceId());
        if (hyPartnerInterviewDO == null || hyPartnerInterviewDO.getInterviewPlanId() == null) {
            throw new ServiceException(ErrorCodeEnum.INTERVIEW_NOT_EXIST);
        }
        String interviewId = hyPartnerInterviewDO.getId().toString();
        String interviewPlanId = hyPartnerInterviewDO.getInterviewPlanId().toString();
        Long partnerLineId = hyPartnerInterviewDO.getPartnerLineId();
        //审核通过
        if ("FINISHED".equals(request.getInstanceStatus())) {
            //更新面试状态
            interviewDAO.updateInterviewWorkflowStatus(interviewPlanId, WorkflowStatusEnum.INTERVIEW_6);
            //2. 准备需要的信息
            String partnerName = request.getIntendedSigner();
            String verifyCity = hyPartnerInterviewMapper.getVerifyCityByInterviewId(interviewId);
            String[] split = verifyCity.split("/");
            //根据长度来取市级行政区域
            if (split.length == 2) {
                verifyCity = split[1];
            } else if (split.length == 3) {
                verifyCity = split[1];
            } else if (split.length == 4) {
                verifyCity = split[2];
            } else {
                throw new ServiceException(ErrorCodeEnum.INTENT_INFO_NOT_EXIST);
            }
            // TODO pass_reason 暂无
            //将通过时间修改为本系统处理回调的时间，不以 request 的 modifiedTime 为准（有误）
            Date passDate = new Date();
            //3. 生成通过函并修改数据库相关信息
            //TODO 问题：如果因为 pdf 生成失败或者其他原因导致异常，但是由于 MDM 只是做回调，不对回调是否成功负责，会导致流程信息缺失
            genPassLetterAndUpdateDB(partnerName, verifyCity, passDate, interviewId);
            //4. 向面试稽核表中新增一条信息
            HyInspectionDO hyInspectionDO = new HyInspectionDO();
            hyInspectionDO.setInterviewPlanId(Long.parseLong(interviewPlanId));
            hyInspectionDO.setCreateTime(DateUtil.formatDateTime(passDate));
            hyInspectionDO.setType(InspectionTyeEnum.INTERVIEW_INSPECTION.getCode());
            inspectionMapper.insertSelective(hyInspectionDO);
            //发送加盟商资质审核通过短信
            HyPartnerBaseInfoDO hyPartnerBaseInfoDO = hyPartnerBaseInfoDAO.getByPartnerLineId(partnerLineId);
            smsService.sendSms(null, CommonConstants.SMS_TEMPLATE_CODE_VERIFY, hyPartnerBaseInfoDO.getMobile());
            //记录日志
            //这里记录的日志时间为 passTime，而不是当前时间，否则 getTipsInfo 接口返回的时间是这里记录的当前时间，与 passTime 不符
            LogBasicDTO log = LogBasicDTO.builder().operateTime(DateUtil.format(passDate, CoolDateUtils.DATE_FORMAT_SEC))
                    .build();
            logService.recordBizLog(null,hyPartnerInterviewDO.getPartnerLineId(), OperateTypeEnum.QUALIFYVERIFY_PASS,log);
        //审核未通过
        } else if ("CANCELED".equals(request.getInstanceStatus())) {
            CloseFollowRequest closeFollowRequest = new CloseFollowRequest();
            closeFollowRequest.setRejectRealReason("资质审核流程拒绝");
            closeFollowRequest.setLineId(partnerLineId);
            closeFollowRequest.setType("reject");
            hyPartnerLineInfoService.closeOrPassFollow(null,closeFollowRequest);
            interviewDAO.updateInterviewWorkflowStatus(interviewPlanId, WorkflowStatusEnum.INTERVIEW_7);
            LogBasicDTO log = LogBasicDTO.builder().operateTime(DateUtil.format(new Date(), CoolDateUtils.DATE_FORMAT_SEC))
                    .build();
            logService.recordBizLog(null,hyPartnerInterviewDO.getPartnerLineId(), OperateTypeEnum.QUALIFYVERIFY_REJECT,log);
        }
    }

    public List<SkrRelshipProve> OSSFileToMDMFile(List<String> fileUrlList) throws ApiException, IOException {
        String url = mdmBaseUrl + "/api/openapi/ext/upload/file";
        ResponseEntity<MDMResultDTO> responseEntity = null;
        RpcGetMdmTokenReq rpcGetMDMTokenReq = new RpcGetMdmTokenReq();
        rpcGetMDMTokenReq.setAppKey(mdmAppKey);
        rpcGetMDMTokenReq.setAppSecret(mdmAppSec);
        ByteArrayOutputStream outputStream = null;
        List<SkrRelshipProve> relshipProves = new ArrayList<>();

        //逐个处理文件
        for (String fileUrl : fileUrlList) {
            //1. 获取 OSS 下载的文件流
            String fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
            outputStream = ossServer.downloadFileServer(fileName);
            //2. 将下载到的文件上传到 MDM 系统中
            try {
                //获取 token 设置到 header
                Map<String, String> headers = new HashMap<>();
                headers.put("Authorization", getMdmAccessToken(rpcGetMDMTokenReq));
                //将文件流编辑为 formdata 格式的数据
                MultiValueMap<String,Object> param = new LinkedMultiValueMap<>();
                ByteArrayResource resource = new ByteArrayResource(outputStream.toByteArray()) {
                    @Override
                    public String getFilename() throws IllegalStateException {
                        return fileName;
                    }
                };
                param.add("file", resource);
                //上传文件
                responseEntity = RestTemplateUtil.post(url, headers, param, MDMResultDTO.class);
                log.info("url:{}, header:{}, response statusCode:{}", url, JSONObject.toJSONString(headers), JSONObject.toJSONString(responseEntity.getStatusCode()));
                Map<String, String> data = (Map<String, String>) responseEntity.getBody().getData();
                SkrRelshipProve skrRelshipProve= BeanUtil.fillBeanWithMap(data, new SkrRelshipProve(), false);
                relshipProves.add(skrRelshipProve);
            } catch (Exception e) {
                log.info("调用MDM接口出错 url{}, fileUrl{}, e{}", url, fileUrl, e);
                throw new ApiException(e.getMessage());
            } finally {
                outputStream.close();
            }
        }
        return relshipProves;
    }

    public String createQualifyVerify(RpcCreateQualifyVerifyReq rpcRequest) throws ApiException{
        String url = mdmBaseUrl + "/api/openapi/runtime/form/startFraQualExamWithData";
        ResponseEntity<MDMResultDTO> responseEntity = null;
        try {
            RpcGetMdmTokenReq rpcGetMDMTokenReq = new RpcGetMdmTokenReq();
            rpcGetMDMTokenReq.setAppKey(mdmAppKey);
            rpcGetMDMTokenReq.setAppSecret(mdmAppSec);
            Map<String, String> headers = new HashMap<>();
            headers.put("Authorization", getMdmAccessToken(rpcGetMDMTokenReq));
            responseEntity = RestTemplateUtil.post(url, headers, rpcRequest, MDMResultDTO.class);
            log.info("url:{}, header:{}, request:{} response:{}", url, JSONObject.toJSONString(headers), JSONObject.toJSONString(rpcRequest), JSONObject.toJSONString(responseEntity));
            if (Objects.nonNull(responseEntity.getBody()) && responseEntity.getBody().isSuccess()) {
                return JSONObject.toJSONString(responseEntity.getBody().getData());
            }
        } catch (Exception e) {
            log.info("调用MDM接口出错 url{},  e{}", url, e);
            throw new ApiException(e.getMessage());
        }
        return null;

    }

    public String getMdmAccessToken(RpcGetMdmTokenReq rpcGetMDMTokenReq) throws ApiException {
        String url = mdmBaseUrl + "/api/oauth2/accessToken";
        ResponseEntity<MDMResultDTO> responseEntity = null;
        try {
            responseEntity = RestTemplateUtil.post(url,rpcGetMDMTokenReq, MDMResultDTO.class);
            log.info("url:{}, response:{}", url, JSONObject.toJSONString(responseEntity));
            if (Objects.nonNull(responseEntity.getBody()) && responseEntity.getBody().isSuccess()) {
                AccessTokenDTO accessTokenDTO = JSONObject.parseObject(JSONObject.toJSONString(responseEntity.getBody().getData()), AccessTokenDTO.class);
                if(accessTokenDTO==null || StringUtils.isBlank(accessTokenDTO.getAccessToken())){
                    throw new ServiceException("获取Mdm token失败!");
                }
                return accessTokenDTO.getAccessToken();
            }
        } catch (Exception e) {
            log.info("获取MDM Token 出错 url:\t{}, e:\t{}", url, e);
            throw new ApiException(e.getMessage());
        }
        return null;
    }


    /**
     * 生成授权码
     *
     * @param jobNumber
     * @return
     */
    public String generateAuthCode(String jobNumber) {
        String prefix = jobNumber + DateUtil.format(new Date(), "yyyyMMdd");
        return prefix + redisUtilPool.incrby(prefix, 1, 60 * 60 * 25);
    }

    /**
     * 生成通知函上传 OSS 和修改数据库相应数据
     * @return passCode
     */
    private void genPassLetterAndUpdateDB(String partnerName, String verifyCity, Date passTime, String interviewId) {
        try {
            String passCode = PassLetterUtils.genPassCode(passTime);
            //生成的 pdf 通过函内存输出流
            ByteArrayOutputStream pdfOut = PassLetterUtils.genPassLetter(partnerName, passCode, verifyCity, passTime);
            //生成的 pdf 通过函内存输入流
            ByteArrayInputStream inputStream = new ByteArrayInputStream(pdfOut.toByteArray());
            String passPdfUrl = ossServer.uploadFileServer(inputStream, "passLetter/" + passCode + ".pdf");
            //不再转换为图片
            //inputStream.reset();
            //ByteArrayOutputStream imageOut = PDFUtils.pdf2Img(inputStream, 2.0f);
            //inputStream = new ByteArrayInputStream(imageOut.toByteArray());
            //上传 OSS
            //String passImageUrl = ossServer.uploadFileServer(inputStream, "passLetter/" + passCode + ".png");
            //计算有效期截止日期
            Date expiryDate = DateUtil.offsetDay(passTime, 60);
            expiryDate = DateUtil.endOfDay(expiryDate);
            HyPartnerInterviewDO interviewDO = new HyPartnerInterviewDO();
            interviewDO.setId(Long.parseLong(interviewId));
            interviewDO.setPassCode(passCode);
            interviewDO.setPassTime(passTime);
            interviewDO.setExpiryDate(DateUtil.formatDateTime(expiryDate));
            interviewDO.setPassPdfUrl(passPdfUrl);
            //interviewDO.setPassImageUrl(passImageUrl);
            hyPartnerInterviewMapper.updateByPrimaryKeySelective(interviewDO);
            inputStream.close();
            pdfOut.close();
            //imageOut.close();
        } catch (Exception e) {
            log.error("资格面试通过函生成失败 e{}", e.getMessage());
            throw new ServiceException("通过函生成失败！");
        }
    }
}
