package com.mcwl.pay.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.lang.UUID;
import cn.hutool.extra.mail.MailUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundAccountQueryModel;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.internal.util.file.IOUtils;
import com.alipay.api.request.AlipayFundAccountQueryRequest;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipayFundAccountQueryResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.payment.common.models.AlipayTradeCancelResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeQueryResponse;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse;
import com.mcwl.common.JSONUtils;
import com.mcwl.common.core.domain.AjaxResult;
import com.mcwl.common.core.domain.R;
import com.mcwl.common.core.domain.entity.SysUser;
import com.mcwl.common.core.redis.RedisCache;
import com.mcwl.common.exception.ServiceException;
import com.mcwl.common.utils.SecurityUtils;
import com.mcwl.common.utils.ShareCodeUtils;
import com.mcwl.common.utils.StringUtils;
import com.mcwl.memberCenter.domain.MemberLevel;
import com.mcwl.memberCenter.service.MemberLevelService;
import com.mcwl.pay.config.AliConfig;
import com.mcwl.pay.domain.OrderTrade;
import com.mcwl.pay.domain.OrderTradeDto;
import com.mcwl.pay.domain.vo.PayVo;
import com.mcwl.pay.service.AliPayService;
import com.mcwl.pay.service.OrderTradeService;
import com.mcwl.system.domain.SysUserPayAccount;
import com.mcwl.system.domain.SysUserPayAccountLog;
import com.mcwl.system.service.ISysUserPayAccountLogService;
import com.mcwl.system.service.ISysUserPayAccountService;
import com.mcwl.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;

import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 支付宝支付
 */
@Component
@Slf4j
public class AliPayServiceImpl implements AliPayService {

    @Autowired
    private Config config;

    @Autowired
    private RedisCache redisCache;


    @Autowired
    private MemberLevelService memberLevelService;


    @Autowired
    private AliConfig aliConfig;

    @Autowired
    private ISysUserPayAccountService sysUserPayAccountService;

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    private ISysUserPayAccountLogService sysUserPayAccountLogService;

    @Autowired
    private OrderTradeService orderTradeService;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private final int MAX_RETRY = 3; // 最大重试次数


    public AliPayServiceImpl(AliConfig aliConfig) {
        Config config = new Config();
        config.protocol = "https";
        config.signType = "RSA2";
        config.appId = aliConfig.getAppId();
        config.merchantPrivateKey = aliConfig.getPrivateKey();
        config.merchantCertPath = aliConfig.getAppCertPath();
        config.alipayCertPath = aliConfig.getAlipayCertPath();
        config.alipayRootCertPath = aliConfig.getAlipayRootCertPath();
        config.gatewayHost = aliConfig.getGatewayHost();
        config.notifyUrl = aliConfig.getNotifyUrl();

        Factory.setOptions(config);
    }


    /**
     * 调用支付宝预下订单接口
     *
     * @param orderTradeDto 订单实体
     * @return 二维码url
     * @throws Exception
     */
//    public String orderPay(OrderTradeDto orderTradeDto) throws Exception {
//        Integer productId = orderTradeDto.getProductId();
//        if (!Optional.ofNullable(productId).isPresent()) {
//            throw new ServiceException("mallProductId不能为空");
//        }
//
//        MallProduct mallProduct = mallProductService.getById(productId);
//        if (!Optional.ofNullable(mallProduct).isPresent()) {
//            throw new ServiceException("mallProduct不存在");
//        }
//
//        // 设置orderTrade信息
//        OrderTrade tradeEntity = new OrderTrade();
//        BeanUtil.copyProperties(orderTradeDto, tradeEntity);
//        tradeEntity.setCode(UUID.randomUUID().toString(true).substring(0, 30));
//        tradeEntity.setUserId(SecurityUtils.getUserId());
//        tradeEntity.setUserName(SecurityUtils.getUsername());
//        tradeEntity.setProductName(mallProduct.getProductName());
//
//        //调用支付宝的接口
//        AlipayTradePrecreateResponse payResponse = Factory.Payment.FaceToFace()
//                .preCreate(mallProduct.getProductName(),
//                        tradeEntity.getCode() + "_product",
//                        orderTradeDto.getPaymentAmount().toString());
//        // 缓存到redis
//        redisCache.setCacheObject(tradeEntity.getCode() + "_product", JSONUtil.toJsonStr(tradeEntity), 3, TimeUnit.MINUTES);
////        AlipayTradePrecreateResponse payResponse = Factory.Payment.FaceToFace().preCreate("订单主题：Mac笔记本", "LS123qwe123", "19999");
//        //参照官方文档响应示例，解析返回结果
//        String httpBodyStr = payResponse.getHttpBody();
//        JSONObject jsonObject = JSONObject.parseObject(httpBodyStr);
//        return jsonObject.getJSONObject("alipay_trade_precreate_response").get("qr_code").toString();
//    }


    /**
     * 调用支付宝预下订单接口
     *
     * @param orderTradeDto 订单实体
     * @return 二维码url
     * @throws Exception
     */
    @Override
    public PayVo memberPay(OrderTradeDto orderTradeDto) throws Exception {

        // 会员等级id
        Long productId = orderTradeDto.getProductId();

        if (!Optional.ofNullable(productId).isPresent()) {
            throw new ServiceException("memberLevelId不能为空");
        }

        MemberLevel memberLevel = memberLevelService.getById(productId);

        if (!Optional.ofNullable(memberLevel).isPresent()) {
            throw new ServiceException("memberLevel不存在");
        }

        // 设置orderTrade信息
        OrderTrade tradeEntity = new OrderTrade();
        BeanUtil.copyProperties(orderTradeDto, tradeEntity);
        tradeEntity.setCode(UUID.randomUUID().toString(true).substring(0, 30) + "_member");
        tradeEntity.setUserId(SecurityUtils.getUserId());
        tradeEntity.setUserName(SecurityUtils.getUsername());
        tradeEntity.setProductId(productId);
        tradeEntity.setProductName(memberLevel.getMemberName());
        tradeEntity.setTotalAmount(orderTradeDto.getAmount());
        tradeEntity.setOrderStatus(1);
        tradeEntity.setPayStatus(1);
        orderTradeService.save(tradeEntity);

        //调用支付宝的接口
        AlipayTradePrecreateResponse payResponse = Factory.Payment.FaceToFace()
                // 设置过期时间
                .preCreate(memberLevel.getMemberName(),
                        tradeEntity.getCode(),
                        orderTradeDto.getAmount().toString());
        // 缓存到redis
        redisCache.setCacheObject(tradeEntity.getCode() + "_promotionId", orderTradeDto.getPromotionId());
//        AlipayTradePrecreateResponse payResponse = Factory.Payment.FaceToFace().preCreate("订单主题：Mac笔记本", "LS123qwe123", "19999");
        //参照官方文档响应示例，解析返回结果
        String httpBodyStr = payResponse.getHttpBody();
        JSONObject jsonObject = JSONObject.parseObject(httpBodyStr);

        PayVo payVo = new PayVo();
        payVo.setOrderNo(tradeEntity.getCode());
        payVo.setUrl(jsonObject.getJSONObject("alipay_trade_precreate_response").get("qr_code").toString());
        return payVo;
    }


    /**
     * 调用支付宝预下订单接口
     *
     * @param paymentAmount 充值积分
     * @return 二维码url
     * @throws Exception
     */
    @Override
    public PayVo pointsPay(Double paymentAmount) throws Exception {
        // 设置orderTrade信息
        OrderTrade tradeEntity = new OrderTrade();
        tradeEntity.setCode(UUID.randomUUID().toString(true).substring(0, 30) + "_points");
        tradeEntity.setUserId(SecurityUtils.getUserId());
        tradeEntity.setProductId(-1L);
        tradeEntity.setProductName("积分充值");
        tradeEntity.setUserName(SecurityUtils.getUsername());
        tradeEntity.setTotalAmount(paymentAmount);
        tradeEntity.setOrderStatus(1);
        tradeEntity.setPayStatus(1);
        orderTradeService.save(tradeEntity);
        //调用支付宝的接口
        AlipayTradePrecreateResponse payResponse = Factory.Payment.FaceToFace()
                .preCreate(tradeEntity.getProductName(),
                        tradeEntity.getCode(),
                        paymentAmount.toString());
        //参照官方文档响应示例，解析返回结果
        String httpBodyStr = payResponse.getHttpBody();
        JSONObject jsonObject = JSONObject.parseObject(httpBodyStr);

        String url = jsonObject.getJSONObject("alipay_trade_precreate_response").get("qr_code").toString();
        PayVo payVo = new PayVo();
        payVo.setOrderNo(tradeEntity.getCode());
        payVo.setUrl(url);

        return payVo;
    }


    /**
     * 调用支付宝预下订单接口
     *
     * @param paymentAmount 充值金币
     * @return 二维码url
     * @throws Exception
     */
    @Override
    public PayVo walletPay(Double paymentAmount) throws Exception {
        // 设置orderTrade信息
        OrderTrade tradeEntity = new OrderTrade();
        tradeEntity.setCode(UUID.randomUUID().toString(true).substring(0, 30) + "_wallet");
        tradeEntity.setUserId(SecurityUtils.getUserId());
        tradeEntity.setProductId(-1L);
        tradeEntity.setProductName("金币充值");
        tradeEntity.setUserName(SecurityUtils.getUsername());
        tradeEntity.setTotalAmount(paymentAmount);
        tradeEntity.setOrderStatus(1);
        tradeEntity.setPayStatus(1);
        orderTradeService.save(tradeEntity);
        //调用支付宝的接口
        AlipayTradePrecreateResponse payResponse = Factory.Payment.FaceToFace()
                .preCreate(tradeEntity.getProductName(),
                        tradeEntity.getCode(),
                        paymentAmount.toString());
        //参照官方文档响应示例，解析返回结果
        String httpBodyStr = payResponse.getHttpBody();
        JSONObject jsonObject = JSONObject.parseObject(httpBodyStr);

        String url = jsonObject.getJSONObject("alipay_trade_precreate_response").get("qr_code").toString();
        PayVo payVo = new PayVo();
        payVo.setOrderNo(tradeEntity.getCode());
        payVo.setUrl(url);

        return payVo;
    }

    /**
     * 支付宝转账方法
     *
     * @param amount 转账金额
     * @return 返回支付宝转账响应的内容
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> fetch(String amount) throws AlipayApiException, FileNotFoundException {
        Long userId = SecurityUtils.getUserId();
        SysUser sysUser = sysUserService.selectUserById(userId);
        if (sysUser.getWallet() < Double.parseDouble(amount)) {
            return R.fail("钱包余额不足");
        }

        // 初始化SDK
        AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());

        // 构造请求参数以调用接口
        AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
        AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
        // 设置商家侧唯一订单号
        model.setOutBizNo(UUID.fastUUID().toString(true));

        // 设置订单总金额
        model.setTransAmount(amount);

        // 设置描述特定的业务场景
        model.setBizScene("DIRECT_TRANSFER");

        // 设置业务产品码
        model.setProductCode("TRANS_ACCOUNT_NO_PWD");

        // 设置转账业务的标题
        model.setOrderTitle("提现");

        // 查询用户支付宝账户
        SysUserPayAccount sysUserPayAccount = sysUserPayAccountService
                .lambdaQuery()
                .eq(SysUserPayAccount::getUserId, userId)
                .eq(SysUserPayAccount::getType, 0)
                .one();
        if (Objects.isNull(sysUserPayAccount)) {
            return R.fail("请先绑定支付宝账号");
        }

        // 收款方信息
        Participant payeeInfo = new Participant();

        // 沙箱环境
//        payeeInfo.setIdentity("2088722057445020");
//        payeeInfo.setIdentityType("ALIPAY_USER_ID");

//        线上环境
        payeeInfo.setIdentity(sysUserPayAccount.getOpenId());
        payeeInfo.setIdentityType("ALIPAY_OPEN_ID");
        model.setPayeeInfo(payeeInfo);

        request.setBizModel(model);

        // 添加提现记录和修改用户钱包
        this.addLogAndUpdateUserWallet(sysUser, amount);

        // 证书用：certificateExecute(request)，密钥用：execute(request)
        AlipayFundTransUniTransferResponse response = alipayClient.certificateExecute(request);
        System.out.println(response.getBody());

        if (response.isSuccess()) {

            return R.ok("提现成功");
        }

        if (response.getSubCode().equals("PAYER_BALANCE_NOT_ENOUGH")) {
            // 账户余额不足，发送邮件通知
            ArrayList<String> tos = CollUtil.newArrayList("2119157836@qq.com");

            String content = String.format("账户余额不足：用户%s提现%s", sysUser.getUserName(), amount);
            MailUtil.send(tos, "上海辰风互娱", content, false);
            throw new ServiceException("网络连接出错,请稍后再试");
        }
        System.out.println("用户" + SecurityUtils.getUsername() + "提现失败：" + response.getSubMsg());
        throw new ServiceException("提现失败");
    }

    @Override
    public R<Object> queryTradeStatus(String outTradeNo) throws Exception {

        // 查询redis中订单信息
        String orderTradeJson = redisCache.getCacheObject(outTradeNo);
        OrderTrade orderTrade = null;
        if (StringUtils.isNotBlank(orderTradeJson)) {
            orderTrade = JSONUtil.toBean(orderTradeJson, OrderTrade.class);
        }
        // 如果redis中存在该订单, 则直接返回该订单的支付状态
        if (Objects.nonNull(orderTrade)) {
            return R.ok(orderTrade.getOrderStatus());
        }

        // 查询支付宝订单
        orderTrade = orderTradeService.lambdaQuery()
                .eq(OrderTrade::getCode, outTradeNo)
                .one();

        // 如果数据库中不存在该订单, 则返回订单不存在
        if (Objects.isNull(orderTrade)) {
            return R.fail("订单不存在");
        }


        if (orderTrade.getPayStatus() == 1) {
            // 查询支付宝订单
            Object tradStatus = this.getTradStatus(outTradeNo);
            String jsonString = JSONObject.toJSONString(tradStatus);
            JSONObject jsonObject = JSONObject.parseObject(jsonString);
            Object tradeStatus = jsonObject.get("trade_status");
            if (Objects.nonNull(tradeStatus) && tradeStatus.equals("TRADE_SUCCESS")) {
                Map<String, String> params = new HashMap<>();
                params.put("buyer_pay_amount", jsonObject.get("buyer_pay_amount").toString());
                params.put("out_trade_no", jsonObject.get("out_trade_no").toString());
                params.put("gmt_payment", jsonObject.get("send_pay_date").toString());
                params.put("total_amount", jsonObject.get("total_amount").toString());
                params.put("trade_no", jsonObject.get("trade_no").toString());
                String code = orderTrade.getCode();
                // 获取订单后缀
                String suffix = code.substring(code.lastIndexOf("_") + 1);
                orderTradeService.orderHandler(orderTrade, suffix, params);
                return R.ok(orderTrade.getOrderStatus());
            }
            long time = (System.currentTimeMillis() - orderTrade.getCreateTime().getTime()) / 1000 / 60;
            // time 超过15分钟，则删除redis中订单信息
            if (time >= 1) {

                this.cancelTrade(orderTrade);

            }
        }

        redisCache.setCacheObject(outTradeNo, JSONUtil.toJsonStr(orderTrade), 4, TimeUnit.SECONDS);
        return R.ok(orderTrade.getOrderStatus());
    }

    @Override
    public String bindingCallback(String authCode, String state) throws FileNotFoundException {
        try {
            Long userId = ShareCodeUtils.codeToId(state);
            if (Objects.isNull(userId)) {
                return "fail";
            }
            AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());
            AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
            request.setCode(authCode);
            request.setGrantType("authorization_code");
            com.alipay.api.response.AlipaySystemOauthTokenResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                // 支付宝用户唯一ID
                String openId = response.getOpenId();
                // 判断是否已经绑定过
                SysUserPayAccount sysUserPayAccount = sysUserPayAccountService.lambdaQuery()
                        .eq(SysUserPayAccount::getUserId, userId)
                        .eq(SysUserPayAccount::getType, 0)
                        .one();
                if (Objects.nonNull(sysUserPayAccount)) {
                    // 已经绑定过，直接返回
                    return "success";
                }
                System.out.println("绑定成功！openId：" + openId);
                // 将openId与当前商城用户绑定（保存到数据库）
                SysUserPayAccount userPayAccount = new SysUserPayAccount();
                userPayAccount.setUserId(userId);
                userPayAccount.setOpenId(openId);
                userPayAccount.setType(0);
                sysUserPayAccountService.save(userPayAccount);
                return "success";
            } else {
                System.out.println("绑定失败：" + response.getSubMsg());
                return "fail";
            }
        } catch (AlipayApiException e) {
            return "fail";
        }

    }


    private void cancelTrade(OrderTrade orderTrade) throws Exception {

        threadPoolTaskExecutor.execute(() -> {
            try {
                for (int i = 0; i < MAX_RETRY; i++) {
                    // 撤销交易
                    AlipayTradeCancelResponse cancel = Factory.Payment.Common().cancel(orderTrade.getCode());
                    if (cancel.getMsg().equals("Success")) {
                        orderTrade.setOrderStatus(4);
                        orderTradeService.updateById(orderTrade);
                        redisCache.deleteObject(orderTrade.getCode() + "promotionId");
                        break;
                    } else {
                        // 检查是否需要继续重试
                        if (!"Y".equals(cancel.getRetryFlag())) {
                            break;
                        }
                    }
                    Thread.sleep(2000); // 间隔2秒重试
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

    }

    private Object getTradStatus(String outTradeNo) throws Exception {
        AlipayTradeQueryResponse query = Factory.Payment.Common().query(outTradeNo);
        Map<String, Object> map = JSONUtils.jsonToMap(query.getHttpBody());

        // 返回交易结果, 是否交易成功需要根据该对象中的 trade_status 来确定
        // trade_status 的枚举值如下, 请见 https://opendocs.alipay.com/apis/api_1/alipay.trade.query
        // WAIT_BUYER_PAY（交易创建，等待买家付款）
        // TRADE_CLOSED（未付款交易超时关闭，或支付完成后全额退款）
        // TRADE_SUCCESS（交易支付成功）
        // TRADE_FINISHED（交易结束，不可退款）
        // 当 trade_status 等于 TRADE_SUCCESS 或 TRADE_FINISHED 时, 表示支付成功
        return map.get("alipay_trade_query_response");
    }

    private void addLogAndUpdateUserWallet(SysUser sysUser, String amount) {
        BigDecimal wallet = new BigDecimal(sysUser.getWallet().toString());
        BigDecimal amountBigDecimal = new BigDecimal(amount);
        sysUser.setWallet(wallet.subtract(amountBigDecimal).setScale(2, RoundingMode.HALF_UP).doubleValue());
        sysUserService.updateUser(sysUser);

        // 添加记录
        SysUserPayAccountLog sysUserPayAccountLog = new SysUserPayAccountLog();
        sysUserPayAccountLog.setUserId(sysUser.getUserId());
        sysUserPayAccountLog.setAmount(Double.valueOf(amount));
        sysUserPayAccountLog.setAccount("支付宝");
        sysUserPayAccountLogService.save(sysUserPayAccountLog);
    }

    public AlipayConfig getAlipayConfig() throws FileNotFoundException {

        AlipayConfig config = new AlipayConfig();

        // 设置网关地址
        config.setServerUrl(aliConfig.getGatewayUrl());

        // 设置应用ID
        config.setAppId(aliConfig.getAppId());

        // 设置应用私钥
        config.setPrivateKey(aliConfig.getPrivateKey());

        // 设置支付宝公钥，因为配置了证书，所以不需要设置支付宝公钥
//        config.setAlipayPublicKey(aliConfig.getPublicKey());

        // 设置应用公钥证书路径（线上）
//        config.setAppCertPath(getClass().getResource("/cert/appCertPublicKey.crt").getPath());
        // 设置应用公钥证书路径（沙箱）
        config.setAppCertPath(ResourceUtils.getFile("classpath:"+aliConfig.getAppCertPath()).getAbsolutePath());

        // 设置支付宝公钥证书路径
        config.setAlipayPublicCertPath(ResourceUtils.getFile("classpath:"+aliConfig.getAlipayCertPath()).getAbsolutePath());

        // 设置支付宝根证书路径
        config.setRootCertPath(ResourceUtils.getFile("classpath:"+aliConfig.getAlipayRootCertPath()).getAbsolutePath());

        // 设置字符编码格式
        config.setCharset("UTF-8");

        // 设置签名类型
        config.setSignType("RSA2");

        return config;
    }
}
