package cn.quant.andy.service;

import cn.quant.andy.aspect.ExplainPoint;
import cn.quant.andy.dict.BillStatusCode;
import cn.quant.andy.jpa.entity.BalanceBillingEntity;
import cn.quant.andy.jpa.entity.BalanceSummaryEntity;
import cn.quant.andy.jpa.entity.EntityConstant;
import cn.quant.andy.jpa.entity.InstitutionTransactionSummaryEntity;
import cn.quant.andy.jpa.mybatis.mapper.BalanceBillingMapper;
import cn.quant.andy.jpa.mybatis.mapper.BalanceSummaryMapper;
import cn.quant.andy.jpa.mybatis.mapper.InstitutionTransactionSummaryMapper;
import cn.quant.andy.util.BalanceUtils;
import cn.quant.andy.util.TransactionBuilder;
import cn.quant.spring.data.domain.Pagination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.time.LocalDate;
import java.util.List;

/**
 * Created by Administrator on 2021/7/21 0021.
 */
@Service
public class BalanceService extends AbstractService {

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

    @Autowired
    private InstitutionTransactionSummaryMapper institutionTransactionSummaryMapper;

    @Autowired
    private BalanceSummaryMapper balanceSummaryMapper;

    @Autowired
    private BalanceBillingMapper balanceBillingMapper;

    private void billingLeft(BalanceBillingEntity bill, InstitutionTransactionSummaryEntity entity) {

        Integer termNo = entity.getTermNo();
        String baseCode = entity.getBaseCode();
        String baseOrderNo = entity.getBaseOrderNo();
        BalanceSummaryEntity balance = balanceSummaryMapper.findOneByIds(termNo, baseCode, baseOrderNo);

        boolean newbalance = balance == null;
        if (newbalance) {
            balance = TransactionBuilder.createLeftTransactionBalance(BalanceUtils.nextSummaryNo(bill), entity);
        }

        List<InstitutionTransactionSummaryEntity> debits = institutionTransactionSummaryMapper.findDebitsByTermBase(baseCode, termNo
                , baseOrderNo, false);
        for (InstitutionTransactionSummaryEntity debit : debits) {
            balance.setRtTermNo(debit.getTermNo());
            balance.setRtBaseCode(debit.getBaseCode());
            balance.setDebitAmt(balance.getDebitAmt().add(debit.getPostAmt()));
            balance.setDebitCount(balance.getDebitCount() + 1);
            debit.setPostedFlag(true);
            institutionTransactionSummaryMapper.save(debit);
            BalanceUtils.billCode(balance, entity, debit);
            BalanceUtils.billing(bill, debit);
            BalanceUtils.refresh(balance);
        }

        entity.setPostedFlag(true);
        entity.setPostDate(LocalDate.now());
        institutionTransactionSummaryMapper.save(entity);

        if (newbalance) {
            balanceSummaryMapper.insert(balance);
        } else {
            balanceSummaryMapper.save(balance);
        }
        BalanceUtils.billing(bill, entity);
    }

    @ExplainPoint
    @Transactional(rollbackOn = Exception.class, value = Transactional.TxType.NOT_SUPPORTED)
    public int billing(String type, int termNo) {
        logger.info("Start billing by {}, {}", type, termNo);
        BalanceBillingEntity billing = balanceBillingMapper.findTypeTerm(type, termNo);
        billing.setBillDate(LocalDate.now());
        boolean loop = true;
        Pagination pagination = new Pagination(1, 10, false);
        try {
            while (loop) {
                loop = billing(billing, pagination, termNo) > 0;
            }

            loop = true;
            pagination = new Pagination(1, 10, false);
            while (loop) {
                loop = billingRight(billing, pagination, termNo) > 0;
            }
            BalanceUtils.refresh(billing, BillStatusCode.SUCC);
        } catch (Exception e) {
            BalanceUtils.refresh(billing, BillStatusCode.WARN);
            logger.error("[ERROR]"+e.getMessage());
            e.printStackTrace();
        } finally {
            balanceBillingMapper.save(billing);
        }
        return 0;
    }

    @ExplainPoint
    @Transactional(rollbackOn = Exception.class, value = Transactional.TxType.REQUIRES_NEW)
    public int billing(BalanceBillingEntity bill, Pagination pagination, int termNo) {
        try {
            List<InstitutionTransactionSummaryEntity> entities = institutionTransactionSummaryMapper.findAllByTerm(pagination, termNo
                    , EntityConstant.CREDIT_TXN_FLAG, false);
            if (entities.size() > 0) {
                for (InstitutionTransactionSummaryEntity entity : entities) {
                    try {
                        billingLeft(bill, entity);
                    } catch (Exception e) {
                        BalanceUtils.nextLfFailedBills(bill);
                        logger.error("Failed billing left - " + entity.toString());
                        e.printStackTrace();
                    }
                }
            }
            return entities.size();
        } finally {
            release();
        }
    }

    private void billingRight(BalanceBillingEntity bill, InstitutionTransactionSummaryEntity entity) {
        Integer termNo = entity.getTermNo();
        String baseCode = entity.getBaseCode();
        String baseOrderNo = entity.getBaseOrderNo();
        BalanceSummaryEntity balance = balanceSummaryMapper.findOneByIds(termNo, baseCode, baseOrderNo);
        boolean newbalance = balance == null;
        if (newbalance) {
            balance = TransactionBuilder.createRightTransactionBalance(BalanceUtils.nextSummaryNo(bill), entity);
            BalanceUtils.refresh(balance);
        }
        entity.setPostedFlag(true);
        entity.setPostDate(LocalDate.now());
        institutionTransactionSummaryMapper.save(entity);

        if (newbalance) {
            balanceSummaryMapper.insert(balance);
        } else {
            balanceSummaryMapper.save(balance);
        }

        BalanceUtils.billing(bill, entity);
    }

    @ExplainPoint
    @Transactional(rollbackOn = Exception.class, value = Transactional.TxType.REQUIRES_NEW)
    public int billingRight(BalanceBillingEntity bill, Pagination pagination, int termNo) {
        try {
            List<InstitutionTransactionSummaryEntity> entities = institutionTransactionSummaryMapper.findAllByTerm(pagination, termNo
                    , EntityConstant.DEBIT_TXN_FLAG, false);
            if (entities.size() > 0) {
                for (InstitutionTransactionSummaryEntity entity : entities) {
                    try {
                        billingRight(bill, entity);
                    } catch (Exception e) {
                        BalanceUtils.nextRtFailedBills(bill);
                        logger.error("Failed billing right - " + entity.toString());
                        e.printStackTrace();
                    }
                }
            }
            return entities.size();
        } finally {
            release();
        }
    }


}
