package track.task;

import common.model.CalculationFlow;
import common.model.Contract;
import common.model.TkioFlow;
import common.model.TkioFlowHistory;
import common.repository.*;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import tkio.model.Account;
import tkio.repository.AccountRepository;
import tkio.repository.AppRepository;
import tkio.service.AccountFlowRestrictService;
import util.Constant;
import util.DateUtil;
import util.StringUtil;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author liyin
 * @description
 * @date
 */
public class TrackingFlowTask {

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

    @Autowired
    private ContractRepository contractRepository;
    @Autowired
    private AccountRepository accountRepository;
    @Autowired
    private AppRepository appRepository;
    @Autowired
    private AccountFlowRestrictService accountFlowRestrictService;
    @Autowired
    private TkioFlowRepository tkioFlowRepository;
    @Autowired
    private ContractChangeRepository contractChangeRepository;
    @Autowired
    private CalculationFlowRepository calculationFlowRepository;
    @Autowired
    private TkioFlowHistoryRepository tkioFlowHistoryRepository;

    private final ExecutorService executor = Executors.newFixedThreadPool(10);


    public void syncFlow() {

        List<CalculationFlow> calculationFlows = calculationFlowRepository.findByStatus(0);
        for (CalculationFlow calculationFlow : calculationFlows) {
            //List<TkioFlow> tkioFlowList = new ArrayList<>();
            calculationFlow.setStatus(1);
            calculationFlowRepository.save(calculationFlow);
            tkioFlowRepository.deleteByEmail(calculationFlow.getEmail());
            tkioFlowHistoryRepository.deleteByEmail(calculationFlow.getEmail());
            String email = calculationFlow.getEmail();
            //查询用户下所有appkey
            //每个用户数据在单独线程处理
            executor.execute(new RunableTask(email, calculationFlow));
        }
    }

    private class RunableTask implements Runnable {
        private String email;
        private CalculationFlow calculationFlow;

        public RunableTask(String email, CalculationFlow calculationFlow) {
            this.email = email;
            this.calculationFlow = calculationFlow;
        }

        @Override
        public void run() {
            calculationFlowByAccount(calculationFlow, email);
        }

    }

    private void calculationFlowByAccount(CalculationFlow calculationFlow, String email) {
        try {
            Account account = accountRepository.findByEmail(email);
            List<Account> accountList = accountRepository.findByRootParent(account.getRootParent());
            List<Long> idList = new ArrayList<>();
            for (Account ac : accountList) {
                idList.add(ac.getId());
            }
            List<String> appkeys = appRepository.findAppkeysNotDebug(idList);
            String appkeyStr = String.join("','", appkeys);
            appkeyStr = "'" + appkeyStr + "'";

            String ago = DateUtil.format(account.getCreateTime(), DateUtil.C_DATE_PATTON_DEFAULT);//查找最早一天的流量
            if (ago == null) {
                /*calculationFlow.setStatus(2);
                calculationFlowRepository.save(calculationFlow);*/
                calculationFlowRepository.updateStatus(calculationFlow.getId(), 2);
                return;
            }

            int between = 0;
            try {
                between = DateUtil.daysBetween(ago, DateUtil.getBeforeDays(1)) + 1;
            } catch (ParseException e) {
                logger.error("强转错误：", e);
            }
            int startInt = 0;
            if (Integer.valueOf(DateUtil.getHH()) > 10) {
                startInt = -1;
            }
            for (int ii = between; ii > startInt; ii--) {
                String yesterday = DateUtil.getBeforeDays(ii);//昨日
                BigInteger clickNum = accountFlowRestrictService.getTotalNum(yesterday, yesterday, appkeyStr, "account_track_flow_restrict", "click_sum");
                if (clickNum != null && clickNum.longValue() > 0) {
                    List<Contract> contracts = contractRepository.findByPlatformAndEmail("tkio", email);
                    if (contracts.size() == 1) {//只有一个合同
                        TkioFlow tkioFlow = getTkioFlow(clickNum, yesterday, contracts.get(0));
                        if (tkioFlow != null) {
                            //tkioFlowList.add(tkioFlow);
                            tkioFlow.setContractCount(1);
                            tkioFlowRepository.save(tkioFlow);
                        }
                    } else {

                        getFlowOnMonths(email, yesterday, clickNum, contracts);//月流量报表逻辑

                        //多个合同
                        //看昨日被哪几个合同包含了
                        //昨日有几个合同开始生效
                        List<Contract> correlationContract = new ArrayList<>();
                        for (Contract contract : contracts) {
                            /*if (ContractStatusEnum.CANCEL.getKey().equals(contract.getStatus()) || ContractStatusEnum.SUSPEND.getKey().equals(contract.getStatus())) {
                                //中止或作废合同处理结束时间，以方便昨日流量的归属计算
                                ContractChange contractChange = contractChangeRepository.findByContentCode(ContractStatusEnum.CANCEL.getValue(), contract.getContractCode());
                                if (contractChange != null) {
                                    contract.setEndDate(new DateTime(contractChange.getCreateTime()).toString("yyyy-MM-dd"));
                                }
                            }*/
                            String startDate = contract.getStartDate();
                            String endDate = contract.getEndDate();
                            if (DateUtil.getDate(yesterday).getTime() >= DateUtil.getDate(startDate).getTime() && DateUtil.getDate(yesterday).getTime() <= DateUtil.getDate(endDate).getTime()) {
                                correlationContract.add(contract);
                            }
                        }
                        //多个合同时，进行排序，如果第一个合同流量超出就要看第二个合同，以此往下（昨日多个合同生效）
                        if (correlationContract.size() > 1) {
                            //冒泡
                            //第一优先级：合同开始日期，第二优先级，合同编号大小
                            Contract[] contractsArray = new Contract[correlationContract.size()];
                            contractsArray = correlationContract.toArray(contractsArray);

                            contractsArray = orderByContract(contractsArray);

                            for (int i = 0; i < contractsArray.length; i++) {
                                TkioFlow tkioFlow = getTkioFlow(clickNum, yesterday, contractsArray[i]);
                                tkioFlow.setContractCount(contracts.size());
                                if (tkioFlow != null && i < contractsArray.length - 1 && tkioFlow.getCostFlow() != null && tkioFlow.getCostFlow() > 0L) {
                                    //处理成本流量，如果超出了，依次算在下一个合同上
                                    //1、超出的流量 记为下一合同的当日流量 2、该合同自身当日流量为 剩余流量 或者为0
                                    clickNum = BigInteger.valueOf(tkioFlow.getCostFlow());
                                    tkioFlow.setCostFlow(null);
                                    if (tkioFlow.getFlow() > 0) {
                                        //tkioFlowList.add(tkioFlow);
                                        tkioFlowRepository.save(tkioFlow);
                                    }
                                } else {
                                    if (tkioFlow != null) {
                                        //tkioFlowList.add(tkioFlow);
                                        tkioFlowRepository.save(tkioFlow);
                                    }
                                    break;
                                }

                            }
                        } else {
                            if (correlationContract.size() == 0) {//昨日不包含在所有合同中
                                //排序
                                Contract[] contractsArray = new Contract[contracts.size()];
                                contractsArray = contracts.toArray(contractsArray);
                                contractsArray = orderByContract(contractsArray);
                                //如果昨日日期在第一个合同之前，则归属到第一个合同的成本，其余区间都归属到前一个合同的成本上
                                for (int i = 0; i < contractsArray.length; i++) {
                                    if (DateUtil.getDate(yesterday).getTime() <= DateUtil.getDate(contractsArray[i].getStartDate()).getTime() || i == contractsArray.length - 1) {
                                        int j = 0;

                                        if (i != 0) {
                                            j = i - 1;
                                        }

                                        if (i == contractsArray.length - 1) {
                                            j = i;
                                        }

                                        TkioFlow tkioFlow = getTkioFlow(clickNum, yesterday, contractsArray[j]);
                                        tkioFlow.setContractCount(contracts.size());
                                        if (tkioFlow != null) {
                                            //tkioFlowList.add(tkioFlow);
                                            tkioFlowRepository.save(tkioFlow);
                                        }
                                        break;
                                    }
                                }
                            } else {
                                TkioFlow tkioFlow = getTkioFlow(clickNum, yesterday, correlationContract.get(0));
                                tkioFlow.setContractCount(contracts.size());
                                if (tkioFlow != null) {
                                    //tkioFlowList.add(tkioFlow);
                                    tkioFlowRepository.save(tkioFlow);
                                }
                            }

                        }
                    }
                }
            }
            /*if (tkioFlowList.size() > 0) {
                tkioFlowRepository.save(tkioFlowList);
            }*/
         /*   calculationFlow.setStatus(2);
            calculationFlowRepository.save(calculationFlow);*/
            calculationFlowRepository.updateStatus(calculationFlow.getId(), 2);
        } catch (Exception e) {
            logger.error("CalculationFlow:Id::" + calculationFlow.getId() + ":全流量同步失败", e);
            /*calculationFlow.setStatus(3);
            calculationFlowRepository.save(calculationFlow);*/
            calculationFlowRepository.updateStatus(calculationFlow.getId(), 3);
        }
    }

    /**
     * 按月统计流量
     *
     * @param email
     * @param yesterday
     * @param clickNum
     * @param contracts
     * @return
     */
    private BigInteger getFlowOnMonths(String email, String yesterday, BigInteger clickNum, List<Contract> contracts) {
        String code = tkioFlowHistoryRepository.findLastRecodeContract(email);
        List<Contract> sortedContract = sortContracts(contracts);
        BigInteger clickFlow = clickNum;
        boolean skip = true;
        boolean fistRecode = code == null ? true : false;
        for (int i = 0; i < sortedContract.size(); i++) {
            if (skip && code != null && !sortedContract.get(i).getContractCode().equals(code)) {
                continue;//从记录的最后一份合同开始处理
            } else {
                skip = false;
            }


            TkioFlow tkioFlow = getTkioFlow(clickFlow, yesterday, sortedContract.get(i), true);
            Long costFlow = tkioFlow.getCostFlow() == null ? 0L : tkioFlow.getCostFlow();
            TkioFlowHistory flowHistory = new TkioFlowHistory(tkioFlow);
            flowHistory.setFirstRecode(fistRecode ? 1 : 0);//标记合同的第一次流量记录
            fistRecode = false;
            flowHistory.setyMonth(DateTime.parse(yesterday).toString("yyyy-MM"));
            flowHistory.setyMonthNext(DateTime.parse(yesterday).plusMonths(1).toString("yyyy-MM"));
            if (costFlow <= 0) {
                //没有超出流量 关联流量信息 到当前合同
                tkioFlowHistoryRepository.save(flowHistory);
                break;
            }

            //流量超出,即 产生合同外流量的时：
            if (i + 1 < sortedContract.size()) {
                //1、 看下一合同是否生效
                Contract next = sortedContract.get(i + 1);
                if (DateTime.parse(next.getStartDate()).isBefore(DateTime.parse(yesterday))
                        || DateTime.parse(next.getStartDate()).isEqual(DateTime.parse(yesterday))) {

                    // 生效
                    if (DateTime.parse(next.getStartDate()).monthOfYear().get() != new DateTime(next.getCreateTime()).monthOfYear().get()
                            && new DateTime(next.getCreateTime()).dayOfMonth().get() > 10) {

                    } else {
                        //2、生效时 且非晚录合同  超出流量计入下一合同
                        clickFlow = new BigInteger(costFlow.toString());
                        fistRecode = true;
                        continue;
                    }

                }
                //3、未生效或晚录合同 则关联流量信息到当前合同
                if (DateTime.parse(yesterday).monthOfYear().get() == DateTime.parse(next.getStartDate()).monthOfYear().get()) {
                    //生效当月 超出的流量关联到 该合同
                    clickFlow = new BigInteger(costFlow.toString());
                    fistRecode = true;
                    continue;
                } else {
                    //生效当月之前 超出的流量计入上一合同
                    flowHistory.setNextCode(next.getContractCode());//标记用于那份合同的调整流量
                    tkioFlowHistoryRepository.save(flowHistory);
                }
                break;
            } else {
                //无下一份合同 则关联流量信息到当前合同内
                tkioFlowHistoryRepository.save(flowHistory);
            }
        }
        return clickFlow;
    }

    public void task() {
        List<String> emails = contractRepository.findDistinctEmailByPlatform("tkio");

        List<TkioFlow> tkioFlowList = new ArrayList<>();
        String yesterday = DateUtil.getBeforeDays(1);//昨日
        logger.info("昨日流量同步：" + yesterday);
        for (String email : emails) {

            //查询用户下所有appkey
            Account account = accountRepository.findByEmail(email);
            List<Account> accountList = accountRepository.findByRootParent(account.getRootParent());
            List<Long> idList = new ArrayList<>();
            for (Account ac : accountList) {
                idList.add(ac.getId());
            }
            List<String> appkeys = appRepository.findAppkeysNotDebug(idList);
            String appkeyStr = String.join("','", appkeys);
            appkeyStr = "'" + appkeyStr + "'";


            BigInteger clickNum = accountFlowRestrictService.getTotalNum(yesterday, yesterday, appkeyStr, "account_track_flow_restrict", "click_sum");
            if (clickNum != null && clickNum.longValue() > 0) {
                List<Contract> contracts = contractRepository.findByPlatformAndEmail("tkio", email);
                if (contracts.size() == 1) {//只有一个合同
                    TkioFlow tkioFlow = getTkioFlow(clickNum, yesterday, contracts.get(0));
                    if (tkioFlow != null) {
                        tkioFlowList.add(tkioFlow);
                        tkioFlow.setContractCount(1);
                        tkioFlowRepository.save(tkioFlow);
                    }
                } else {//多个合同

                    getFlowOnMonths(email, yesterday, clickNum, contracts);//月流量报表逻辑

                    //看昨日被哪几个合同包含了
                    List<Contract> correlationContract = new ArrayList<>();
                    for (Contract contract : contracts) {
                        /*if (ContractStatusEnum.CANCEL.getKey().equals(contract.getStatus()) || ContractStatusEnum.SUSPEND.getKey().equals(contract.getStatus())) {
                            //中止或作废合同处理结束时间，以方便昨日流量的归属计算
                            ContractChange contractChange = contractChangeRepository.findByContentCode(ContractStatusEnum.CANCEL.getValue(), contract.getContractCode());
                            if(contractChange!=null){
                                contract.setEndDate(new DateTime(contractChange.getCreateTime()).toString("yyyy-MM-dd"));
                            }
                        }*/
                        String startDate = contract.getStartDate();
                        String endDate = contract.getEndDate();
                        if (DateUtil.getDate(yesterday).getTime() >= DateUtil.getDate(startDate).getTime() && DateUtil.getDate(yesterday).getTime() <= DateUtil.getDate(endDate).getTime()) {
                            correlationContract.add(contract);
                        }
                    }
                    //多个合同时，进行排序，如果第一个合同流量超出就要看第二个合同，以此往下
                    if (correlationContract.size() > 1) {
                        //冒泡
                        //第一优先级：合同开始日期，第二优先级，合同编号大小
                        Contract[] contractsArray = new Contract[correlationContract.size()];
                        contractsArray = correlationContract.toArray(contractsArray);

                        contractsArray = orderByContract(contractsArray);

                        for (int i = 0; i < contractsArray.length; i++) {
                            TkioFlow tkioFlow = getTkioFlow(clickNum, yesterday, contractsArray[i]);
                            if (tkioFlow != null && i < contractsArray.length - 1 && tkioFlow.getCostFlow() != null && tkioFlow.getCostFlow() > 0L) {//处理成本流量，如果超出了，依次算在下一个合同上
                                clickNum = BigInteger.valueOf(tkioFlow.getCostFlow());
                                tkioFlow.setCostFlow(null);
                                if (tkioFlow.getFlow() > 0) {
                                    tkioFlowList.add(tkioFlow);
                                    tkioFlow.setContractCount(contracts.size());
                                    tkioFlowRepository.save(tkioFlow);
                                }
                            } else {
                                if (tkioFlow != null) {
                                    tkioFlowList.add(tkioFlow);
                                    tkioFlow.setContractCount(contracts.size());
                                    tkioFlowRepository.save(tkioFlow);
                                }
                                break;
                            }

                        }
                    } else {
                        if (correlationContract.size() == 0) {//昨日不包含在所有合同中
                            //排序
                            Contract[] contractsArray = new Contract[contracts.size()];
                            contractsArray = contracts.toArray(contractsArray);
                            contractsArray = orderByContract(contractsArray);
                            //如果昨日日期在第一个合同之前，则归属到第一个合同的成本，其余区间都归属到前一个合同的成本上
                            for (int i = 0; i < contractsArray.length; i++) {
                                if (DateUtil.getDate(yesterday).getTime() <= DateUtil.getDate(contractsArray[i].getStartDate()).getTime() || i == contractsArray.length - 1) {
                                    int j = 0;

                                    if (i != 0) {
                                        j = i - 1;
                                    }

                                    if (i == contractsArray.length - 1) {
                                        j = i;
                                    }

                                    TkioFlow tkioFlow = getTkioFlow(clickNum, yesterday, contractsArray[j]);
                                    if (tkioFlow != null) {
                                        tkioFlowList.add(tkioFlow);
                                        tkioFlow.setContractCount(contracts.size());
                                        tkioFlowRepository.save(tkioFlow);
                                    }
                                    break;
                                }
                            }


                        } else {
                            TkioFlow tkioFlow = getTkioFlow(clickNum, yesterday, correlationContract.get(0));
                            if (tkioFlow != null) {
                                tkioFlowList.add(tkioFlow);
                                tkioFlow.setContractCount(contracts.size());
                                tkioFlowRepository.save(tkioFlow);
                            }
                        }

                    }
                }
            }


        }
        if (tkioFlowList.size() > 0) {
            //tkioFlowRepository.save(tkioFlowList);
        }

    }

    //排序
    public Contract[] orderByContract(Contract[] contractsArray) {
        for (int i = 0; i < contractsArray.length - 1; i++) {
            for (int j = 0; j < contractsArray.length - 1 - i; j++) {
                String startDate1 = contractsArray[j].getSignedDate() == null ? contractsArray[j].getStartDate() : contractsArray[j].getSignedDate();
                String startDate2 = contractsArray[j + 1].getSignedDate() == null ? contractsArray[j + 1].getStartDate() : contractsArray[j + 1].getSignedDate();
                if (DateUtil.getDate(startDate1).getTime() > DateUtil.getDate(startDate2).getTime()) {
                    Contract temp = contractsArray[j];
                    contractsArray[j] = contractsArray[j + 1];
                    contractsArray[j + 1] = temp;
                } else if (DateUtil.getDate(startDate1).getTime() == DateUtil.getDate(startDate2).getTime()) {

                    String contractCode = StringUtil.matchNumber(contractsArray[j].getContractCode());
                    String contractCodeMin = StringUtil.matchNumber(contractsArray[j + 1].getContractCode());
                    if (Long.valueOf(contractCode) > Long.valueOf(contractCodeMin)) {
                        Contract temp = contractsArray[j];
                        contractsArray[j] = contractsArray[j + 1];
                        contractsArray[j + 1] = temp;
                    }
                }
            }
        }
        return contractsArray;
    }


    public TkioFlow getTkioFlow(BigInteger clickNum, String yesterday, Contract contract) {
        return getTkioFlow(clickNum, yesterday, contract, false);
    }

    private TkioFlow getTkioFlow(BigInteger clickNum, String yesterday, Contract contract, Boolean isHistory) {
        TkioFlow tkioFlow = new TkioFlow();
        tkioFlow.setDs(yesterday);
        tkioFlow.setEmail(contract.getEmail());
        tkioFlow.setContractCode(contract.getContractCode());
        try {
            if (DateUtil.daysBetween(contract.getStartDate(), yesterday) < 0 || DateUtil.daysBetween(contract.getEndDate(), yesterday) > 0) {//昨日日期早于合同开始日期
                //设置为成本流量
                tkioFlow.setCostFlow(clickNum.longValue());
            } else {
                //查看历史总消耗流量是否超出
                BigDecimal totalFlow = isHistory ? tkioFlowHistoryRepository.sumAllFowByEmailAndCode(contract.getEmail(), contract.getContractCode())
                        : tkioFlowRepository.sumFlowByEmailAndContractCode(contract.getEmail(), contract.getContractCode());
                totalFlow = totalFlow == null ? new BigDecimal(0) : totalFlow;
                Double contractTrackFlow = contract.getTrackFlow() * 10000;
                if (contractTrackFlow.longValue() - totalFlow.longValue() - clickNum.longValue() >= 0 || contract.getPriceLevel() == Constant.tkioPriceLevelNotLimit) {
                    tkioFlow.setFlow(clickNum.longValue());
                } else {
                    tkioFlow.setFlow(contractTrackFlow.longValue() - totalFlow.longValue());
                    tkioFlow.setCostFlow(clickNum.longValue() - tkioFlow.getFlow());
                }
            }
            return tkioFlow;
        } catch (ParseException e) {
            logger.error("合同编号-" + contract.getContractCode() + "-同步昨日流量错误：", e);
        }
        return null;
    }

    public List<Contract> sortContracts(List<Contract> contracts) {
        List<Contract> sortedContract = contracts.stream()
                .sorted(Comparator.comparing(v -> {
                    String start = ((Contract) v).getStartDate();
                    return DateTime.parse(start);
                }).thenComparingLong(v -> {
                    String code = ((Contract) v).getContractCode();
                    return Long.parseLong(StringUtil.matchNumber(code));
                }))
                .collect(Collectors.toList());
        return sortedContract;
    }

    public static void main(String[] args) throws ParseException {
        String ago = "2020-10-01";
        int between = DateUtil.daysBetween(ago, DateUtil.getBeforeDays(1)) + 1;
        System.out.println(between);
        for (int i = between; i > 0; i--) {
            //System.out.println(DateUtil.getBeforeDays(i));
        }

        List<Contract> contracts = new ArrayList<>();

        Contract c1 = new Contract();
        c1.setStartDate("2020-10-02");
        c1.setContractCode("QWWE-20201101-03");
        Contract c2 = new Contract();
        c2.setStartDate("2020-11-02");
        c2.setContractCode("QWWE-20201101-01");

        Contract c3 = new Contract();
        c3.setStartDate("2020-10-02");
        c3.setContractCode("QWWE-20201101-02");

        Contract c4 = new Contract();
        c4.setStartDate("2020-11-03");
        c4.setContractCode("QWWE-20201101-02");
        contracts.add(c1);
        contracts.add(c2);
        contracts.add(c3);
        contracts.add(c4);


        List<Contract> sortedContract = contracts.stream()
                .sorted(Comparator.comparing(v -> {
                    String start = ((Contract) v).getStartDate();
                    return DateTime.parse(start);
                }).thenComparingLong(v -> {
                    String code = ((Contract) v).getContractCode();
                    return Long.parseLong(StringUtil.matchNumber(code));
                }))
                .collect(Collectors.toList());

        sortedContract.forEach(v -> System.out.println(v.getStartDate() + "-" + v.getContractCode()));

        Map<String, List<Contract>> monthContract = sortedContract.stream().collect(Collectors.groupingBy(v -> v.getStartDate().substring(0, 7)));

        monthContract.forEach((k, v) -> {
            System.out.println("key:  " + k);
            v.forEach(vn -> System.out.println(vn.getContractCode()));
        });

    }
}
