package cn.quant.baa.pay.service;

import cn.quant.baa.pay.acquirer.AcquirerProperties;
import cn.quant.baa.pay.acquirer.ChannelResponse;
import cn.quant.baa.pay.acquirer.MerchantAcquirer;
import cn.quant.baa.pay.context.TransactionSession;
import cn.quant.baa.pay.jpa.EntityBuilder;
import cn.quant.baa.pay.jpa.entity.AccountEntity;
import cn.quant.baa.pay.jpa.entity.BatchCycleTriggerEntity;
import cn.quant.baa.pay.jpa.entity.PayGoodsDetailEntity;
import cn.quant.baa.pay.jpa.entity.TransactionSummaryEntity;
import cn.quant.baa.pay.model.web.*;
import cn.quant.baa.pay.util.AssertUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;

import static cn.quant.baa.pay.Constant.PAY_DUE_TIME;
import static cn.quant.baa.pay.dict.MessageEnum.ACQUIRER_NOMATCH;
import static cn.quant.baa.pay.dict.MessageEnum.EXIST_ORDER;

/**
 * Created by Administrator on 2021/8/24 0024.
 */
@Service
public class TransactionService extends BusinessService {

    private static final Logger logger = LoggerFactory.getLogger(TransactionService.class);

    @Autowired
    private MerchantAcquirer acquirer;

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public String check(String institutionId, String productId, String tradeNo, TransactionSession session) {
        String md5 = DigestUtils.md5DigestAsHex(String.join(institutionId, productId, tradeNo).getBytes());
        TransactionSummaryEntity historyEntity = session.findOne(TransactionSummaryEntity.class, md5);
        if (historyEntity != null) {
            AssertUtils.throwMessage(EXIST_ORDER, institutionId, productId, tradeNo);
        }
        return md5;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public ChannelResponse pay(AcquirerProperties properties, PayRequestData data) throws Exception {

        validate(data);

        TransactionSession session = TransactionSession.session();

        Long chanId = properties.getMchChanId();
        String institutionId = properties.getInstitutionId();
        String productId = properties.getProductId();
        String buyerId = data.getBuyerId();
        String outTradeNo = data.getOutTradeNo();


        String checkCode = check(institutionId, productId, outTradeNo, session);

        prepare(institutionId, productId, buyerId, session);

        String mchId = data.getMchId();
        if (!mchId.equals(properties.getPayAppId())) {
            AssertUtils.throwMessage(ACQUIRER_NOMATCH, chanId, mchId);
        }

        AccountEntity account = session.getAccount();
        long historyId = session.nextId();
        Integer partitionKey = account.getPartitionKey();
        GoodsDetail[] goodsDetails = data.getGoodsDetail();

        List<PayGoodsDetailEntity> details = new LinkedList<>();
        for (GoodsDetail detail : goodsDetails) {
            validate(detail);
            PayGoodsDetailEntity detailEntity = EntityBuilder.goodsDetail(session.nextId(), historyId, detail);
            detailEntity.setPartitionKey(partitionKey);
            details.add(detailEntity);
        }

        TransactionSummaryEntity history = EntityBuilder.payHistory(account, historyId, data, properties, session);
        ChannelResponse responseData = acquirer.pay(data, history);

        if (responseData.getSuccess()) {
            EntityBuilder.nextTxnNo(account, history);

            history.setCheckCode(checkCode);
            history.setPayDueTime(LocalDateTime.now().plus(PAY_DUE_TIME));
            history.setRequestId(responseData.getRequestId());
            history.setDescText(responseData.getNotification());

            BatchCycleTriggerEntity trigger = EntityBuilder.payTrigger(session.nextId(), history);
            trigger.setRequestId(session.getRequestId());
            trigger.setNotifyUrl(data.getNotifyUrl());

            session.pushEntity(history, trigger);
            session.pushEntity(details);
            session.commit();
        }

        return responseData;
    }


//    @Transactional(propagation = Propagation.NOT_SUPPORTED)
//    public JsonNode pay(PayRequestData data) {
//
//
//
//        TransactionSession session = payHistory(data);
//
//        TransactionSummaryEntity payHistoryEntity = session.getProperty(TransactionSummaryEntity.class, TransactionSummaryEntity.class);
//        System.currentTimeMillis();
//        return acquirer.pay(data, payHistoryEntity);
//    }

    @Transactional(propagation = Propagation.REQUIRED)
    public ChannelResponse checkPay(CheckPayRequestData data) throws Exception {
        return acquirer.checkPay(data);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public ChannelResponse checkRefund(CheckRefundRequestData data) throws Exception {
        return acquirer.checkRefund(data);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public ChannelResponse close(CloseRequestData data) throws Exception {
        return acquirer.close(data);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public ChannelResponse refund(RefundRequestData data) throws Exception {
        return acquirer.refund(data);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void callback(CallbackRequestData data) throws Exception {
        acquirer.callback(data);
    }

}
