package cn.qg.qaplatform.service.xyqb.impl;

import cn.qg.qaplatform.config.WebSocketServer;
import cn.qg.qaplatform.entity.vo.XyqbApplyDataVo;
import cn.qg.qaplatform.entity.vo.XjfqGenProcessVo;
import cn.qg.qaplatform.entity.xyqb.AssignFundingRecord;
import cn.qg.qaplatform.entity.xyqbuser.User;
import cn.qg.qaplatform.process.Xyqb;
import cn.qg.qaplatform.service.xyqb.AssignFundingRecordService;
import cn.qg.qaplatform.service.xyqb.LoanApplicationHistoryService;
import cn.qg.qaplatform.service.xyqbUser.UserService;
import cn.qg.qaplatform.service.xyqb.XyqbService;
import cn.qg.qaplatform.utils.HttpClientUtils;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.jayway.jsonpath.JsonPath;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Service
@Slf4j
public class XyqbServiceImpl implements XyqbService {

    @Resource
    RedisTemplate redisTemplate;

    @Autowired
    UserService userService;

    @Autowired
    AssignFundingRecordService assignFundingRecordService;

    @Autowired
    LoanApplicationHistoryService loanApplicationHistoryService;

    /**
     * 分配资产计划
     *
     * @param namespace
     * @param fundId
     * @return
     */
    @Override
    public boolean assignFundCorp(String namespace, Integer fundId, String symbol) {
        Xyqb.symbol = symbol;
        // 登录资金方OP
        String fundOpToken = Xyqb.loginFundOP(namespace);
        boolean isAleadyAssign = false;
        // 检查是否已分配资产计划
        try {
            isAleadyAssign = Xyqb.assignFundOrNot(namespace, fundId, fundOpToken);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            e.printStackTrace();
            return false;
        }
        boolean assignResult = false;
        boolean unblockResult = false;
        // 如果未分配
        if (!isAleadyAssign) {
            // 分配资产计划
            assignResult = Xyqb.setFundPlan(namespace, fundId, fundOpToken);
            // 打开推送放款
            unblockResult = Xyqb.unblockLoan(namespace, fundId, fundOpToken);
            if (assignResult && unblockResult) {
                return true;
            }
            return false;
        } else {
            return true;
        }
    }

    /**
     * 授信
     *
     * @param xyqbApplyDataVo 造数据的相关信息
     * @return
     * @throws Exception
     */
    @Override
    public boolean quotaAuth(XyqbApplyDataVo xyqbApplyDataVo, boolean auditResult) throws Exception {
        String namespace = xyqbApplyDataVo.getNamespace();
        String phoneNo = xyqbApplyDataVo.getPhoneNo();
        Integer channelId = xyqbApplyDataVo.getChannel();
        Integer fundId = xyqbApplyDataVo.getFundId();
        Integer amount = xyqbApplyDataVo.getAmount();
        Integer term = xyqbApplyDataVo.getTerm();
        String name = xyqbApplyDataVo.getName();
        String idCardNo = xyqbApplyDataVo.getIdCardNo();

        // 登录
        String token = Xyqb.login(namespace, channelId, phoneNo);
        // 查询用户中心id
        Map map = Xyqb.checkUserId(namespace, phoneNo);
        String uuid = map.get("uuid").toString();
        String userId = map.get("id").toString();
        // 同步session到xyqb
        Xyqb.syncSessionToXyqb(namespace, token);
        // 授信session同步
        Xyqb.creditSessionSync(namespace, "1", token);
        // 实名认证
        Xyqb.realNameAuth(namespace, token, name, idCardNo);
        // 基本信息认证
        Xyqb.authBasicInfo(namespace, token);
        // 进入上传身份证页
        String ocrToken = Xyqb.enterAuthOcr(namespace, token);
        // 上传身份证正面
        Xyqb.uploadFrontOfIdCard(namespace, ocrToken);
        // 上传身份证反面
        Xyqb.uploadBackOfIdCard(namespace, ocrToken);
        // 提交拍照认证
        Xyqb.submitPhotoAuth(namespace, ocrToken);
        // 授信
        Xyqb.credit(namespace, phoneNo, userId, uuid);
        // 提交审核
        Xyqb.submitAudit(namespace, token);
        // 查询用户审核记录
        Map authResult = Xyqb.queryUserAuditRecord(namespace, userId);
        String orderNo = authResult.get("order_no").toString().trim();
        String productId = authResult.get("product_id").toString();
        // 风控授信回调
        boolean creditResult = Xyqb.creditAuthNotify(namespace, token, uuid, channelId, fundId, orderNo, 0, auditResult, amount, term);
        // 风控授信回调成功，redis值为0
        if (creditResult) {
            setUserStatusRedisValue(namespace, phoneNo, 0);
        } else {
            setUserStatusRedisValue(namespace, phoneNo, 1);
        }
        return creditResult;
    }

    /**
     * 提现
     *
     * @param xyqbApplyDataVo 造数据相关信息
     * @param auditResult     二次风控成功or失败
     * @return
     * @throws Exception
     */
    @Override
    public boolean withDraw(XyqbApplyDataVo xyqbApplyDataVo, boolean auditResult) throws Exception {
        String namespace = xyqbApplyDataVo.getNamespace();
        String phoneNo = xyqbApplyDataVo.getPhoneNo();
        Integer channelId = xyqbApplyDataVo.getChannel();
        Integer fundId = xyqbApplyDataVo.getFundId();
        Integer amount = xyqbApplyDataVo.getAmount();
        Integer term = xyqbApplyDataVo.getTerm();
        String bankCardNo = xyqbApplyDataVo.getBankCardNo();
        String bankCode = xyqbApplyDataVo.getBankCode();

        // 登录
        String token = Xyqb.login(namespace, channelId, phoneNo);
        // 查询用户中心id
        Map map = Xyqb.checkUserId(namespace, phoneNo);
        String uuid = map.get("uuid").toString();
        String userId = map.get("id").toString();
        // 同步session到xyqb
        Xyqb.syncSessionToXyqb(namespace, token);
        // 查询用户审核记录
        Map authResult = Xyqb.queryUserAuditRecord(namespace, userId);
        String orderNo = authResult.get("order_no").toString().trim();
        String productId = authResult.get("product_id").toString();
        Long accountId;
        // 查询accountId
        try {
            accountId = Xyqb.getUserAccountId(namespace, userId, productId);
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
            log.info("资方分配失败，quota_account表中无法查询到accountId!");
            return false;
        }
        // 绑卡
        String payToken = Xyqb.bindCard(namespace, token, accountId);
        // 绑卡发送短信
        Xyqb.cardAuthSms(namespace, payToken, phoneNo, bankCardNo, bankCode);
        // 绑卡确认
        Xyqb.cardAuthSmsConfirm(namespace, payToken, bankCardNo);
        // 获取绑卡列表
        Map firstCard = Xyqb.getBindCardList(namespace, token, accountId);
        Integer cardId = (Integer) firstCard.get("id");
        Integer cardBindInfoId = (Integer) firstCard.get("cardBindInfoId");
        // 绑卡后提现
        Integer loanId = Xyqb.createLoan(namespace, token, amount, term, accountId, productId, cardId, cardBindInfoId);
        // 二次风控
        boolean result = Xyqb.externalQuotaOrderAuditNotify(namespace, channelId, token, uuid, loanId, 0, auditResult);
        // 修改合同状态
        Xyqb.modifyContactStatus(namespace, userId);
        if (result) {
            setUserStatusRedisValue(namespace, phoneNo, 2);
        }
        return result;
    }

    /**
     * 放款
     *
     * @param xyqbApplyDataVo
     * @return
     */
    @Override
    public boolean makeLoan(XyqbApplyDataVo xyqbApplyDataVo, boolean payStatus) throws Exception {
        String namespace = xyqbApplyDataVo.getNamespace();
        String phoneNo = xyqbApplyDataVo.getPhoneNo();
        Integer channelId = xyqbApplyDataVo.getChannel();
        Integer fundId = xyqbApplyDataVo.getFundId();
        Integer amount = xyqbApplyDataVo.getAmount();
        Integer term = xyqbApplyDataVo.getTerm();

        // 登录
        String token = Xyqb.login(namespace, channelId, phoneNo);
        // 查询用户中心id
        Map map = Xyqb.checkUserId(namespace, phoneNo);
        String uuid = map.get("uuid").toString();
        String userId = map.get("id").toString();

        // 登录OP，获取token
        String opToken = Xyqb.loginOP(namespace);
        // 查询待打款数量
        Map<String, Integer> peopleMap = Xyqb.checkWaitingFundingCorpOperatePeople(namespace, fundId);
        Integer money = peopleMap.get("money");
        Integer people = peopleMap.get("people");
        // 修改待放款用户创建时间
        Integer loanId = Xyqb.getLoanId(namespace, userId);
        Xyqb.modifyWaitingFundingCreatedAt(namespace, loanId);
        Thread.sleep(5000);
        // 直接打款
        Xyqb.fundsPlanLoanApply(namespace, opToken, fundId, money, people);
        Thread.sleep(30000);
        // 放款结果通知
        boolean makeLoanResult = Xyqb.payNotify(namespace, loanId, payStatus);
        if (makeLoanResult) {
            setUserStatusRedisValue(namespace, phoneNo, 3);
        } else {
            setUserStatusRedisValue(namespace, phoneNo, 4);
        }
        return makeLoanResult;
    }

    /**
     * 设置redis值，查询状态
     *
     * @param namespace
     * @param phoneNo
     * @param status    0-授信成功，1-授信失败，2-提现成功，3-放款成功，4-放款失败
     */
    public void setUserStatusRedisValue(String namespace, String phoneNo, Integer status) {
        String redisKey = namespace + "_" + phoneNo;
        redisTemplate.opsForValue().set(redisKey, status);
        redisTemplate.expire(redisKey, 1800, TimeUnit.SECONDS);
    }

    /**
     * 查询资方分配情况
     * @param namespace
     * @param phoneNo
     * @param fundId
     * @param symbol
     * @throws InterruptedException
     */
    public void queryFundingAssignProgress(String namespace, String phoneNo, Integer fundId, String symbol) throws InterruptedException {
        String userId = String.valueOf(userService.getUserInfoByPhoneNo(namespace, phoneNo).getId());
        while (true) {
            Integer searchResult = Math.toIntExact(assignFundingRecordService.getFundRecordByUserId(namespace, userId).getFundingCorpId());
            log.info(searchResult.toString());
            if (fundId.equals(searchResult)) {
                log.info("用户：" + userId + "资方分配成功!");
                if (!StringUtils.isEmpty(symbol)) {
                    WebSocketServer.sendInfo("查询到的资方为：" + searchResult +",资方分配成功.", symbol);
                }
                break;
            } else {
                if (!StringUtils.isEmpty(symbol)) {
                    WebSocketServer.sendInfo("查询到的资方为：" + searchResult +",资方还未分配成功，10秒后重试", symbol);
                }
                Thread.sleep(10000);
            }
        }
    }

    /**
     * 查询放款进度
     * @param namespace
     * @param phoneNo
     * @param symbol
     * @throws InterruptedException
     */
    public void queryLoanProgress(String namespace, String phoneNo, String symbol) throws InterruptedException {
        String userId = String.valueOf(userService.getUserInfoByPhoneNo(namespace, phoneNo).getId());
        while (true) {
            Integer progress = loanApplicationHistoryService.getLastestLoanByUserId(namespace, userId).getProgress();
            if (progress == 15) {
                log.info("用户：" + userId + "放款成功！");
                if (!StringUtils.isEmpty(symbol)) {
                    WebSocketServer.sendInfo("放款成功.", symbol);
                }
                break;
            } else {
                if (!StringUtils.isEmpty(symbol)) {
                    WebSocketServer.sendInfo("查询到的状态为：" + progress + "，资方放款中，30秒后重新查询状态！", symbol);
                }
                Thread.sleep(30000);
            }
        }
    }

    @Override
    public boolean genLoanUser(XyqbApplyDataVo xyqbApplyDataVo) throws Exception {
        Integer status = xyqbApplyDataVo.getStatus();
        String namespace = xyqbApplyDataVo.getNamespace();
        Integer fundId = xyqbApplyDataVo.getFundId();
        String symbol = xyqbApplyDataVo.getSymbol();
        String phoneNo = xyqbApplyDataVo.getPhoneNo();

        WebSocketServer.sendInfo("本次请求的参数为：" + xyqbApplyDataVo.toString(), symbol);
        log.info("本次请求的参数为：" + xyqbApplyDataVo.toString());

        boolean assignFundResult = assignFundCorp(namespace, fundId, symbol);
        log.info("分配资金方的结果为：" + assignFundResult);
        if (!assignFundResult) {
            log.info("资金方分配失败，请检查环境！");
            return false;
        }

        /**
         * 0 - 审核通过
         */
        if (status == 0) {
            return quotaAuth(xyqbApplyDataVo, true);
        }

        /**
         * 1 - 审核拒绝
         */
        if (status == 1) {
            return quotaAuth(xyqbApplyDataVo, false);
        }

        /**
         * 2 - 放款中
         */
        if (status == 2) {
            boolean result = quotaAuth(xyqbApplyDataVo, true);
            if (!result) {
                return false;
            }
            // 查询资方审核进度，如未分配成功，则阻塞下一步
            queryFundingAssignProgress(namespace, phoneNo, fundId, symbol);
            return withDraw(xyqbApplyDataVo, true);
        }

        /**
         * 3 - 放款成功
         */
        if (status == 3) {
            boolean quotaAuthResult = quotaAuth(xyqbApplyDataVo, true);
            if (!quotaAuthResult) {
                return false;
            }
            // 查询资方审核进度，如未分配成功，则阻塞下一步
            queryFundingAssignProgress(namespace, phoneNo, fundId, symbol);
            boolean withDrawResult = withDraw(xyqbApplyDataVo, true);
            if (!withDrawResult) {
                WebSocketServer.sendInfo("提现失败!", symbol);
                return false;
            }
            // 如果资方选择云信，则不需要手动调放款
            if (fundId == 1040) {
                WebSocketServer.sendInfo("提现成功，等待云信放款...", symbol);
                queryLoanProgress(namespace, phoneNo, symbol);
                setUserStatusRedisValue(namespace, phoneNo, 3); // 因为没有调放款广达放款接口，因此需要手动设置redis值
                return true;
            }
            // 资方为平顶山，不需要手动放款
            if (fundId == 1030) {
                WebSocketServer.sendInfo("已完成二次风控，等待平银放款...", symbol);
                queryLoanProgress(namespace, phoneNo, symbol);
                setUserStatusRedisValue(namespace, phoneNo, 3);
            }
            WebSocketServer.sendInfo("提现成功，10秒后放款!", symbol);
            Thread.sleep(10000);
            // 广达小贷-手动放款
            boolean makeLoanResult = makeLoan(xyqbApplyDataVo, true);
            if (makeLoanResult) {
                WebSocketServer.sendInfo("放款成功!", symbol);
            } else {
                WebSocketServer.sendInfo("放款失败!", symbol);
            }
            return makeLoanResult;
        }

        /**
         * 4 - 放款失败
         */
        if (status == 4) {
            boolean quotaAuthResult = quotaAuth(xyqbApplyDataVo, true);
            if (!quotaAuthResult) {
                return false;
            }
            // 查询资方审核进度，如未分配成功，则阻塞下一步
            queryFundingAssignProgress(namespace, phoneNo, fundId, symbol);
            // 云南信托：二次风控失败，即代表放款失败
            if (fundId == 1040 || fundId == 1030) {
                withDraw(xyqbApplyDataVo, false);
                WebSocketServer.sendInfo("放款失败！", symbol);
                setUserStatusRedisValue(namespace, phoneNo, 4);
                return true;
            }
            boolean withDrawResult = withDraw(xyqbApplyDataVo, true);
            if (!withDrawResult) {
                WebSocketServer.sendInfo("提现失败!", symbol);
                return false;
            }
            WebSocketServer.sendInfo("提现成功，10秒后放款!", symbol);
            Thread.sleep(10000);
            boolean makeLoanResult = makeLoan(xyqbApplyDataVo, false);
            if (makeLoanResult) {
                WebSocketServer.sendInfo("放款失败!", symbol);
            }
            return makeLoanResult;

        }
        return false;
    }

    @Override
    public XjfqGenProcessVo queryUserStatus(String namespace, String phoneNo) {
        String redisResult = redisTemplate.opsForValue().get(namespace + "_" + phoneNo).toString();
        log.info("获取redis key结果为：" + redisResult);
        XjfqGenProcessVo xjfqGenProcessVo = new XjfqGenProcessVo();
        User user = userService.getUserInfoByPhoneNo(namespace, phoneNo);
        AssignFundingRecord assignFundingRecord = assignFundingRecordService.getFundRecordByUserId(namespace, String.valueOf(user.getId()));
        if (assignFundingRecord != null) {
            xjfqGenProcessVo.setFundId(Math.toIntExact(assignFundingRecord.getFundingCorpId()));
        }
        // 授信审核中
        if (redisResult.equals("0")) {
            xjfqGenProcessVo.setStatus(0);
            xjfqGenProcessVo.setDesc("授信审核中");
        }
        // 授信审核失败
        if (redisResult.equals("1")) {
            xjfqGenProcessVo.setStatus(1);
            xjfqGenProcessVo.setDesc("授信审核失败");
        }
        // 提现成功
        if (redisResult.equals("2")) {
            // 设置资方id
            xjfqGenProcessVo.setStatus(2);
            xjfqGenProcessVo.setDesc("提现成功");
        }
        // 提现失败
        if (redisResult.equals("10")) {
            xjfqGenProcessVo.equals("10");
            xjfqGenProcessVo.setDesc("资方分配失败");
        }
        // 放款成功
        if (redisResult.equals("3")) {
            xjfqGenProcessVo.setStatus(3);
            xjfqGenProcessVo.setDesc("放款成功");
        }
        // 放款失败
        if (redisResult.equals("4")) {
            xjfqGenProcessVo.setStatus(4);
            xjfqGenProcessVo.setDesc("放款失败");
        }
        return xjfqGenProcessVo;
    }

    @Override
    public String assignFund(String namespace, Integer fundCorpId) throws Exception {
        String loginOpUrl = "http://opapi-" + namespace + ".liangkebang.net" + "/user/login";
        String fundAssignUrl = "https://clotho-" + namespace + ".liangkebang.net/" + "/funds/plan/funding/plan/save";
        String unLockSwitchUrl = "http://opapi-" + namespace + ".liangkebang.net" + "/clotho/funds/plan/funding/switch/" + fundCorpId.toString() + "/loan/auto_lock";
        String fundPolicySetUrl = "http://opapi-" + namespace + ".liangkebang.net" + "/clotho/funds/plan/funding/policy/save";
        // 登录OP
        log.info("开始分配资产计划，环境：{}, 资方id：{}", namespace, fundCorpId);
        Map<String, Object> loginOpParams = new HashMap<>();
        loginOpParams.put("auth", "dXNlcj1xYSZwYXNzd29yZD1xYXRlc3Q=");
        JSONObject loginOpResult = HttpClientUtils.doGetReturnJson(loginOpUrl, loginOpParams, null);
        log.info("登录OP结果：{}", loginOpResult);
        String opToken = JsonPath.read(loginOpResult, "$.data.token");
        // 分配资产计划
        Map<String, Object> headers = new HashMap<>();
        Map<String, Object> assignFundParams = new HashMap<>();
        headers.put("Content-Type", "application/x-www-form-urlencoded");
        headers.put("x-auth-token", opToken);
        assignFundParams.put("fundCorpId", fundCorpId);
        assignFundParams.put("planDate", "");
        assignFundParams.put("maxAssignAmount", 1000000);
        assignFundParams.put("maxLoanAmount", 1000000);
        assignFundParams.put("maxLoanPeople", 100);
        assignFundParams.put("maxAssignPeople", 100);
        assignFundParams.put("priorityLevel", 1);
        JSONObject assignFundResult = HttpClientUtils.doPost(fundAssignUrl, assignFundParams, headers);
        log.info("分配资产计划结果：{}", assignFundResult);
        Map<String, Object> unLockSwitchParams = new HashMap<>();
        unLockSwitchParams.put("to", true);
        JSONObject unLockSwitchResult = HttpClientUtils.doPost(unLockSwitchUrl, unLockSwitchParams, headers);
        log.info("推送放款的结果：{}", unLockSwitchResult);
        Map<String, Object> fundPolicySetParams = new HashMap<>();
        fundPolicySetParams.put("fundingCorpId", fundCorpId);
        fundPolicySetParams.put("minQgScore", -1);
        fundPolicySetParams.put("maxQgScore", -1);
        fundPolicySetParams.put("productType", 1);
        fundPolicySetParams.put("fetchConfig", "0:01");
        fundPolicySetParams.put("autoLoanFetchConfig", "0:01");
        fundPolicySetParams.put("loanTimes", "0:00-23:59");
//        fundPolicySetParams.put("onlyChannel", 159439);
        fundPolicySetParams.put("isAutoAssign", true);
        fundPolicySetParams.put("isAutoLoan", true);
        fundPolicySetParams.put("isAutoFetch", true);
        fundPolicySetParams.put("autoLoanFetch", true);
        JSONObject fundPolicySetResult = HttpClientUtils.doPost(fundPolicySetUrl, fundPolicySetParams, headers);
        log.info("设置资方不受限制的结果：{}", fundPolicySetResult);
        return "资方分配成功";
    }


}
