package com.ai.da.service.impl;

import com.ai.da.common.constant.AlipayHKConstant;
import com.ai.da.common.enums.CreditsEventsEnum;
import com.ai.da.common.enums.OrderStatusEnum;
import com.ai.da.common.enums.PayTypeEnum;
import com.ai.da.common.utils.AlipayHKEncryptionUtil;
import com.ai.da.common.utils.AlipayHKRequestUtil;
import com.ai.da.common.utils.SendEmailUtil;
import com.ai.da.mapper.primary.AccountMapper;
import com.ai.da.mapper.primary.entity.OrderInfo;
import com.ai.da.model.dto.AlipayHKCallbackDTO;
import com.ai.da.model.dto.AlipayHKRequestDTO;
import com.ai.da.model.dto.ProductPurchaseDTO;
import com.ai.da.service.*;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class AlipayHKServiceImpl implements AlipayHKService {

    @Value("${alipayHK.merchantId}")
    private String merchantId;

    @Value("${alipayHK.segmentId}")
    private String segmentId;

    @Value("${alipayHK.AESKey}")
    private String aesKey;

    @Value("${alipayHK.rsaPrivateKey}")
    private String privateKeyPath;

    @Value("${alipayHK.rsaPublicKey}")
    private String publicKeyPath;

    @Resource
    private OrderInfoService orderInfoService;
    @Resource
    private PaymentInfoService paymentInfoService;
    @Resource
    private CreditsService creditsService;
    @Resource
    private AlipayHKEncryptionUtil alipayHKEncryptionUtil;
    @Resource
    private AlipayHKRequestUtil alipayHKRequestUtil;
    @Resource
    private AccountMapper accountMapper;


    /**
     * 创建订单
     */
    @Override
    public String createOrder(ProductPurchaseDTO productPurchaseDTO, HttpServletRequest request){

        try{
            HashMap<String, Object> param = new HashMap<>();
            String orderRef = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            param.put("order_ref", orderRef);
            param.put("amount", Float.parseFloat(CreditsEventsEnum.PRICE.getValue()) * productPurchaseDTO.getQuantity());
            param.put("subject", "AiDA Credits Purchase");
            // ALIPAYHK 或者 ALIPAYCN
            param.put("wallet", productPurchaseDTO.getWallet());
            param.put("segment_id", segmentId);
//            param.put("payment_solution", "WAP");
            param.put("payment_solution", "PC2MOBILE");
            log.info("alipay-hk 创建订单，参数信息： {}", param);
            // 生成订单
            log.info("创建订单");
            OrderInfo orderInfo = orderInfoService.createOrderByProductId(productPurchaseDTO.getQuantity(), PayTypeEnum.ALIPAY_HK.getType(), request);
            /*// 加密
            AlipayHKRequestDTO alipayHKRequestDTO = alipayHKEncryptionUtil.AESCBCWithRSA(param, AlipayHKConstant.CREATE_ORDER);
            // 请求Alipay服务端
            String response = alipayHKRequestUtil.requestAlipayHK(alipayHKRequestDTO);
            // 获取response中的加密数据
            JSONObject responseObj = JSONObject.parseObject(response);
            JSONObject resultObj = JSONObject.parseObject(responseObj.get("result").toString());
            String nonce = resultObj.get("nonce").toString();
            String message = resultObj.get("message").toString();
            // 解密
            String s = alipayHKEncryptionUtil.decryptAES(message, nonce);
            JSONObject jsonObject = JSONObject.parseObject(s);*/
            String s = enAndDecryption(param, AlipayHKConstant.CREATE_ORDER);
            JSONObject jsonObject = JSONObject.parseObject(s);
            String orderId = jsonObject.get("out_trade_no").toString();

            orderInfoService.updateOrderNoById(orderInfo.getId(), orderId);
            log.info("create_order 接口返回信息 ：{}", s);

            return s;
        }catch (Exception e){
            log.error("订单创建失败 ： {}", e.getMessage());
            // todo 或者抛异常
            return null;
        }

    }

    private String enAndDecryption(HashMap<String,Object> param, String serviceName){
        try{
            // 加密
            AlipayHKRequestDTO alipayHKRequestDTO = alipayHKEncryptionUtil.AESCBCWithRSA(param, serviceName);
            // 请求Alipay服务端
            String response = alipayHKRequestUtil.requestAlipayHK(alipayHKRequestDTO);
            // 获取response中的加密数据
            JSONObject responseObj = JSONObject.parseObject(response);
            JSONObject resultObj = JSONObject.parseObject(responseObj.get("result").toString());
            String nonce = resultObj.get("nonce").toString();
            String message = resultObj.get("message").toString();
            // 解密
            return alipayHKEncryptionUtil.decryptAES(message, nonce);
        }catch (Exception e){
            log.error("订单创建失败 ： {}", e.getMessage());
            // todo 或者抛异常
            return null;
        }

    }

    /**
     * 异步回调
     * @return
     */
    public String callback(String paramString){
        log.info("支付宝（HK）通知正在执行");
        Map<String, String> params = JSONObject.parseObject(paramString, Map.class);

        String result = "failure";
        String data = params.get("data");
        String signature = params.get("signature");

        try {
            // 异步回调验签
            Boolean verification = alipayHKEncryptionUtil.signatureVerification(data, signature);
            if (!verification){
                log.error("Alipay-hk 验签失败");
                return result;
            }
            log.info("Alipay-HK 验签成功");

        String dataString = data.replace("\\\"", "\"")
                .replace("\"{", "{")
                .replace("}\"", "}");

            AlipayHKCallbackDTO alipayHKCallbackDTO = JSONObject.parseObject(dataString, AlipayHKCallbackDTO.class);
            //按照支付结果异步通知中的描述，对支付结果中的业务内容进行二次校验，
            //1 商户需要验证该通知数据中的 out_trade_no 是否为商户系统中创建的订单号
            String outTradeNo = alipayHKCallbackDTO.getOut_trade_no();
            OrderInfo order = orderInfoService.getOrderByOrderNo(outTradeNo);
            if(order == null){
                log.error("订单：{} 不存在",outTradeNo);
                return result;
            }

            //2 判断 total_amount 是否确实为该订单的实际金额（即商户订单创建时的金额）
            Float totalAmount = alipayHKCallbackDTO.getAmount();
            Float totalFee = order.getTotalFee();
            if(!Objects.equals(totalAmount, totalFee)){
                log.error("金额校验失败，回调中金额为：{}，实际金额为：{}", totalAmount, totalFee);
                return result;
            }

            //3 校验通知中的 seller_id（或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方
            String sellerId = alipayHKCallbackDTO.getMerchant_id();
            String sellerIdProperty = merchantId;
            if(!sellerId.equals(sellerIdProperty)){
                log.error("商家merchantId校验失败，回调中merchantId为：{}，实际merchantId为：{}", sellerId, sellerIdProperty);
                return result;
            }

            //4 验证 app_id 是否为该商户本身
            String segId = alipayHKCallbackDTO.getSegment_id();
            String segmentIdProperty = segmentId;
            if(!segId.equals(segmentIdProperty)){
                log.error("segmentId校验失败，回调中segmentId为：{}，实际segmentId为：{}", segId, segmentIdProperty);
                return result;
            }

            //在支付宝的业务通知中，只有交易通知状态为 TRADE_SUCCESS时，
            // 支付宝才会认定为买家付款成功。
            String tradeStatus = alipayHKCallbackDTO.getStatus();
            if(!AlipayHKConstant.STATUS_PAID.equals(tradeStatus)){
                log.error("支付未成功");
                return result;
            }

            log.info("Alipay-HK参数二次校验成功，进入数据处理与存储");
            processOrder(alipayHKCallbackDTO);
            result = "success";
        }catch (Exception e){
            log.error(e.getMessage());
        }

        log.info("Alipay-HK 回调处理结果：{}", result);
        return result;

    }

    private final ReentrantLock lock = new ReentrantLock();


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void processOrder(AlipayHKCallbackDTO alipayHKCallbackDTO) {

        log.info("处理订单");

        //获取订单号
        String orderNo = alipayHKCallbackDTO.getOut_trade_no();
        String totalAmount = alipayHKCallbackDTO.getAmount().toString();

        /*在对业务数据进行状态检查和处理之前，
        要采用数据锁进行并发控制，
        以避免函数重入造成的数据混乱*/
        //尝试获取锁：
        // 成功获取则立即返回true，获取失败则立即返回false。不必一直等待锁的释放
        if(lock.tryLock()) {
            try {
                //处理重复通知
                //接口调用的幂等性：无论接口被调用多少次，以下业务执行一次
//                String orderStatus = orderInfoService.getOrderStatus(orderNo);
                OrderInfo orderByOrderNo = orderInfoService.getOrderByOrderNo(orderNo);
                String orderStatus = orderByOrderNo.getOrderStatus();
                // 当订单状态处于未支付或超时已关闭时，更新订单状态，其他状态均不更新订单状态
                if (!OrderStatusEnum.NOT_PAY.getType().equals(orderStatus) && !OrderStatusEnum.TIMEOUT_CLOSED.getType().equals(orderStatus)) {
                    log.info("订单状态 : {}", orderStatus);
                    return;
                }
                //更新订单状态
                orderInfoService.updateStatusByOrderNo(orderNo, OrderStatusEnum.SUCCESS);
                log.info("Alipay-HK 订单：{} 状态更新成功",orderNo);
                //记录支付日志
                paymentInfoService.createPaymentInfoForAliPayHK(alipayHKCallbackDTO, "credits");
                log.info("Alipay-HK 订单：{} 支付信息状态更新成功",orderNo);
                float quantity = Float.parseFloat(totalAmount) / Float.parseFloat(CreditsEventsEnum.PRICE.getValue());
                // 更新积分
                creditsService.buyCredits(orderByOrderNo.getAccountId(), quantity);
                // 添加积分变更记录
                creditsService.insertToCreditsDetail(orderByOrderNo.getAccountId(),
                        CreditsEventsEnum.BUY_CREDITS.getName() + "--AlipayHK",
                        String.valueOf((Long.parseLong(CreditsEventsEnum.BUY_CREDITS.getValue()) * quantity)),
                        "positive",
                        orderByOrderNo.getOrderNo());
                log.info("用户：{} 积分信息更新成功",orderByOrderNo.getAccountId());
                // 填入用户信息，邮件通知Kim
                String username = accountMapper.selectById(orderByOrderNo.getAccountId()).getUserName();
                SendEmailUtil.creditsPurchaseReminder(username, String.valueOf(quantity), totalAmount);
            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }
    }

    // 查询订单详情
    public String queryDetail(String orderRef){
        HashMap<String, Object> param = new HashMap<>();
        param.put("order_ref", orderRef);
        String s = enAndDecryption(param, AlipayHKConstant.ORDER_DETAILS);
        JSONObject jsonObject = JSONObject.parseObject(s);
        log.info("订单：{}，状态为：{}", orderRef, jsonObject.get("status"));

        return s;
    }


}
