package cn.quant.baa.pay.service;

import cn.quant.baa.pay.acquirer.AcquirerPropertiesSource;
import cn.quant.baa.pay.acquirer.AcquirerProperties;
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.*;
import cn.quant.baa.pay.model.web.*;
import cn.quant.baa.pay.util.AssertUtils;
import com.fasterxml.jackson.databind.JsonNode;
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 java.util.LinkedList;
import java.util.List;

import static cn.quant.baa.pay.dict.MessageEnum.*;

/**
 * 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 AcquirerPropertiesSource acquirerPropertiesSource;

    @Autowired
    private MerchantAcquirer acquirer;

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void check(PayHistoryIds ids, TransactionSession session) {
        PayHistoryEntity historyEntity = session.findOne(PayHistoryEntity.class, ids);
        if (historyEntity != null) {
            AssertUtils.throwMessage(EXIST_ORDER, ids.getInstitutionId(), ids.getProductId(), ids.getExternalOrderNo());
        }
    }

    @Transactional
    public TransactionSession payHistory(PayRequestData data) {

        validate(data);

        String chanId = data.getChanId();
        AcquirerProperties profile = acquirerPropertiesSource.get(Long.valueOf(chanId));
        if (profile == null) {
            AssertUtils.throwMessage(ACQUIRER_NOSUCH, chanId);
        }

        String institutionId = profile.getInstitutionId();
        String productId = profile.getProductId();
        String buyerId = data.getBuyerId();

        TransactionSession session = TransactionSession.session();
        prepare(institutionId, productId, buyerId, session);


        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);
        }


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

        String outTradeNo = data.getOutTradeNo();
        PayHistoryIds ids = new PayHistoryIds();
        ids.setInstitutionId(institutionId);
        ids.setProductId(productId);
        ids.setExternalOrderNo(outTradeNo);
        check(ids, session);


        PayHistoryEntity history = EntityBuilder.history(account, ids, historyId, data, profile, session);
        session.pushEntity(history);
        session.pushEntity(details);

        session.commit();

        session.addProperty(PayHistoryEntity.class, history);
        session.addProperty(PayGoodsDetailEntity.class, details);

        return session;
    }


    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public JsonNode pay(PayRequestData data) {

        TransactionSession session = payHistory(data);

        PayHistoryEntity payHistoryEntity = session.getProperty(PayHistoryEntity.class, PayHistoryEntity.class);
        System.currentTimeMillis();
        return acquirer.pay(data, payHistoryEntity);
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public JsonNode checkPay(CheckPayRequestData data) {
        return acquirer.checkPay(data);
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public JsonNode checkRefund(CheckRefundRequestData data) {
        return acquirer.checkRefund(data);
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public JsonNode close(CloseRequestData data) {
        return acquirer.close(data);
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public JsonNode refund(RefundRequestData data) {
        return acquirer.refund(data);
    }

}
