package com.izouma.nineth.service;

import com.alibaba.fastjson.JSONObject;
import com.izouma.nineth.config.Constants;
import com.izouma.nineth.config.GeneralProperties;
import com.izouma.nineth.domain.*;
import com.izouma.nineth.dto.PayQuery;
import com.izouma.nineth.dto.UserBankCard;
import com.izouma.nineth.enums.AuctionOrderStatus;
import com.izouma.nineth.enums.MintOrderStatus;
import com.izouma.nineth.enums.OrderStatus;
import com.izouma.nineth.enums.PayMethod;
import com.izouma.nineth.event.OrderNotifyEvent;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.*;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

@Service
@Slf4j
@AllArgsConstructor
public class OrderPayService {
    private static String PAY_CHANNEL = Constants.PayChannel.SAND;

    private final OrderService        orderService;
    private final OrderRepo           orderRepo;
    private final MintOrderRepo       mintOrderRepo;
    private final GiftOrderRepo       giftOrderRepo;
    private final SandPayService      sandPayService;
    private final HMPayService        hmPayService;
    private final GeneralProperties   generalProperties;
    private final UserBalanceService  userBalanceService;
    private final RocketMQTemplate    rocketMQTemplate;
    private final GiftOrderService    giftOrderService;
    private final MintOrderService    mintOrderService;
    private final UserRepo            userRepo;
    private final SnowflakeIdWorker   snowflakeIdWorker;
    private final RechargeOrderRepo   rechargeOrderRepo;
    private final SysConfigService    sysConfigService;
    private final PasswordEncoder     passwordEncoder;
    private final PayEaseService      payEaseService;
    private final UserBankCardRepo    userBankCardRepo;
    private final AuctionOrderRepo    auctionOrderRepo;
    private final AuctionOrderService auctionOrderService;

    public static void setPayChannel(String payChannel) {
        log.info("set pay channel {}", payChannel);
        if (Constants.PayChannel.HM.equals(payChannel) || Constants.PayChannel.SAND.equals(payChannel)) {
            PAY_CHANNEL = payChannel;
        }
    }

    @Cacheable(value = "payOrder", key = "'order#'+#orderId")
    public String payOrder(Long orderId) {
        Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        switch (PAY_CHANNEL) {
            case Constants.PayChannel.SAND:
                return sandPayService.pay(orderId + "", order.getName(), order.getTotalPrice(),
                        order.getCreatedAt().plusMinutes(3), "order");
            case Constants.PayChannel.HM:
                return hmPayService.requestAlipay(orderId + "", order.getTotalPrice(), order.getName(),
                        HMPayService.getTimeout(order.getCreatedAt(), 180), Constants.OrderNotifyType.ORDER,
                        generalProperties.getHost() + "/9th/orderDetail?id=" + orderId);
        }
        throw new BusinessException(Constants.PAY_ERR_MSG);
    }

    @Cacheable(value = "payOrder", key = "'order#'+#orderId")
    public String payOrderQuick(Long orderId) {
        Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        return sandPayService.payQuick(orderId + "", order.getName(), order.getTotalPrice(),
                order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.ORDER,
                generalProperties.getHost() + "/9th/home");
    }

    public void payOrderBalance(Long orderId, Long userId, String tradeCode) {
        Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        checkTradeCode(userId, tradeCode, order.getUserId());
        BalanceRecord record = userBalanceService.balancePay(order.getUserId(), order.getTotalPrice(), orderId, order.getName());
        rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
                new OrderNotifyEvent(orderId, PayMethod.BALANCE, record.getId().toString(),
                        System.currentTimeMillis()));
    }

    @Cacheable(value = "payOrder", key = "'order#'+#orderId")
    public Map<String, Object> payOrderAgreement(Long orderId, String bindCardId) {
        Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        if (StringUtils.isEmpty(bindCardId)) {
            bindCardId = userBankCardRepo.findByUserId(order.getUserId())
                    .stream().map(UserBankCard::getBindCardId).findFirst().orElse(null);
        }
        if (StringUtils.isEmpty(bindCardId)) {
            throw new BusinessException("请先绑定银行卡");
        }
        return payEaseService.pay(order.getName(), orderId.toString(), order.getTotalPrice(),
                order.getUserId().toString(), bindCardId, Constants.OrderNotifyType.ORDER);
    }

    public void confirmOrderAgreement(String requestId, String paymentOrderId, String code) {
        try {
            payEaseService.payConfirm(requestId, paymentOrderId, code);
        } catch (BusinessException e) {
            try {
                new Thread(() -> {
                    orderService.cancel(Long.parseLong(requestId));
                }).start();
            } catch (Exception ee) {
            }
            throw e;
        }
    }

    @Cacheable(value = "payOrder", key = "'gift#'+#orderId")
    public String payGiftOrder(Long orderId) {
        GiftOrder order = giftOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        switch (PAY_CHANNEL) {
            case Constants.PayChannel.SAND:
                return sandPayService.pay(orderId + "", "转赠" + order.getAssetId(), order.getGasPrice(),
                        order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.GIFT);
            case Constants.PayChannel.HM:
                return hmPayService.requestAlipay(orderId + "", order.getGasPrice(),
                        "转赠" + order.getAssetId(),
                        HMPayService.getTimeout(order.getCreatedAt(), 180),
                        Constants.OrderNotifyType.GIFT, generalProperties.getHost() + "/9th/home");
        }
        throw new BusinessException(Constants.PAY_ERR_MSG);
    }

    @Cacheable(value = "payOrder", key = "'gift#'+#orderId")
    public String payGiftQuick(Long orderId) {
        GiftOrder order = giftOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        return sandPayService.payQuick(orderId + "", "转赠" + order.getAssetId(), order.getGasPrice(),
                order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.GIFT,
                generalProperties.getHost() + "/9th/home");
    }

    public void payGiftBalance(Long orderId, Long userId, String tradeCode) {
        GiftOrder order = giftOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        checkTradeCode(userId, tradeCode, order.getUserId());
        BalanceRecord record = userBalanceService.balancePay(order.getUserId(), order.getGasPrice(), orderId, "转赠");
        giftOrderService.giftNotify(orderId, PayMethod.BALANCE, record.getId().toString());
    }

    @Cacheable(value = "payOrder", key = "'gift#'+#orderId")
    public Map<String, Object> payGiftOrderAgreement(Long orderId, String bindCardId) {
        GiftOrder order = giftOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != OrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        if (StringUtils.isEmpty(bindCardId)) {
            bindCardId = userBankCardRepo.findByUserId(order.getUserId())
                    .stream().map(UserBankCard::getBindCardId).findFirst().orElse(null);
        }
        if (StringUtils.isEmpty(bindCardId)) {
            throw new BusinessException("请先绑定银行卡");
        }
        return payEaseService.pay("转赠" + order.getAssetId(), orderId.toString(), order.getGasPrice(),
                order.getUserId().toString(), bindCardId, Constants.OrderNotifyType.GIFT);
    }

    @Cacheable(value = "payOrder", key = "'mintOrder#'+#orderId")
    public String payMintOrder(Long orderId) {
        MintOrder order = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != MintOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        switch (PAY_CHANNEL) {
            case Constants.PayChannel.SAND:
                return sandPayService.pay(orderId + "", "铸造活动:" + order.getMintActivityId(),
                        order.getGasPrice(), order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.MINT);
            case Constants.PayChannel.HM:
                return hmPayService.requestAlipay(orderId + "", order.getGasPrice(),
                        "铸造活动:" + order.getMintActivityId(),
                        HMPayService.getTimeout(order.getCreatedAt(), 180),
                        Constants.OrderNotifyType.MINT, generalProperties.getHost() + "/9th/home");
        }
        throw new BusinessException("绿洲宇宙冷却系统已启动，请稍后支付");
    }

    @Cacheable(value = "payOrder", key = "'mintOrder#'+#orderId")
    public String payMintQuick(Long orderId) {
        MintOrder order = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != MintOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        return sandPayService.payQuick(orderId + "", "铸造活动:" + order.getMintActivityId(),
                order.getGasPrice(), order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.MINT,
                generalProperties.getHost() + "/9th/home");
    }

    public void payMintOrderBalance(Long orderId, Long userId, String tradeCode) {
        MintOrder order = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != MintOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        checkTradeCode(userId, tradeCode, order.getUserId());
        BalanceRecord record = userBalanceService.balancePay(order.getUserId(), order.getGasPrice(), orderId, "铸造活动");
        mintOrderService.mintNotify(orderId, PayMethod.BALANCE, record.getId().toString());
    }

    private void checkTradeCode(Long userId, String tradeCode, Long orderUserId) {
        if (!Objects.equals(orderUserId, userId)) {
            throw new BusinessException("订单不属于该用户");
        }
        String encodedPwd = userRepo.findTradeCode(userId);
        if (StringUtils.isEmpty(encodedPwd)) {
            throw new BusinessException("请先设置交易密码");
        }
        if (!passwordEncoder.matches(tradeCode, encodedPwd)) {
            throw new BusinessException("交易码错误");
        }
    }

    @Cacheable(value = "payOrder", key = "'mint#'+#orderId")
    public Map<String, Object> payMintOrderAgreement(Long orderId, String bindCardId) {
        MintOrder order = mintOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != MintOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        if (StringUtils.isEmpty(bindCardId)) {
            bindCardId = userBankCardRepo.findByUserId(order.getUserId())
                    .stream().map(UserBankCard::getBindCardId).findFirst().orElse(null);
        }
        if (StringUtils.isEmpty(bindCardId)) {
            throw new BusinessException("请先绑定银行卡");
        }
        return payEaseService.pay("铸造活动:" + order.getMintActivityId(), orderId.toString(), order.getGasPrice(),
                order.getUserId().toString(), bindCardId, Constants.OrderNotifyType.MINT);
    }

    public String recharge(Long userId, BigDecimal amount) {
        BigDecimal minAmount = sysConfigService.getBigDecimal("min_recharge_amount");
        if (amount.compareTo(minAmount) < 0) {
            throw new BusinessException("充值金额不能小于" + minAmount);
        }
        if (amount.compareTo(new BigDecimal("50000")) > 0) {
            throw new BusinessException("充值金额不能大于50000");
        }
        RechargeOrder order = RechargeOrder.builder()
                .id(snowflakeIdWorker.nextId())
                .userId(userId)
                .amount(amount)
                .status(OrderStatus.NOT_PAID)
                .build();
        rechargeOrderRepo.save(order);
        switch (PAY_CHANNEL) {
            case Constants.PayChannel.SAND:
                return sandPayService.pay(order.getId() + "", "余额充值", order.getAmount(),
                        order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.RECHARGE);
            case Constants.PayChannel.HM:
                return hmPayService.requestAlipay(order.getId() + "", order.getAmount(),
                        "余额充值",
                        HMPayService.getTimeout(order.getCreatedAt(), 180),
                        Constants.OrderNotifyType.RECHARGE, generalProperties.getHost() + "/9th/home");
        }
        throw new BusinessException("绿洲宇宙冷却系统已启动，请稍后支付");
    }

    public Map<String, Object> rechargeAgreement(Long userId, BigDecimal amount, String bindCardId) {
        BigDecimal minAmount = sysConfigService.getBigDecimal("min_recharge_amount");
        if (amount.compareTo(minAmount) < 0) {
            throw new BusinessException("充值金额不能小于" + minAmount);
        }
        if (amount.compareTo(new BigDecimal("50000")) > 0) {
            throw new BusinessException("充值金额不能大于50000");
        }
        RechargeOrder order = RechargeOrder.builder()
                .id(snowflakeIdWorker.nextId())
                .userId(userId)
                .amount(amount)
                .status(OrderStatus.NOT_PAID)
                .build();
        rechargeOrderRepo.save(order);
        if (StringUtils.isEmpty(bindCardId)) {
            bindCardId = userBankCardRepo.findByUserId(order.getUserId())
                    .stream().map(UserBankCard::getBindCardId).findFirst().orElse(null);
        }
        if (StringUtils.isEmpty(bindCardId)) {
            throw new BusinessException("请先绑定银行卡");
        }
        return payEaseService.pay("余额充值", order.getId().toString(), order.getAmount(),
                order.getUserId().toString(), bindCardId, Constants.OrderNotifyType.RECHARGE);
    }

    public String rechargeQuick(Long userId, BigDecimal amount) {
        BigDecimal minAmount = sysConfigService.getBigDecimal("min_recharge_amount");
        if (amount.compareTo(minAmount) < 0) {
            throw new BusinessException("充值金额不能小于" + minAmount);
        }
        if (amount.compareTo(new BigDecimal("50000")) > 0) {
            throw new BusinessException("充值金额不能大于50000");
        }
        RechargeOrder order = RechargeOrder.builder()
                .id(snowflakeIdWorker.nextId())
                .userId(userId)
                .amount(amount)
                .status(OrderStatus.NOT_PAID)
                .build();
        rechargeOrderRepo.save(order);
        return sandPayService.payQuick(order.getId() + "", "余额充值",
                order.getAmount(), LocalDateTime.now().plusMinutes(3), Constants.OrderNotifyType.RECHARGE,
                generalProperties.getHost() + "/9th/home");
    }

    public JSONObject refund(String orderId, String transactionId, BigDecimal amount, String channel) {
        switch (channel) {
            case Constants.PayChannel.SAND: {
                JSONObject res = sandPayService.refund(orderId, amount);
                if (!"000000".equals(res.getJSONObject("head").getString("respCode"))) {
                    String msg = res.getJSONObject("head").getString("respMsg");
                    throw new BusinessException("退款失败:" + msg);
                }
                return res;
            }
            case Constants.PayChannel.HM: {
                JSONObject res = hmPayService.refund(orderId, amount);
                if (!"REFUND_SUCCESS".equals(res.getString("sub_code"))) {
                    String msg = res.getString("msg");
                    throw new BusinessException("退款失败:" + msg);
                }
                return res;
            }
            case Constants.PayChannel.PE: {
                JSONObject res = payEaseService.refund(orderId, transactionId, amount);
                String status = res.getString("status");
                if (!"SUCCESS".equals(status)) {
                    String error = res.getString("error");
                    String cause = res.getString("cause");
                    throw new BusinessException("退款失败:" + error + ";" + cause);
                }
                return res;
            }
        }
        throw new BusinessException("退款失败");
    }

    public PayQuery query(String orderId) {
        return query(orderId, null);
    }

    public PayQuery query(String orderId, String channel) {
        if (StringUtils.isNotEmpty(channel)) {
            switch (channel) {
                case Constants.PayChannel.SAND:
                    return sandPayService.payQuery(orderId);
                case Constants.PayChannel.HM:
                    return hmPayService.payQuery(orderId);
                case Constants.PayChannel.PE:
                    return payEaseService.payQuery(orderId);
            }
        }
        PayQuery query = sandPayService.payQuery(orderId);
        if (query == null || !query.isExist()) {
            query = hmPayService.payQuery(orderId);
        }
        if (query == null || !query.isExist()) {
            query = payEaseService.payQuery(orderId);
        }
        return Optional.ofNullable(query).orElse(PayQuery.builder().exist(false).build());
    }

    @Cacheable(value = "payOrder", key = "'auctionOrder#'+#orderId")
    public String payAuctionOrder(Long orderId) {
        AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != AuctionOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        switch (PAY_CHANNEL) {
            case Constants.PayChannel.SAND:
                return sandPayService.pay(orderId + "", "拍卖:" + order.getName(), order.getTotalPrice(),
                        order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.AUCTION);
            case Constants.PayChannel.HM:
                return hmPayService.requestAlipay(orderId + "", order.getTotalPrice(),
                        "拍卖:" + order.getName(), HMPayService.getTimeout(order.getCreatedAt(), 180),
                        Constants.OrderNotifyType.AUCTION, generalProperties.getHost() + "/9th/home");
        }
        throw new BusinessException("绿洲宇宙冷却系统已启动，请稍后支付");
    }

    @Cacheable(value = "payOrder", key = "'auctionOrder#'+#orderId")
    public String payAuctionQuick(Long orderId) {
        AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != AuctionOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        return sandPayService.payQuick(orderId + "", "拍卖:" + order.getName(),
                order.getTotalPrice(), order.getCreatedAt().plusMinutes(3), Constants.OrderNotifyType.AUCTION,
                generalProperties.getHost() + "/9th/home");
    }

    public void payAuctionOrderBalance(Long orderId, Long userId, String tradeCode) {
        AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != AuctionOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        checkTradeCode(userId, tradeCode, order.getUserId());
        BalanceRecord record = userBalanceService.balancePay(order.getUserId(), order.getTotalPrice(), orderId, "拍卖");
        auctionOrderService.notify(orderId, PayMethod.BALANCE, record.getId().toString());
    }

    @Cacheable(value = "payOrder", key = "'auctionOrder#'+#orderId")
    public Map<String, Object> payAuctionOrderAgreement(Long orderId, String bindCardId) {
        AuctionOrder order = auctionOrderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
        if (order.getStatus() != AuctionOrderStatus.NOT_PAID) {
            throw new BusinessException("订单状态错误");
        }
        if (StringUtils.isEmpty(bindCardId)) {
            bindCardId = userBankCardRepo.findByUserId(order.getUserId())
                    .stream().map(UserBankCard::getBindCardId).findFirst().orElse(null);
        }
        if (StringUtils.isEmpty(bindCardId)) {
            throw new BusinessException("请先绑定银行卡");
        }
        return payEaseService.pay("拍卖:" + order.getAuctionId(), orderId.toString(), order.getTotalPrice(),
                order.getUserId().toString(), bindCardId, Constants.OrderNotifyType.AUCTION);
    }
}
