package com.izouma.nineth.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.anji.captcha.util.StringUtils;
import com.izouma.nineth.config.RedisKeys;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.userfund.BalanceInfoDTO;
import com.izouma.nineth.dto.userfund.RechargeOrderForm;
import com.izouma.nineth.dto.userfund.WithdrawApplyForm;
import com.izouma.nineth.enums.*;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.lock.RedisLockable;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.service.pay.PayHelper;
import com.izouma.nineth.service.pay.UniformPayConstants;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 杉德资金账户
 */
@Service
@Slf4j
@AllArgsConstructor
public class UserFundAccountForSandpayService {

    private UserFundAccountRepo userFundAccountRepo;

    private SandAccountService sandAccountService;

    private IdentityAuthRepo identityAuthRepo;

    private UserBankCardRepo userBankCardRepo;

    private PayHelper payHelper;

    private SnowflakeIdWorker snowflakeIdWorker;

    private RechargeOrderRepo rechargeOrderRepo;

    private WithdrawApplyRepo withdrawApplyRepo;

    private UserFundChangeLogService userFundChangeLogService;

    /**
     * 获取杉德云账户
     * @param userId
     * @return
     */
    public UserFundAccount getAccount(Long userId){
        UserFundAccount userFundAccount = userFundAccountRepo.findByUserIdAndAccountType(userId, AccountType.SANDPAY_USER)
                .orElse(null);
        return  userFundAccount;
    }

    /**
     * 添加杉德云账户
     * @param userId
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public UserFundAccount addAccount(Long userId, String smsCode, String smsSerialNo){

        //记录资金账户信息
        UserFundAccount account  = userFundAccountRepo.findByUserIdAndAccountType(userId, AccountType.SANDPAY_USER)
                .orElse(null);

        if(account != null){
            throw new BusinessException("系统已存在杉德账户");
        }

        IdentityAuth identityAuth = identityAuthRepo.findFirstByUserIdAndStatusAndDelFalseOrderByCreatedAtDesc(userId, AuthStatus.SUCCESS)
                .orElseThrow(new BusinessException("用户未通过实名认证"));

        JSONObject res = sandAccountService.registerOpen(identityAuth, smsCode, smsSerialNo);

        String masterAccount = res.getString("masterAccount");
        UserFundAccount.UserFundAccountBuilder builder = UserFundAccount.builder();
        builder.userId(userId).accountType(AccountType.SANDPAY_USER)
                .accountNo(identityAuth.getIdNo())
                .accountName(identityAuth.getRealName())
                .thirdUserId(masterAccount)
                .accountStatus(AccountStatus.NORMAL);
        account.setApplyTime(LocalDateTime.now());
        account = builder.build();

        userFundAccountRepo.save(account);

        return account;
    }


    /**
     * 杉德支付绑卡
     * @param userBankCard
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public UserFundAccount bindCard(UserBankCard userBankCard){
        //记录资金账户信息
        UserFundAccount account  = userFundAccountRepo.findByUserIdAndAccountType(userBankCard.getUserId(), AccountType.SANDPAY_USER)
                .orElse(null);
        if(account !=  null){
            JSONObject  res = sandAccountService.bindCard(userBankCard);
            String relatedCardNo = res.getString("relatedCardNo");
            account.setUserBankCardId(userBankCard.getId());
            account.setThirdBankId(relatedCardNo);
            userFundAccountRepo.save(account);

        }

        return account;
    }

    /**
     * 杉德支付解绑卡
     * @param userId
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public UserFundAccount unbindCard(Long userId){
        //记录资金账户信息
        UserFundAccount account  = userFundAccountRepo.findByUserIdAndAccountType(userId, AccountType.SANDPAY_USER)
                .orElse(null);
        if(account != null){
            JSONObject  res = sandAccountService.unbindCard(account.getUserId(), account.getThirdBankId());
            String sandSerialNo = res.getString("sandSerialNo");
            log.info("杉德解绑银行卡流水：" + sandSerialNo);
            account.setThirdBankId(null);
            userFundAccountRepo.save(account);
        }

        return account;
    }

    /**
     * 余额查询
     * @param userId
     * @return
     */
    public BalanceInfoDTO getBalance(Long userId){
        //记录资金账户信息
        UserFundAccount account  = userFundAccountRepo.findByUserIdAndAccountType(userId, AccountType.SANDPAY_USER)
                .orElseThrow(new BusinessException("杉德账户不存在"));
        JSONObject balanceRes = sandAccountService.balanceQuery(account.getUserId());
        JSONArray accountList = balanceRes.getJSONArray("accountList");
        if(accountList == null || accountList.size() == 0){
            throw new BusinessException("未获取到余额信息");
        }

        JSONObject item = accountList.getJSONObject(0);
        String accountName = item.getString("accountName");
        String accountType = item.getString("accountType");
        BigDecimal availableBal = item.getBigDecimal("availableBal");
        BigDecimal frozenBal = item.getBigDecimal("frozenBal");
        String accountStatus = item.getString("accountStatus");
        BalanceInfoDTO balanceInfo = new BalanceInfoDTO();
        balanceInfo.setBankAccount(accountName);
        balanceInfo.setAccountType(accountType);
        balanceInfo.setAvailableBalance(availableBal);
        balanceInfo.setFreezeBalance(frozenBal);
        balanceInfo.setAccountStatus(accountStatus);

        return balanceInfo;
    }



    /**
     * 创建充值订单
     * @param rechargeOrderDTO
     * @return
     */
    @Transactional
    public RechargeOrder createRechargeOrder(RechargeOrderForm rechargeOrderDTO){
        String payTool = "QUICKPAY";//暂时默认快捷支付

        UserFundAccount account = getAccount(rechargeOrderDTO.getUserId());
        if(account == null){
            throw new BusinessException("用户未开通杉德钱包");
        }

        BigDecimal rechargeAmount = rechargeOrderDTO.getRechargeAmount();
        if(rechargeAmount == null || rechargeAmount.compareTo(BigDecimal.ZERO) <= 0){
            throw new BusinessException("充值金额不能小于0");
        }

        if(account.getUserBankCardId() == null){
            throw new BusinessException("没有绑卡记录，请先绑卡");
        }

        UserBankCard userBankcard = userBankCardRepo.findById(account.getUserBankCardId())
                .orElseThrow(new BusinessException("绑卡记录未找到，请重新绑卡"));

        //需要绑卡
        if(StringUtils.isEmpty(account.getThirdBankId())) {
            throw new BusinessException("未找到关联卡信息，请重新绑卡");
        }

        String frontUrl = rechargeOrderDTO.getFrontUrl();
        if(StringUtils.isEmpty(frontUrl)){
            frontUrl = payHelper.resolveHome(rechargeOrderDTO.getProjectId());
        }

        String orderNo = String.valueOf(snowflakeIdWorker.nextId());
        String notifyUrl = payHelper.resolveRechargeNotifyUrl(orderNo);

        RechargeOrder.RechargeOrderBuilder builder = RechargeOrder.builder();
        builder.title("用户充值")
                .orderNo(orderNo)
                .userId(account.getUserId())
                .userFundAccountId(account.getId())
                .orderAmount(rechargeAmount)
                .userBankCardId(userBankcard.getId())
                .bankName(userBankcard.getBankName())
                .bankNo(userBankcard.getBankNo())
                .relatedCardNo(account.getThirdBankId())
                .status(RechargeStatus.CREATED)
                .notifyUrl(notifyUrl)
                .frontUrl(frontUrl)
                .payPlatform(UniformPayConstants.SANDPAY_PLATFORM);

        RechargeOrder rechargeOrder = builder.build();
        rechargeOrder.setPayTool(payTool);

        //使用后台充值方式
        JSONObject res = sandAccountService.recharge(rechargeOrder);
        //商户手续费，暂不做到帐金额计算
        BigDecimal feeAmt = res.getBigDecimal("feeAmt");
        rechargeOrder.setFeeAmt(feeAmt);
        //到账金额
        //BigDecimal amount = rechargeOrder.getOrderAmount().subtract(feeAmt);
        BigDecimal amount = rechargeOrder.getOrderAmount();
        rechargeOrder.setAmount(amount);
        //支付凭证
        String payVoucher = res.getString("payVoucher");

        rechargeOrder.setPayVoucher(payVoucher);
        //页面跳转地址
        //rechargeOrder.setJumpUrl("");

        rechargeOrderRepo.save(rechargeOrder);

        //记录充值流水
        userFundChangeLogService.createByRecharge(rechargeOrder);

        return rechargeOrder;
    }


    /**
     * 充值订单确认
     * @param orderNo
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public JSONObject rechargeConfirm(@RequestParam String orderNo, @RequestParam String smsCode){
        RechargeOrder order = rechargeOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("充值订单不存在"));
        JSONObject res = sandAccountService.orderConfirm(order.getOrderNo(), order.getOrderAmount(), smsCode);
        String orderStatus = res.getString("orderStatus");
        //是否确认成功
        if("02".equals(orderStatus)){
            throw new BusinessException("订单确认失败");
        }

        order.setStatus(RechargeStatus.PENDING);
        rechargeOrderRepo.save(order);

        return res;
    }


    /**
     * 充值通知处理
     * @param orderNo
     * @param reqData
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public JSONObject rechargeNotify(String orderNo, String reqData){
        JSONObject res = new JSONObject();
        res.put("respCode", "00000");

        RechargeOrder order = rechargeOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("充值订单未找到"));
        JSONObject bizData = sandAccountService.checkSuccessBody(reqData);
        String transType = bizData.getString("transType");
        if("DEPOSIT".equals(transType)){
            throw new BusinessException("交易类型不正确:" + transType);
        }

        String fromOrderNo = bizData.getString("orderNo");
        if(!orderNo.equals(fromOrderNo)){
            throw new BusinessException("订单编号不一致：" + fromOrderNo);
        }

        String orderStatus = bizData.getString("orderStatus");

        order.setNotifyTime(LocalDateTime.now());
        order.setTransactionId(fromOrderNo);
        if("00".equals(orderStatus)){
            order.setStatus(RechargeStatus.SUCCESS);
            order.setResult("成功");
            //更新资金流水
            userFundChangeLogService.updateSuccess(order.getUserId(), FundChangeType.RECHARGE,
                    order.getOrderNo());
        }else{
            order.setStatus(RechargeStatus.FAILED);
            order.setResult("失败：" + orderStatus);
            //更新资金流水
            userFundChangeLogService.updateFailed(order.getUserId(), FundChangeType.RECHARGE,
                    order.getOrderNo());
        }

        rechargeOrderRepo.save(order);

        return res;

    }



    /**
     * 提现申请
     * @param withdrawApplyDTO
     * @return
     */
    @Transactional
    public WithdrawApply createWithdrawApply(WithdrawApplyForm withdrawApplyDTO){
        UserFundAccount account = getAccount(withdrawApplyDTO.getUserId());
        if(account == null){
            throw new BusinessException("用户未开通杉德钱包");
        }

        if(account.getUserBankCardId() == null){
            throw new BusinessException("没有绑卡记录，请先绑卡");
        }

        UserBankCard userBankcard = userBankCardRepo.findById(account.getUserBankCardId())
                .orElseThrow(new BusinessException("绑卡记录未找到，请重新绑卡"));

        //需要绑卡
        if(StringUtils.isEmpty(account.getThirdBankId())) {
            throw new BusinessException("未找到关联卡信息，请重新绑卡");
        }

        BigDecimal withdrawAmount = withdrawApplyDTO.getWithdrawAmount();
        if(withdrawAmount == null || withdrawAmount.compareTo(BigDecimal.ZERO) <= 0){
            throw new BusinessException("提现金额不能小于0");
        }

        String frontUrl = withdrawApplyDTO.getFrontUrl();
        if(StringUtils.isEmpty(frontUrl)){
            frontUrl = payHelper.resolveHome(withdrawApplyDTO.getProjectId());
        }

        String orderNo = String.valueOf(snowflakeIdWorker.nextId());
        String notifyUrl = payHelper.resolveWithdrawNotifyUrl(orderNo);

        WithdrawApply.WithdrawApplyBuilder builder = WithdrawApply.builder();
        builder.title("用户提现")
                .orderNo(orderNo)
                .userId(account.getUserId())
                .userFundAccountId(account.getId())
                .orderAmount(withdrawAmount)
                .userBankCardId(userBankcard.getId())
                .bankName(userBankcard.getBankName())
                .bankNo(userBankcard.getBankNo())
                .relatedCardNo(userBankcard.getThirdBankId())
                .status(WithdrawStatus.CREATED)
                .notifyUrl(notifyUrl)
                .frontUrl(frontUrl)
                .payPlatform(UniformPayConstants.SANDPAY_PLATFORM);

        WithdrawApply withdrawApply = builder.build();

        //提现操作
        JSONObject res = sandAccountService.withdrawApply(withdrawApply);
        BigDecimal feeAmt = res.getBigDecimal("feeAmt");
        withdrawApply.setFeeAmt(feeAmt);
        //到账金额
        BigDecimal amount = withdrawApply.getOrderAmount().subtract(feeAmt);
        withdrawApply.setAmount(amount);

        //01：发送短信（有效时间5分钟）
        //02：支付密码
        //03：免密免短信（无需确认支付）
        //04：支付密码+短信
        String authWay = res.getString("authWay");
        if("01".equals(authWay)){
            withdrawApply.setAuthWay(AuthWay.SMS);
        }else if("02".equals(authWay)){
            withdrawApply.setAuthWay(AuthWay.PASSWORD);
        }else if("03".equals(authWay)){
            withdrawApply.setAuthWay(AuthWay.NOAUTH);
        }else if("04".equals(authWay)){
            withdrawApply.setAuthWay(AuthWay.PASSWORD_SMS);
        }else {
            throw new BusinessException("未知认证方式:" + authWay);
        }

        //02或04鉴权方式返回
        String passwordURL = res.getString("passwordURL");

        //页面跳转地址
        withdrawApply.setJumpUrl(passwordURL);

        withdrawApplyRepo.save(withdrawApply);

        //记录提现流水
        userFundChangeLogService.createByWithdraw(withdrawApply);

        return withdrawApply;

    }


    /**
     * 提现操作确认
     * @param orderNo
     * @return
     */
    @RedisLockable(prefix = RedisKeys.WALLET_PAY_CONFIRM_LOCK, key = "#orderNo")
    @Transactional
    public JSONObject withdrawConfirm(@RequestParam String orderNo, @RequestParam String smsCode){
        WithdrawApply order = withdrawApplyRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("充值订单不存在"));
        JSONObject res = sandAccountService.orderConfirm(order.getOrderNo(), order.getOrderAmount(), smsCode);
        String orderStatus = res.getString("orderStatus");
        //是否确认成功
        if("02".equals(orderStatus)){
            throw new BusinessException("订单确认失败");
        }

        order.setStatus(WithdrawStatus.PENDING);
        withdrawApplyRepo.save(order);

        return res;
    }

    /**
     * 提现通知处理
     * @param orderNo
     * @param reqData
     * @return
     */
    @Transactional(Transactional.TxType.REQUIRES_NEW)
    public JSONObject withdrawNotify(String orderNo, String reqData){
        JSONObject res = new JSONObject();
        res.put("respCode", "00000");

        WithdrawApply withdrawApply = withdrawApplyRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("提现申请未找到"));
        JSONObject bizData = sandAccountService.checkSuccessBody(reqData);
        String transType = bizData.getString("transType");
        if("WITHDRAW".equals(transType)){
            throw new BusinessException("交易类型不正确:" + transType);
        }

        String fromOrderNo = bizData.getString("orderNo");
        if(!orderNo.equals(fromOrderNo)){
            throw new BusinessException("订单编号不一致：" + fromOrderNo);
        }

        String orderStatus = bizData.getString("orderStatus");

        withdrawApply.setNotifyTime(LocalDateTime.now());
        withdrawApply.setTransactionId(fromOrderNo);
        if("00".equals(orderStatus)){
            withdrawApply.setStatus(WithdrawStatus.SUCCESS);
            withdrawApply.setResult("成功");
            withdrawApplyRepo.saveAndFlush(withdrawApply);
            //更新提现流水
            userFundChangeLogService.updateSuccess(withdrawApply.getUserId(), FundChangeType.WITHDRAW, withdrawApply.getOrderNo());
        }else{
            withdrawApply.setStatus(WithdrawStatus.FAILED);
            withdrawApply.setResult("失败：" + orderStatus);
            //更新提现流水
            userFundChangeLogService.updateFailed(withdrawApply.getUserId(), FundChangeType.WITHDRAW, withdrawApply.getOrderNo());
        }

        return res;

    }



}
