package com.izouma.nineth.web.mobile.v1;

import cn.hutool.core.date.DateUtil;
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.config.SysConstants;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.dto.pay.TradeType;
import com.izouma.nineth.dto.pay.UniformPayRequest;
import com.izouma.nineth.dto.pay.UniformPayResult;
import com.izouma.nineth.dto.userfund.BalanceInfoDTO;
import com.izouma.nineth.dto.userfund.RechargeOrderForm;
import com.izouma.nineth.dto.userfund.TradeLogPageDTO;
import com.izouma.nineth.dto.userfund.WithdrawApplyForm;
import com.izouma.nineth.dto.wallet.PayResultDTO;
import com.izouma.nineth.dto.wallet.RechargeOrderDTO;
import com.izouma.nineth.dto.wallet.WithdrawApplyDTO;
import com.izouma.nineth.enums.*;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.lock.RedisLockException;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.service.LianlianPayService;
import com.izouma.nineth.service.SysConfigService;
import com.izouma.nineth.service.UserFundAccountForLianlianpayService;
import com.izouma.nineth.service.UserFundChangeLogService;
import com.izouma.nineth.service.pay.UniformPayConstants;
import com.izouma.nineth.service.pay.UniformPayUtil;
import com.izouma.nineth.web.BaseController;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.izouma.nineth.dto.pay.TradeType.WALLET_ACCOUNT;

/**
 * 连连付钱包
 */
@RestController
@RequestMapping("/walletLianlianpay/v1")
@AllArgsConstructor
@Slf4j
public class WalletLianlianpayControllerV1 extends BaseController {

    private UserFundAccountForLianlianpayService userFundAccountForLianlianpayService;
    private LianlianPayService lianlianPayService;
    private IdentityAuthRepo identityAuthRepo;
    private PaymentInfoRepo paymentInfoRepo;
    private UniformPayUtil uniformPayUtil;
    private RedisTemplate redisTemplate;

    private UserFundChangeLogService userFundChangeLogService;

    private SysConfigService sysConfigService;


    private RechargeOrderRepo rechargeOrderRepo;

    private WithdrawApplyRepo withdrawApplyRepo;

    private WalletRenewOrderRepo walletRenewOrderRepo;


    /**
     * 获取连连钱包账户
     * @return
     */
    @PostMapping("getAccount")
    public JSONObject getAccount(String refresh){
        JSONObject res = new JSONObject();
        User user = currentUser();
        //强制更新会员编号
        UserFundAccount userFundAccount = userFundAccountForLianlianpayService.getAccount(user.getId());
        BalanceInfoDTO balanceInfoDTO = new BalanceInfoDTO();
        boolean needRenew = false;
        BigDecimal manageCost;
        if(userFundAccount != null){
            needRenew = userFundAccount.isNeedRenew();
            manageCost = userFundAccount.getManageCost();
            //是否刷新
            if("1".equals(refresh)){
                userFundAccountForLianlianpayService.clearBalanceCache(user.getId());
            }

            //检查用户状态
            try {
                if (userFundAccount.getAccountStatus() != AccountStatus.NORMAL) {
                    JSONObject checkInfo = userFundAccountForLianlianpayService.checkUserInfo(user.getId());
                    res.put("user_status", checkInfo.getString("user_status"));
                }

                balanceInfoDTO = userFundAccountForLianlianpayService.getBalance(user.getId());
            }catch(Exception e){
                res.put("checkError", e.getMessage());
                log.warn("用户账户未激活：" + e.getMessage());
            }

        }else{
            userFundAccount = new UserFundAccount();
            userFundAccount.setAccountStatus(AccountStatus.ACTIVATE_PENDING);
            userFundAccount.setNeedRenew(true);
            //开户费用
            manageCost = sysConfigService.getBigDecimal(SysConstants.WALLET_REGISTER_COST);
            if(manageCost.compareTo(BigDecimal.ZERO) > 0){
                needRenew = true;
            }
        }

        res.put("account", userFundAccount);
        res.put("balance", balanceInfoDTO);
        res.put("memberId", user.getMemberId());
        res.put("manageCost", manageCost);
        res.put("needRenew", needRenew);
        return res;
    }


    /**
     * 连连账户开通短信
     * @return
     */
    @PostMapping("sendRegisterSms")
    public Map<String,Object> sendRegisterSms(){
        User user = currentUser();
        String cacheKey = RedisKeys.WALLET_SMS_CACHE + user.getPhone();
        Long expire = redisTemplate.getExpire(cacheKey, TimeUnit.SECONDS);
        if(expire != null && expire > 0){
            throw new BusinessException("请求过于频繁，请" + expire + "秒之后再试");
        }

        LocalDateTime freshTime = LocalDateTime.now();

        JSONObject res = lianlianPayService.registerVerifyCodeApply(user.getId(), user.getPhone());
        Map<String,Object> result = new LinkedHashMap<>();
        result.put("phone", res.getString("reg_phone"));
        result.put("memberId", res.getString("user_id"));
        result.put("freshTime", freshTime);

        redisTemplate.opsForValue().set(cacheKey, freshTime, 60, TimeUnit.SECONDS);

        return result;
    }

    /**
     * 配合加密控件使用
     * 获取随机因子(30分钟内有效)
     * @return
     */
    @PostMapping("getRandom")
    public JSONObject getRandom(String algorithm){
        User user = currentUser();
        JSONObject result = userFundAccountForLianlianpayService.getRandomWithCache(user.getId(), algorithm);

        return result;
    }


    /**
     * 申请开通连连付钱包账户
     * @return
     */
    @PostMapping("registerApply")
    public UserFundAccount registerApply(@RequestParam String smsCode, String idStartDate, String idEndDate){
        User user = currentUser();
        //记录资金账户信息
        UserFundAccount account  = userFundAccountForLianlianpayService.getAccount(user.getId());
        if(account != null){
            if(account.getAccountStatus() == AccountStatus.NORMAL){
                throw new BusinessException("钱包账户已开通，不能重复申请");
            }

            if(account.getAccountStatus() == AccountStatus.CHECK_PENDING){
                throw new BusinessException("钱包账户正在审核，不能重复申请");
            }

        }

        //完善实名信息
        IdentityAuth identityAuth = identityAuthRepo.findFirstByUserIdAndStatusAndDelFalseOrderByCreatedAtDesc(user.getId(), AuthStatus.SUCCESS)
                .orElseThrow(new BusinessException("用户未通过实名认证,无法申请开户"));
        if(identityAuth.getIdEndDate() == null){
            if(StringUtils.isEmpty(idEndDate)){
                throw new BusinessException("缺失证件有效日期,请提供有效日期");
            }
        }

        //更新有效日期
        boolean hasUpdate = false;
        if(StringUtils.isNotEmpty(idStartDate)){
            identityAuth.setIdStartDate(DateUtil.parseDate(idStartDate).toSqlDate().toLocalDate());
            hasUpdate = true;
        }

        if(StringUtils.isNotEmpty(idEndDate)){
            identityAuth.setIdEndDate(DateUtil.parseDate(idEndDate).toSqlDate().toLocalDate());
            hasUpdate = true;
        }

        if(hasUpdate){
            //保存
            identityAuthRepo.save(identityAuth);
        }

        //验证手机号
        if(StringUtils.isNotEmpty(smsCode)){
            JSONObject verifyRes = lianlianPayService.registerVerifyCodeVerify(user.getId(), smsCode);
            String regPhone = verifyRes.getString("reg_phone");
            log.info("校验注册钱包手机号通过：" + regPhone);
        }

        account = userFundAccountForLianlianpayService.registerApply(user.getId(), null);

        return account;
    }


    /**
     * 申请开通连连付钱包账户
     * @return
     */
    @PostMapping("registerWithPage")
    public UserFundAccount registerWithPage(@RequestParam String returnUrl){
        User user = currentUser();
        if(StringUtils.isEmpty(returnUrl)){
            throw new BusinessException("请求的返回地址参数不能为空");
        }

        UserFundAccount account = userFundAccountForLianlianpayService.registerApply(user.getId(), returnUrl);
        return account;
    }



    /**
     * 用户资金账户
     * @param verifyCode
     * @param password
     * @return
     */
    @PostMapping("/registerVerify")
    public UserFundAccount registerVerify(@RequestParam String password, @RequestParam String randomKey,
                                          @RequestParam String verifyCode){
        User user = currentUser();
        try {
            UserFundAccount userFundAccount = userFundAccountForLianlianpayService.registerVerify(user.getId(), password,
                    randomKey, verifyCode);
            return userFundAccount;
        }catch (RedisLockException e){
            throw new BusinessException("验证点击过于频繁，请稍后再试");
        }

    }

    /**
     * 修改密码
     * @param password
     * @param randomKey
     * @param passwordNew
     * @return
     */
    @PostMapping("changePassword")
    public JSONObject changePassword(@RequestParam String password, @RequestParam String randomKey, @RequestParam String passwordNew){
        User user = currentUser();
        JSONObject res = userFundAccountForLianlianpayService.changePassword(user.getId(), password, randomKey, passwordNew);
        String memberId = res.getString("user_id");
        JSONObject result = new JSONObject();
        result.put("memberId", memberId);
        return result;
    }


    /**
     * 找回密码申请
     * @return
     */
    @PostMapping("findPasswordApply")
    public JSONObject findPasswordApply(){
        User user = currentUser();
        JSONObject res = userFundAccountForLianlianpayService.findPasswordApply(user.getId());
        String token = res.getString("token");
        JSONObject result = new JSONObject();
        result.put("token", token);
        return result;
    }



    /**
     * 找回密码验证
     * @param token
     * @param verifyCode
     * @param password
     * @param randomKey
     * @return
     */
    @PostMapping("findPasswordVerify")
    public JSONObject findPasswordVerify(@RequestParam String token,
                                         @RequestParam String verifyCode,
                                         @RequestParam String password,
                                         @RequestParam String randomKey){
        User user = currentUser();
        try {
            JSONObject res = userFundAccountForLianlianpayService.findPasswordVerify(user.getId(), token, verifyCode, password, randomKey);
            String memberId = res.getString("user_id");
            JSONObject result = new JSONObject();
            result.put("memberId", memberId);
            return result;
        }catch(RedisLockException e){
            throw new BusinessException("验证点击过于频繁，请稍后再试");
        }

    }


    /**
     * 创建充值订单
     * @return
     */
    @PostMapping("createRecharge")
    public RechargeOrderDTO createRecharge(@RequestBody RechargeOrderForm rechargeOrderForm){
        User user = currentUser();
        rechargeOrderForm.setUserId(user.getId());
        RechargeOrder rechargeOrder = userFundAccountForLianlianpayService.createRechargeOrder(rechargeOrderForm);
        return new RechargeOrderDTO(rechargeOrder);
    }

    /**
     * 快捷支付
     * @param orderNo
     * @param password
     * @param randomKey
     * @return
     */
    @PostMapping("rechargeWithQuickpay")
    public RechargeOrderDTO rechargeWithQuickpay(@RequestParam String orderNo, @RequestParam String password, @RequestParam String randomKey){

        try {
            RechargeOrder rechargeOrder = userFundAccountForLianlianpayService.rechargeWithQuickpay(orderNo, password, randomKey);
            return new RechargeOrderDTO(rechargeOrder);
        }catch(RedisLockException e){
            throw new BusinessException("交易正在提交，请稍后再试");
        }
    }


    /**
     * 获取充值订单
     * @param orderNo
     * @return
     */
    @RequestMapping("getRechargeOrder")
    public RechargeOrderDTO getRechargeOrder(@RequestParam String orderNo){
        RechargeOrder rechargeOrder = rechargeOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("充值订单不存在"));
        return new RechargeOrderDTO(rechargeOrder);
    }

    /**
     * 创建提现订单
     * @return
     */
    @PostMapping("createWithdraw")
    public WithdrawApplyDTO createWithdraw(@RequestBody WithdrawApplyForm withdrawApplyDTO){
        User user = currentUser();
        withdrawApplyDTO.setUserId(user.getId());
        BalanceInfoDTO balanceInfoDTO = userFundAccountForLianlianpayService.getBalance(withdrawApplyDTO.getUserId());
        //判断余额是否足够
        if(balanceInfoDTO.getAvailableBalance().compareTo(withdrawApplyDTO.getWithdrawAmount()) < 0){
            throw new BusinessException("可提现余额不足" + withdrawApplyDTO.getWithdrawAmount());
        }

        WithdrawApply withdrawApply = userFundAccountForLianlianpayService.createWithdrawApply(withdrawApplyDTO);
        return new WithdrawApplyDTO(withdrawApply);
    }



    /**
     * 获取提现申请
     * @param orderNo
     * @return
     */
    @RequestMapping("getWithdrawApply")
    public WithdrawApplyDTO getWithdrawApply(@RequestParam String orderNo){
        WithdrawApply withdrawApply = withdrawApplyRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("提现记录不存在"));
        return new WithdrawApplyDTO(withdrawApply);
    }

    /**
     * 创建续费订单
     * @return
     * @throws Exception
     */
    @PostMapping(value = "/createRenewOrder")
    @ResponseBody
    public WalletRenewOrder createRenewOrder(@RequestParam String tradeType){
        User user = currentUser();
        TradeType tradeTypeObj = WALLET_ACCOUNT;
        if(StringUtils.isNotEmpty(tradeType)){
            tradeType = tradeType.trim();
            if(!tradeType.equals(TradeType.QUICK_PAY.name())  && !tradeType.equals(TradeType.WALLET_ACCOUNT.name())){
                throw new BusinessException("交易类型不正确，仅支持快捷支付和钱包余额交易方式");
            }

            tradeTypeObj = TradeType.valueOf(tradeType);
        }

        WalletRenewOrder walletRenewOrder =  userFundAccountForLianlianpayService.createRenewOrder(user.getId(), tradeTypeObj);

        return walletRenewOrder;
    }

    /**
     * 获取续费记录
     * @param orderNo
     * @return
     */
    @RequestMapping("getRenewOrder")
    public WalletRenewOrder getRenewOrder(@RequestParam String orderNo){
        WalletRenewOrder walletRenewOrder = walletRenewOrderRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("续费记录不存在"));
        return walletRenewOrder;
    }


    /**
     * 钱包支付创建
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/payCreate", method = RequestMethod.GET)
    @ResponseBody
    public PaymentInfo payCreate(@RequestParam String tradeType, Long orderId, Long giftOrderId) throws Exception {
        TradeType tradeTypeObj = WALLET_ACCOUNT;
        if(StringUtils.isNotEmpty(tradeType)){
            tradeType = tradeType.trim();
            if(!tradeType.equals(TradeType.QUICK_PAY.name())  && !tradeType.equals(TradeType.WALLET_ACCOUNT.name())){
                throw new BusinessException("交易类型不正确，仅支持快捷支付和钱包余额交易方式");
            }

            tradeTypeObj = TradeType.valueOf(tradeType);
        }

        if(orderId == null && giftOrderId == null){
            throw new BusinessException("必须指定一个订单ID");
        }

        if(orderId != null && giftOrderId != null){
            throw new BusinessException("普通订单ID和转赠订单ID不能同时指定");
        }

        UniformPayRequest request = UniformPayRequest.builder().orderId(orderId)
                .giftOrderId(giftOrderId)
                .tradeType(tradeTypeObj).build();
        //指定连连付支付
        request.setPayPlatform(UniformPayConstants.LIANLIANPAY_PLATFORM);
        UniformPayResult result = uniformPayUtil.pay(request);
        OrderStatus orderStatus = result.getOrderStatus();
        if(orderStatus == OrderStatus.CANCELLED){
            throw new BusinessException("订单已被取消，请重新下单");
        }

        if(orderStatus != OrderStatus.NOT_PAID){
            throw new BusinessException("订单交易已完成，不能重复支付");
        }

        PaymentInfo paymentInfo =  paymentInfoRepo.findByOrderNo(result.getOrderNo())
                .orElseThrow(new BusinessException("支付创建失败"));
        return paymentInfo;
    }


    /**
     * 执行支付
     * @param orderNo
     * @param password
     * @param randomKey
     * @return
     */
    @PostMapping("payExecute")
    public PayResultDTO payExecute(@RequestParam String orderNo, String password, String randomKey){

        //使用支付渠道确认支付方式
        FundChangeType fundChangeType = userFundChangeLogService.identChangeType(orderNo);
        if(fundChangeType == null){
            throw new BusinessException("未找到订单记录");
        }

        PayResultDTO payResultDTO = null;
        if(fundChangeType == FundChangeType.PAYMENT){
            PaymentInfo paymentInfo = userFundAccountForLianlianpayService.collectionOrderExecute(orderNo, password, randomKey);
            payResultDTO = new PayResultDTO(paymentInfo);
        }else if(fundChangeType == FundChangeType.RENEW){
            WalletRenewOrder walletRenewOrder = userFundAccountForLianlianpayService.renewOrderExecute(orderNo, password, randomKey);
            payResultDTO = new PayResultDTO(walletRenewOrder);
        }else{
            throw new BusinessException("不支持的支付类型:" + fundChangeType);
        }

        return payResultDTO;

    }




    /**
     * 获取支付记录
     * @param orderNo
     * @return
     */
    @RequestMapping("getPaymentInfo")
    public PaymentInfo getPaymentInfo(@RequestParam String orderNo){
        PaymentInfo paymentInfo = paymentInfoRepo.findByOrderNo(orderNo).orElseThrow(new BusinessException("支付记录不存在"));
        return paymentInfo;
    }


    /**
     * 交易二次短信确认
     * @param orderNo
     * @param verifyCode
     * @return
     */
    @PostMapping("tradeConfirm")
    public JSONObject tradeConfirm(@RequestParam String orderNo, @RequestParam String verifyCode){

        //使用支付渠道确认支付方式
        FundChangeType fundChangeType = userFundChangeLogService.identChangeType(orderNo);
        if(fundChangeType == null){
            throw new BusinessException("未找到订单记录");
        }

        JSONObject result = new JSONObject();
        result.put("orderNo", orderNo);
        result.put("type", fundChangeType);
        try {
            switch (fundChangeType) {
                case PAYMENT:
                    PaymentInfo paymentInfo = userFundAccountForLianlianpayService.payConfirm(orderNo, verifyCode);
                    result.put("status", paymentInfo.getStatus());
                    break;
                case RENEW:
                    WalletRenewOrder walletRenewOrder = userFundAccountForLianlianpayService.renewOrderConfirm(orderNo, verifyCode);
                    result.put("status", walletRenewOrder.getStatus());
                    break;
                case WITHDRAW:
                    WithdrawApply withdrawApply = userFundAccountForLianlianpayService.withdrawConfirm(orderNo, verifyCode);
                    result.put("status", withdrawApply.getStatus());
                    break;
                case RECHARGE:
                    RechargeOrder rechargeOrder = userFundAccountForLianlianpayService.rechargeConfirm(orderNo, verifyCode);
                    result.put("status", rechargeOrder.getStatus());
                    break;
                default:
                    throw new BusinessException("暂不支持处理类型:" + fundChangeType);
            }
        }catch(RedisLockException e){
            throw new BusinessException("交易正在短信确认，请稍后再试");
        }

        return result;
    }



    /**
     * 资金变更记录
     * @param pageQuery
     * @return
     */
    @PostMapping("changeLogList")
    public Page<UserFundChangeLog> changeLogList(@RequestBody  PageQuery pageQuery){
        User user = currentUser();
        pageQuery.getQuery().put("userId", user.getId());
        Page<UserFundChangeLog> page = userFundChangeLogService.all(pageQuery);
        return page;
    }


    /**
     * 绑卡查询
     * @return
     */
    @RequestMapping("bindCardQuery")
    public JSONArray bindCardQuery(){
        User user = currentUser();
        JSONObject res = lianlianPayService.bindCardQuery(user.getId());
        JSONArray list = res.getJSONArray("linked_acctlist");
        return list;
    }

    /**
     * 账户注销申请
     * @return
     */
    @PostMapping("cancelApply")
    public JSONObject cancelApply(@RequestParam String password, @RequestParam String randomKey){
        User user = currentUser();
        JSONObject res = userFundAccountForLianlianpayService.cancelApply(user.getId(), password, randomKey);
        JSONObject result = new JSONObject();

        String cancelOrderNo = res.getString("txn_seqno");
        result.put("cancelOrderNo", cancelOrderNo);
        return result;
    }



    /**
     * 查询交易流水
     * @param pageQuery
     * @return
     */
    @PostMapping("tradeLogList")
    @PreAuthorize("hasRole('ADMIN')")
    public Page<JSONObject>  tradeLogList(@RequestBody  PageQuery pageQuery){
        String userId = Objects.toString(pageQuery.getQuery().get("userId"), "");
        if(StringUtils.isEmpty(userId)){
            throw new BusinessException("必须指定用户");
        }

        TradeLogPageDTO tradeLogDTO = userFundAccountForLianlianpayService.tradeLogList(Long.valueOf(userId), pageQuery);

        return tradeLogDTO;
    }




}
