package cn.palmte.work.service;

import cn.palmte.work.bean.SettleBean;
import cn.palmte.work.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.jfunc.common.db.utils.Pagination;
import top.jfunc.common.utils.CollectionUtil;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Yuanping Zhang
 * @date 2021/11/10
 */
@Service
public class ProjectSummaryService {
    private static final Logger logger = LoggerFactory.getLogger(ProjectSummaryService.class);

    @Autowired
    private ProjectBudgetIncomeRepository projectBudgetIncomeRepository;

    @Autowired
    private ProjectBudgetCostRepository projectBudgetCostRepository;

    @Autowired
    private ProjectBudgetCostManageRepository projectBudgetCostManageRepository;

    @Autowired
    private ProjectBudgetPlanDetailRepository projectBudgetPlanDetailRepository;

    @Autowired
    private ProjectSettleIncomeRepository projectSettleIncomeRepository;

    @Autowired
    private ProjectSettleService projectSettleService;

    @Autowired
    private SysRoleRepository sysRoleRepository;

    @Autowired
    private Pagination pagination;

    public List<SettleBean> getList(ConcurrentHashMap<String, String> searchInfo, String time, Admin admin) {

        List<Project> projectList = null;
        //项目可见性，根据角色和人员id
        int roleId = admin.getRoleId();
        Integer adminId = admin.getId();
        String sql = "select proj.id, proj.name, proj.approve_status_settle from project_settle_cost psc left join project proj on psc.project_id = proj.id where psc.time = ? ";
        //自己创建的肯定能看见，配置的可以看见，系统管理员可以看见
        SysRole sysRole = sysRoleRepository.findSysRoleById(roleId);
        if(SysRole.ROLE_TYPE_SYSTEM != sysRole.getType()){
            sql = sql + "and (proj.creator_id=? OR proj.id in (SELECT pv1.project_id FROM project_visible pv1 WHERE pv1.type=1 AND pv1.tid=? UNION SELECT pv2.project_id FROM project_visible pv2 WHERE pv2.type=2 AND pv2.tid=?)) group by proj.id order by proj.id asc";
            projectList = pagination.find(sql, Project.class, time, adminId, roleId, adminId);
        } else {
            sql = sql + " group by proj.id order by proj.id asc";
            projectList = pagination.find(sql, Project.class, time);
        }
        List<Project> projects = new ArrayList<>();
        List<Integer> projectInt = new ArrayList<>();
        for (Project project : projectList) {
            if (project.getApproveStatusSettle() == 2) {
                projects.add(project);
                projectInt.add(project.getId());
                continue;
            }
            ProjectSettleIncome projectNewest = projectSettleIncomeRepository.findNewByProjectId(project.getId());
            if (!time.equals(projectNewest.getTime())) {
                projects.add(project);
                projectInt.add(project.getId());
            }
        }

        SettleBean budgetBean = new SettleBean();
        if (projectInt.size() == 0) {
            projectInt.add(0);
        }
        List<ProjectBudgetIncome> incomes = projectBudgetIncomeRepository.findAllByProjectIds(projectInt);
        BigDecimal taxTotal = new BigDecimal(0);
        if(CollectionUtil.isNotEmpty(incomes)){
            BigDecimal incomeDevice = incomes.stream().filter(d -> d.getType() == ProjectBudgetIncome.TYPE_DEVICE).map(ProjectBudgetIncome::getIncomeTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setIncomeDevice(incomeDevice);
            BigDecimal incomeDeviceInclude = incomes.stream().filter(d -> d.getType() == ProjectBudgetIncome.TYPE_DEVICE).map(ProjectBudgetIncome::getIncomeTaxInclude).reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal incomeEngineer  = incomes.stream().filter(d -> d.getType() == ProjectBudgetIncome.TYPE_ENGINEER).map(ProjectBudgetIncome::getIncomeTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setIncomeEngineer(incomeEngineer);
            BigDecimal incomeEngineerInclude = incomes.stream().filter(d -> d.getType() == ProjectBudgetIncome.TYPE_ENGINEER).map(ProjectBudgetIncome::getIncomeTaxInclude).reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal incomeService = incomes.stream().filter(d -> d.getType() == ProjectBudgetIncome.TYPE_SERVICE).map(ProjectBudgetIncome::getIncomeTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setIncomeService(incomeService);
            BigDecimal incomeServiceInclude = incomes.stream().filter(d -> d.getType() == ProjectBudgetIncome.TYPE_SERVICE).map(ProjectBudgetIncome::getIncomeTaxInclude).reduce(BigDecimal.ZERO, BigDecimal::add);

            taxTotal = taxTotal.subtract(budgetBean.getIncomeTotal()).add(incomeDeviceInclude).add(incomeEngineerInclude).add(incomeServiceInclude);
        }

        List<ProjectBudgetCost> costs = projectBudgetCostRepository.findAllByProjectIds(projectInt);
        if(CollectionUtil.isNotEmpty(costs)){
            BigDecimal costDevice = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_DEVICE).map(ProjectBudgetCost::getCostTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setCostPurchaseDevice(costDevice);
            BigDecimal costDeviceInclude = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_DEVICE).map(ProjectBudgetCost::getCostTaxInclude).reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal costBuild  = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_BUILDING).map(ProjectBudgetCost::getCostTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setCostPurchaseBuild(costBuild);
            BigDecimal costBuildInclude = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_BUILDING).map(ProjectBudgetCost::getCostTaxInclude).reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal costService = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_SERVICE).map(ProjectBudgetCost::getCostTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setCostPurchaseService(costService);
            BigDecimal costServiceInclude = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_SERVICE).map(ProjectBudgetCost::getCostTaxInclude).reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal costOther = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_OTHER).map(ProjectBudgetCost::getCostTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setCostPurchaseOther(costOther);
            BigDecimal costOtherInclude = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_OTHER).map(ProjectBudgetCost::getCostTaxInclude).reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal costProjectManage  = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_PROJECT_MANAGE).map(ProjectBudgetCost::getCostTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setCostProjectManage(costProjectManage );
//            BigDecimal costProjectManageInclude = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_PROJECT_MANAGE).map(ProjectBudgetCost::getCostTaxInclude).reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal costOtherOther = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_OTHER_OTHER).map(ProjectBudgetCost::getCostTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setCostOther(costOtherOther);
            BigDecimal costOtherOtherInclude = costs.stream().filter(d -> d.getType() == ProjectBudgetCost.TYPE_OTHER_OTHER).map(ProjectBudgetCost::getCostTaxInclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            taxTotal = taxTotal.add(budgetBean.getCostTotal()).subtract(costDeviceInclude).subtract(costBuildInclude).subtract(costServiceInclude).subtract(costOtherInclude).subtract(costProjectManage).subtract(costOtherOtherInclude);
        }

        List<ProjectBudgetCostManage> manages = projectBudgetCostManageRepository.findAllByProjectIds(projectInt);
        if(CollectionUtil.isNotEmpty(manages)){
            BigDecimal costManageExpropriation = manages.stream().filter(d -> d.getType() == ProjectBudgetCostManage.TYPE_EXPROPRIATION).map(ProjectBudgetCostManage::getCostTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setCostExpropriation(costManageExpropriation);

            BigDecimal costManageCompany  = manages.stream().filter(d -> d.getType() == ProjectBudgetCostManage.TYPE_COMPANY_MANAGE).map(ProjectBudgetCostManage::getCostTaxExclude).reduce(BigDecimal.ZERO, BigDecimal::add);
            budgetBean.setCostCompanyManage(costManageCompany);

            budgetBean.setCostIncomeTax(taxTotal);
        }

        budgetBean.setGrossProfit(budgetBean.getIncomeTotal().subtract(budgetBean.getCostTotal()).subtract(budgetBean.getCostExpropriation()));
        budgetBean.setContributionProfit(budgetBean.getGrossProfit().subtract(budgetBean.getCostCompanyManage()));
        budgetBean.setNetProfit(budgetBean.getContributionProfit().subtract(budgetBean.getCostIncomeTax()));

        List<ProjectBudgetPlanDetail> cashFlows = projectBudgetPlanDetailRepository.findAllByProjectIds(projectInt);
        BigDecimal saleIncome = cashFlows.stream().map(ProjectBudgetPlanDetail::getSaleIncome).reduce(BigDecimal.ZERO, BigDecimal::add);
        budgetBean.setSaleIncomeCash(saleIncome);

        BigDecimal earnestMoneyIncome = cashFlows.stream().map(ProjectBudgetPlanDetail::getEarnestMoneyIncome).reduce(BigDecimal.ZERO, BigDecimal::add);
        budgetBean.setEarnestMoneyIncome(earnestMoneyIncome);

        BigDecimal deviceCost = cashFlows.stream().map(ProjectBudgetPlanDetail::getDeviceCost).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal engineerCost = cashFlows.stream().map(ProjectBudgetPlanDetail::getEngineerCost).reduce(BigDecimal.ZERO, BigDecimal::add);
        budgetBean.setPurchaseCost(deviceCost.add(engineerCost));

        BigDecimal projectManageCost = cashFlows.stream().map(ProjectBudgetPlanDetail::getProjectManageCost).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal earnestMoneyCost = cashFlows.stream().map(ProjectBudgetPlanDetail::getEarnestMoneyCost).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal capitalInterest = cashFlows.stream().map(ProjectBudgetPlanDetail::getCapitalInterest).reduce(BigDecimal.ZERO, BigDecimal::add);
        budgetBean.setEarnestMoneyCost(projectManageCost.add(earnestMoneyCost).add(capitalInterest));

        BigDecimal underWritten = cashFlows.stream().map(ProjectBudgetPlanDetail::getUnderwrittenPlan).reduce(BigDecimal.ZERO, BigDecimal::add);
        budgetBean.setFinancingCapitalInflow(underWritten);

        BigDecimal repaymentPlan = cashFlows.stream().map(ProjectBudgetPlanDetail::getRepaymentPlan).reduce(BigDecimal.ZERO, BigDecimal::add);
        budgetBean.setFinancingCapitalOutflow(repaymentPlan);

        budgetBean.setNetCashFlow(saleIncome.add(earnestMoneyIncome).subtract(budgetBean.getEarnestMoneyCost()));
        budgetBean.setFinancingCapitalCashflow(underWritten.subtract(repaymentPlan));
        budgetBean.setNetIncreaseMonetaryFunds(budgetBean.getNetCashFlow().add(budgetBean.getFinancingCapitalCashflow()));

        setProfitMargin(budgetBean);
        budgetBean.setProjectName("预算金额(不含税)");
        List<SettleBean> list = new ArrayList<>();
        list.add(budgetBean);

        SettleBean totalSettle = projectSettleService.getMonthTotalSettle(projectInt, time);
        setProfitMargin(totalSettle);
        totalSettle.setProjectName("实际累计(不含税)");
        list.add(totalSettle);

        for (Project project : projects) {
            SettleBean monthSettle = projectSettleService.getMonthSettle(project, time);
            setProfitMargin(monthSettle);
            monthSettle.setProjectName(project.getName());
            list.add(monthSettle);
        }
        return list;
    }

    private void setProfitMargin(SettleBean monthSettle) {
        BigDecimal divide2 = monthSettle.getIncomeTotal();
        BigDecimal min = BigDecimal.valueOf(0.01);
        if (divide2.compareTo(min) < 0 && divide2.compareTo(BigDecimal.valueOf(-0.01)) > 0) {
            divide2 = BigDecimal.ONE;
        }
        monthSettle.setGrossProfitProfitMargin(monthSettle.getGrossProfit().multiply(new BigDecimal(100)).divide(divide2, 4, BigDecimal.ROUND_HALF_UP));
        monthSettle.setContributionProfitProfitMargin(monthSettle.getContributionProfit().multiply(new BigDecimal(100)).divide(divide2, 4, BigDecimal.ROUND_HALF_UP));
        monthSettle.setNetProfitProfitMargin(monthSettle.getNetProfit().multiply(new BigDecimal(100)).divide(divide2, 4, BigDecimal.ROUND_HALF_UP));
    }
}
