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.*;
import cn.quant.baa.pay.model.web.*;
import cn.quant.baa.pay.util.AssertUtils;
import cn.quant.spring.util.StringUtils;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer;
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.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
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 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(propagation = Propagation.REQUIRES_NEW)
    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();

        PayHistoryIds ids = new PayHistoryIds();
        ids.setInstitutionId(institutionId);
        ids.setProductId(productId);
        ids.setExternalOrderNo(outTradeNo);
        check(ids, 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);
        }

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

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

            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);
//
//        PayHistoryEntity payHistoryEntity = session.getProperty(PayHistoryEntity.class, PayHistoryEntity.class);
//        System.currentTimeMillis();
//        return acquirer.pay(data, payHistoryEntity);
//    }

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

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

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

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

}
