package com.sonic.lion.service.impl;

import com.sonic.common.AppRuntime;
import com.sonic.common.utils.RedisLock;
import com.sonic.lion.dao.PayCallChannelRecordDao;
import com.sonic.lion.dao.PayTradeDao;
import com.sonic.lion.dao.ProcessingChargeDao;
import com.sonic.lion.dao.ProcessingWithdrawDao;
import com.sonic.lion.domain.entity.PayCallChannelRecord;
import com.sonic.lion.domain.entity.PayTrade;
import com.sonic.lion.domain.entity.ProcessingCharge;
import com.sonic.lion.domain.entity.ProcessingWithdraw;
import com.sonic.lion.domain.enums.CallChannelStatus;
import com.sonic.lion.domain.output.ChannelCheckOutput;
import com.sonic.lion.domain.output.SyncChannelOutput;
import com.sonic.lion.domain.output.TradeHandleOutput;
import com.sonic.lion.enums.BizType;
import com.sonic.lion.enums.ToastResultCode;
import com.sonic.lion.enums.TradeStatus;
import com.sonic.lion.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicReference;

@Service
@Slf4j
public class ChannelProcessingServiceImpl implements ChannelProcessingService {

    @Autowired
    private PayCallChannelService payCallChannelService;

    @Autowired
    private PayTradeService payTradeService;

    @Autowired
    private PayTradeDao payTradeDao;

    @Autowired
    private WithdrawService withdrawService;

    @Autowired
    private RefundService refundService;

    @Autowired
    private PayCallChannelRecordDao payCallChannelRecordDao;

    @Autowired
    private ProcessingChargeDao processingChargeDao;


    @Autowired
    private ProcessingWithdrawDao processingWithdrawDao;

    @Autowired
    private RedisLock.RedisWrapper redisWrapper;
    @Autowired
    private AppRuntime appRuntime;

    @Override
    public SyncChannelOutput chargeProcessing(PayCallChannelRecord record) {
        AtomicReference<SyncChannelOutput> atomicReference = new AtomicReference<>();
        //加锁，避免定时任务并发重复处理
        RedisLock redisLock = new RedisLock(appRuntime.buildPrefixKey("lock", "chargeProcessingHandler", record.getId()), redisWrapper);
        redisLock.tryAcquireRun(10 * 60 * 1000, () -> {
            atomicReference.set(chargeProcessingHandler(record));
            return true;
        });
        return atomicReference.get();
    }

    /**
     * 充值中的数据状态获取处理
     * @param record
     * @return
     */
    public SyncChannelOutput chargeProcessingHandler(PayCallChannelRecord record) {
        //业务类型判断：非充值业务类型，不进行处理。直接抛出异常
        ToastResultCode.UNSUPPORTED_BUSINESS_TYPE.check(!BizType.CHARGE.equals(record.getBizType()));
        //查询充值中的基础数据
        ProcessingCharge processingCharge = processingChargeDao.findByRecordId(record.getId());
        //数据不存在，则直接快速返回
        if(processingCharge == null) {
            return null;
        }
        //判断数据状态是否正确（状态不为 初始化、处理中 则删掉 充值中的基础数据，并快速返回）
        if (!CallChannelStatus.PROCESSING.equals(record.getStatus()) && !CallChannelStatus.INIT.equals(record.getStatus())) {
            processingChargeDao.deleteById(processingCharge.getId());
            return SyncChannelOutput.builder().callChannelStatus(record.getStatus()).build();
        }
        log.info("===> chargeProcessingHandler record :{}", record);
        //从渠道侧 获取充值数据是否正确
        ChannelCheckOutput checkOutput = payCallChannelService.checkRecord(record);
        log.info("===> chargeProcessingHandler checkOutput :{}", checkOutput);
        //根据交易号获取交易基础数据
        PayTrade payTrade = payTradeService.getByTradeNo(record.getTradeNo());
        //判断交易状态，如果当前付款交易状态不为待处理、已关闭状态则直接删掉 充值中的基础数据
        if(TradeStatus.WAITPAY != payTrade.getStatus() && TradeStatus.CLOSED != payTrade.getStatus()) {
            processingChargeDao.deleteById(processingCharge.getId());
            return SyncChannelOutput.builder().callChannelStatus(checkOutput.getStatus()).message(checkOutput.getErrorMessage()).build();
        }
        //拉黑也是争议的一种
        if (checkOutput.isInBlackList() && checkOutput.getTransactionId() != null) {
            //更新交易状态
            payTradeDao.updateStatusErrorMessage(CallChannelStatus.toTradeStatus(checkOutput.getStatus()).getValue(), checkOutput.getErrorMessage(), payTrade.getTradeNo());
            //进行退款操作
            refundService.tryRefund(payTrade, record);
            //抛出异常
            ToastResultCode.UNSUPPORTED_BUSINESS_TYPE.check(true);
        }
        //订单已经过期 设置为过期（）
        if (record.getExpTime() != null && record.getExpTime().isBefore(LocalDateTime.now())) {
            checkOutput.setStatus(CallChannelStatus.EXPIRED);
        }

        //处理成功的时候 充值处理
        if (checkOutput.getStatus() == CallChannelStatus.SUCC) {
            TradeHandleOutput tradeHandleOutput = payTradeService.tradeHandleV2(record.getChannel(), record.getTradeNo(), checkOutput.getChannelEvent());
            tradeHandleOutput.setChargeAmount(payTrade.getAmount());
            handWebsocket(payTrade, tradeHandleOutput);
            //失败处理
        } else if (checkOutput.getStatus() == CallChannelStatus.FAIL) {
            payTradeDao.updateStatusErrorMessage(CallChannelStatus.toTradeStatus(checkOutput.getStatus()).getValue(), checkOutput.getErrorMessage(), payTrade.getTradeNo());
        } else if (checkOutput.getStatus() == CallChannelStatus.PROCESSING) {
            //处理中不做处理
        } else if (checkOutput.getStatus() == CallChannelStatus.CANCEL) {
            //更新状态为取消
            payTradeDao.updateStatusErrorMessage(CallChannelStatus.toTradeStatus(checkOutput.getStatus()).getValue(), checkOutput.getErrorMessage(), payTrade.getTradeNo());
        } else if (checkOutput.getStatus() == CallChannelStatus.EXPIRED) {
            payCallChannelRecordDao.updateStatus(record.getId(), CallChannelStatus.EXPIRED.getValue());
            //渠道调用已经失效  根据是否返回交易ID 决定是否退款
            if (StringUtils.isNotEmpty(checkOutput.getTransactionId())) {
                try {
                    refundService.tryRefund(payTrade, record);
                } catch (Exception e) {
                    payTradeDao.updateStatusErrorMessage(CallChannelStatus.toTradeStatus(checkOutput.getStatus()).getValue(), checkOutput.getErrorMessage(), payTrade.getTradeNo());
                }
            } else {
                payTradeDao.updateStatusErrorMessage(CallChannelStatus.toTradeStatus(checkOutput.getStatus()).getValue(), checkOutput.getErrorMessage(), payTrade.getTradeNo());
            }
        }
        //删除处理中的基础数据
        if (!CallChannelStatus.PROCESSING.equals(checkOutput.getStatus()) && !CallChannelStatus.INIT.equals(checkOutput.getStatus())) {
            processingChargeDao.deleteById(processingCharge.getId());
        }
        //构造输出对象
        return SyncChannelOutput.builder().callChannelStatus(checkOutput.getStatus()).message(checkOutput.getErrorMessage()).build();
    }

    @Override
    public SyncChannelOutput withdrawProcessing(PayCallChannelRecord record) {
        //同步的类型不是充值 不处理
        ToastResultCode.UNSUPPORTED_BUSINESS_TYPE.check(!BizType.WITHDRAW.equals(record.getBizType()));
        //数据不存在，则直接快速返回
        ProcessingWithdraw processingWithdraw = processingWithdrawDao.findByRecordId(record.getId());
        if (processingWithdraw == null) {
            return null;
        }
        //提现中的数据状态不为 初始化、处理中 则表明已经走到了终态，直接删除掉数据并快速返回
        if (!CallChannelStatus.PROCESSING.equals(record.getStatus()) && !CallChannelStatus.INIT.equals(record.getStatus())) {
            processingWithdrawDao.deleteById(processingWithdraw.getId());
            return SyncChannelOutput.builder().callChannelStatus(record.getStatus()).build();
        }
        //根据交易号查询交易基础数据
        PayTrade payTrade = payTradeService.getByTradeNo(record.getTradeNo());
        //从渠道侧 获取提现最终处理结果
        ChannelCheckOutput checkOutput = payCallChannelService.checkRecord(record);
        //处理成功的时候 充值处理
        if (checkOutput.getStatus() == CallChannelStatus.SUCC) {
            payTradeService.tradeHandleV2(record.getChannel(), record.getTradeNo(), checkOutput.getChannelEvent());
            withdrawService.withdrawSuccess(record.getTradeNo());
            //处理失败 返回异常消息， 提现如果失败 回滚资金
        } else if (checkOutput.getStatus() == CallChannelStatus.FAIL) {
            withdrawService.withdrawFail(payTrade.getTradeNo(), payTrade.getBizType(), checkOutput.getErrorMessage());
            payTradeDao.updateStatusErrorMessage(CallChannelStatus.toTradeStatus(checkOutput.getStatus()).getValue(), checkOutput.getErrorMessage(), payTrade.getTradeNo());
            //更新处理中的 错误消息
        } else if (checkOutput.getStatus() == CallChannelStatus.PROCESSING) {
            payTradeDao.updateErrorMessage(payTrade.getTradeNo(), checkOutput.getErrorMessage());
        } else if (checkOutput.getStatus() == CallChannelStatus.CANCEL) {
            //暂不处理
        }
        
        if (!CallChannelStatus.PROCESSING.equals(checkOutput.getStatus()) && !CallChannelStatus.INIT.equals(checkOutput.getStatus())) {
            processingWithdrawDao.deleteById(processingWithdraw.getId());
        }
        return SyncChannelOutput.builder().callChannelStatus(checkOutput.getStatus()).message(checkOutput.getErrorMessage()).build();
    }


    /**
     * 处理消息
     *
     * @param payTrade
     * @param tradeHandleOutput
     */
    private void handWebsocket(PayTrade payTrade, TradeHandleOutput tradeHandleOutput) {
        try {
            if (payTrade != null && StringUtils.isNotBlank(payTrade.getPaymentTradeNo())) {
                PayTrade paymentTrade = payTradeService.getByTradeNo(payTrade.getPaymentTradeNo());
                tradeHandleOutput.setPaymentTradeNo(paymentTrade.getTradeNo());
                tradeHandleOutput.setPaymentTradeOrderNo(paymentTrade.getOutTradeNo());
                tradeHandleOutput.setPaymentBizType(paymentTrade.getBizType());
            }
        } catch (Exception e) {

        }
    }
}
