package io.anyway.galaxy.intercepter.support;

import com.alibaba.fastjson.JSON;
import io.anyway.galaxy.common.Constants;
import io.anyway.galaxy.common.TransactionStatusEnum;
import io.anyway.galaxy.context.TXContext;
import io.anyway.galaxy.context.support.ActionExecutePayload;
import io.anyway.galaxy.context.support.TXContextSupport;
import io.anyway.galaxy.domain.RetryCount;
import io.anyway.galaxy.domain.TransactionInfo;
import io.anyway.galaxy.intercepter.ActionIntercepter;
import io.anyway.galaxy.message.TransactionMessageService;
import io.anyway.galaxy.repository.TransactionIdGenerator;
import io.anyway.galaxy.repository.TransactionRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.sql.SQLException;

/**
 * Created by yangzz on 16/7/21.
 */
@Component
@Slf4j
public class ActionIntercepterSupport implements ActionIntercepter{

    @Autowired
    private TransactionRepository transactionRepository;

    @Autowired
    private TransactionMessageService transactionMessageService;

    @Value("${tx.default.msg.retry.times}")
    private int defaultMsgRetryTimes;

    @Value("${tx.default.cancel.retry.times}")
    private int defaultCancelRetryTimes;

    @Value("${tx.default.confirm.retry.times}")
    private int defaultConfirmRetryTimes;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public TXContext addAction(String serialNumber,ActionExecutePayload bean)throws Throwable{
        // 新增事务Begin状态
        TransactionInfo transactionInfo = new TransactionInfo();
        transactionInfo.setTxId(TransactionIdGenerator.next());
        transactionInfo.setParentId(Constants.TX_ROOT_ID); // 事务入口
        transactionInfo.setContext(JSON.toJSONString(bean)); // 当前调用上下文环境
        transactionInfo.setBusinessId(serialNumber); // 业务流水号
        transactionInfo.setBusinessType(bean.getBizType()); // 业务类型
        transactionInfo.setModuleId(bean.getModuleId());
        transactionInfo.setTxType(bean.getTxType().getCode()); // TC | TCC
        transactionInfo.setTxStatus(TransactionStatusEnum.BEGIN.getCode()); //begin状态
        transactionInfo.setRetriedCount(JSON.toJSONString(  // 设置重试次数
                new RetryCount(defaultMsgRetryTimes, defaultCancelRetryTimes, defaultConfirmRetryTimes)));
        createTransactionInfo(transactionInfo);
        // 设置事务上下文
        TXContextSupport ctx= new TXContextSupport();
        ctx.setParentId(transactionInfo.getParentId());
        ctx.setTxId(transactionInfo.getTxId());
        ctx.setTxType(transactionInfo.getTxType());
        ctx.setBusinessType(transactionInfo.getBusinessType());
        ctx.setSerialNumber(serialNumber);
        return ctx;
    }

    @Override
    public void tryAction(TXContext ctx) throws Throwable {
        TransactionInfo transactionInfo = new TransactionInfo();
        transactionInfo.setParentId(ctx.getParentId());
        transactionInfo.setTxId(ctx.getTxId());
        transactionInfo.setTxStatus(TransactionStatusEnum.TRIED.getCode());
        transactionRepository.update(transactionInfo);
    }

    @Override
    public void confirmAction(TXContext ctx) throws Throwable {
        try {
            transactionMessageService.sendMessage(ctx, TransactionStatusEnum.CONFIRMING);
        } catch (Throwable t) {
            log.warn("Send confirm message failed, waiting job retry. TXContext=" + ctx, t);
        }
    }

    @Override
    public void cancelAction(TXContext ctx) throws Throwable {
        try {
            transactionMessageService.sendMessage(ctx, TransactionStatusEnum.CANCELLING);
        } catch (Throwable t) {
            log.warn("Send cancel message failed, waiting job retry. TXContext=" + ctx, t);
        }
    }

    private void createTransactionInfo(TransactionInfo transactionInfo) throws Throwable{
        int i = 2;
        while(i > 0) {
            try {
                transactionRepository.create(transactionInfo);
                break;
            } catch (SQLException e) {
                if (e.getSQLState().equals(Constants.KEY_23505)) {
                    log.warn("Create root transactionInfo record failed and retry:", e);
                    transactionInfo.setTxId(TransactionIdGenerator.next());
                    transactionRepository.create(transactionInfo);
                } else {
                    throw e;
                }
            }
            i--;
        }
    }
}