package com.ningdatech.pmapi.projectlib.manage;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.basic.util.ValidUtil;
import com.ningdatech.pmapi.common.constant.CommonConst;
import com.ningdatech.pmapi.common.model.entity.ExcelExportWriter;
import com.ningdatech.pmapi.common.statemachine.event.ProjectStatusChangeEvent;
import com.ningdatech.pmapi.common.statemachine.util.StateMachineUtils;
import com.ningdatech.pmapi.common.util.ExcelDownUtil;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectStatusEnum;
import com.ningdatech.pmapi.projectlib.helper.ProjectHelper;
import com.ningdatech.pmapi.projectlib.model.dto.AnnualLibImportDTO;
import com.ningdatech.pmapi.projectlib.model.dto.ProjectDTO;
import com.ningdatech.pmapi.projectlib.model.entity.Project;
import com.ningdatech.pmapi.projectlib.model.req.ProjectApprovedReq;
import com.ningdatech.pmapi.projectlib.model.req.ProjectIdReq;
import com.ningdatech.pmapi.projectlib.model.req.ProjectListReq;
import com.ningdatech.pmapi.projectlib.model.req.StartProjectDeclareReq;
import com.ningdatech.pmapi.projectlib.model.vo.AnnualPlanListItemVO;
import com.ningdatech.pmapi.projectlib.service.IProjectService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static com.ningdatech.pmapi.projectlib.enumeration.ProjectStatusEnum.*;

/**
 * <p>
 * AnnualPlanLibManage
 * </p>
 *
 * @author WendyYang
 * @since 14:09 2023/2/11
 */
@Component
@AllArgsConstructor
public class AnnualPlanLibManage {

    private final IProjectService projectService;
    private final StateMachineUtils stateMachine;

    /**
     * 年度计划查询状态
     */
    private static final List<ProjectStatusEnum> ANNUAL_PLAN_LIST_STATUS = Arrays.asList(
            IN_THE_ANNUAL_PLAN,
            SCHEME_UNDER_REVIEW,
            SCHEME_REVIEW_FAILED,
            TO_BE_APPROVED,
            TO_BE_DECLARED,
            PLAN_TO_BE_DECLARED,
            PENDING_PREQUALIFICATION_CHOICE,
            PROJECT_APPROVED,
            TO_BE_PURCHASED,
            UNDER_CONSTRUCTION,
            TO_BE_FINALLY_INSPECTED,
            FINAL_ACCEPTANCE_IS_UNDER_REVIEW,
            FINAL_ACCEPTANCE_REVIEW_FAILED,
            ARCHIVED
    );


    public PageVo<AnnualPlanListItemVO> annulPlanLibList(ProjectListReq req) {
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        Integer isTemporaryAugment = req.getIsTemporaryAugment();
        if (Objects.isNull(isTemporaryAugment)){
            throw new BizException("请传入是否临时增补标志!");
        }
        query.eq(Project::getIsTemporaryAugment, isTemporaryAugment);
        query.orderByDesc(Project::getAnnualPlanAddTime);
        query.in(Project::getStatus, CollUtils.fieldList(ANNUAL_PLAN_LIST_STATUS, ProjectStatusEnum::getCode));
        Page<Project> page = projectService.page(req.page(), query);
        long total;
        if ((total = page.getTotal()) == 0) {
            return PageVo.empty();
        }
        PageVo<AnnualPlanListItemVO> result = PageVo.of(new ArrayList<>(), total);
        page.getRecords().forEach(w -> {
            AnnualPlanListItemVO item = new AnnualPlanListItemVO();
            item.setProjectId(w.getId());
            item.setProjectId(w.getId());
            item.setProjectName(w.getProjectName());
            item.setStage(w.getStage());
            item.setStatus(w.getStatus());
            item.setProjectYear(w.getProjectYear());
            item.setProjectType(w.getProjectType());
            item.setDeclaredAmount(w.getDeclareAmount());
            item.setBuildOrg(w.getBuildOrgName());
            item.setCreateOn(w.getCreateOn());
            item.setIsStartDeclaredProject(!IN_THE_ANNUAL_PLAN.eq(w.getStatus()));
            result.getRecords().add(item);
        });
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    public void startProjectDeclared(StartProjectDeclareReq req) {
        List<Project> projects = projectService.listByIds(req.getProjectIds());
        projects.forEach(w -> {
            if (!IN_THE_ANNUAL_PLAN.eq(w.getStatus())) {
                throw BizException.wrap("开启方案申报失败");
            }
        });
        projects.forEach(project -> {
            try {
                stateMachine.execute(project, ProjectStatusChangeEvent.ANNUAL_PLAN_PROJECT_OPEN_PLAN_DECLARE);
            } catch (Exception e) {
                throw new BizException("开启方案申报失败");
            }
        });
    }

    @Transactional(rollbackFor = Exception.class)
    public void projectApproved(ProjectApprovedReq req) throws Exception {
        Project project = projectService.getById(req.getProjectId());
        stateMachine.execute(project, ProjectStatusChangeEvent.PROJECT_APPROVAL);
        LambdaUpdateWrapper<Project> update = Wrappers.lambdaUpdate(Project.class)
                .set(Project::getApprovalAmount, req.getApprovedAmount())
                .set(Project::getApprovedFile, req.getApprovedFileId())
                .set(Project::getBuildCycle, req.getBuildCycle())
                .set(Project::getConstructionPlanFile, req.getBuildPlanFileId())
                .set(Project::getApprovalDate, req.getApprovedDate())
                .eq(Project::getId, req.getProjectId());
        projectService.update(update);
    }

    @Transactional(rollbackFor = Exception.class)
    public void suspendAnnualPlan(ProjectIdReq req) throws Exception {
        Project project = projectService.getById(req.getProjectId());
        stateMachine.execute(project, ProjectStatusChangeEvent.ANNUAL_PLAN_SUSPEND);
    }

    @Transactional(rollbackFor = Exception.class)
    public void importAnnualPlan(MultipartFile file) {
        try (InputStream inputStream = file.getInputStream()) {
            EasyExcel.read(inputStream, new AnalysisEventListener<AnnualLibImportDTO>() {

                private final List<Project> records = new ArrayList<>();

                @Override
                public void onException(Exception exception, AnalysisContext context) throws Exception {
                    super.onException(exception, context);
                    throw BizException.wrap("导入年度计划解析失败");
                }

                @Override
                public void invoke(AnnualLibImportDTO data, AnalysisContext context) {
                    ValidUtil.valid(data);
                    Project project = new Project();
                    project.setId(data.getProjectId());
                    project.setProjectName(data.getProjectName());
                    project.setProjectRemarks(data.getProjectRemarks());
                    project.setEngineeringSpeedOne(data.getEngineeringSpeedOne());
                    project.setEngineeringSpeedTwo(data.getEngineeringSpeedTwo());
                    project.setEngineeringSpeedThree(data.getEngineeringSpeedThree());
                    project.setEngineeringSpeedFour(data.getEngineeringSpeedFour());
                    project.setAnnualPlanAmount(data.getAnnualPlanAmount());
                    project.setDeclareOtherAmount(data.getDeclareOtherAmount());
                    project.setBuildBasis(data.getBuildBasis());
                    project.setBuildOrgName(data.getBuildUnitName());
                    project.setDeclareBankLendingAmount(data.getDeclareBankLendingAmount());
                    project.setDeclareGovOwnFinanceAmount(data.getDeclareGovOwnFinanceAmount());
                    project.setDeclareGovSuperiorFinanceAmount(data.getDeclareGovSuperiorFinanceAmount());
                    project.setDeclareHaveAmount(data.getDeclareHaveAmount());
                    project.setDeclareOtherAmount(data.getDeclareOtherAmount());
                    project.setContactName(data.getContactName());
                    project.setResponsibleMan(data.getResponsibleMan());
                    String[] dataArr = data.getBuildCycle().split("至");
                    project.setBeginTime(dataArr[0].trim());
                    project.setEndTime(dataArr[1].trim());
                    project.setProjectIntroduction(data.getProjectIntroduction());
                    project.setIsFirst("新建".equals(data.getIsFirst()) ? 1 : 0);
                    records.add(project);
                }

                @Override
                public void doAfterAllAnalysed(AnalysisContext context) {
                    if (records.isEmpty()) {
                        throw BizException.wrap("导入年度计划为空");
                    }
                    List<Long> projectIds = CollUtils.fieldList(records, Project::getId);
                    long count = projectService.count(Wrappers.lambdaQuery(Project.class)
                            .in(Project::getId, projectIds));
                    if (count != records.size()) {
                        throw BizException.wrap("请确保所有项目都存在");
                    }
                    projectService.updateBatchById(records);
                }
            }).sheet(0).doReadSync();
        } catch (IOException e) {
            throw BizException.wrap("导入年度计划失败");
        }
    }

    public void updateAnnualPlan(ProjectDTO req) {
        Project project = BeanUtil.copyProperties(req, Project.class);
        projectService.updateById(project);
    }

    public void exportList(ProjectListReq param, HttpServletResponse response) {
        param.setPageNumber(CommonConst.EXPORT_PAGE_NUMBER);
        param.setPageSize(CommonConst.EXPORT_PAGE_SIZE);
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(param);
        Integer isTemporaryAugment = param.getIsTemporaryAugment();
        if (Objects.isNull(isTemporaryAugment)){
            throw new BizException("请传入是否临时增补标志!");
        }
        query.eq(Project::getIsTemporaryAugment, isTemporaryAugment);
        query.eq(Project::getIsTemporaryAugment, 0);
        query.orderByDesc(Project::getAnnualPlanAddTime);
        query.in(Project::getStatus, CollUtils.fieldList(ANNUAL_PLAN_LIST_STATUS, ProjectStatusEnum::getCode));
        List<Project> projects = projectService.list(query);

        ExcelExportWriter excelExportWriter = new ExcelExportWriter();

        Integer tableFlag = param.getTableFlag();
        if (Objects.isNull(tableFlag)){
            throw new BizException("请传入要导出的表格类型!");
        }
        String fileName = null;
        if (tableFlag == 0){
            fileName = "年度计划库编辑表";
        } else if (tableFlag == 1) {
            fileName = "年度计划库列表";
        }
        excelExportWriter.setFileName(fileName);
        List<String> sheetsNames = new ArrayList<>();
        sheetsNames.add(fileName);
        // 表体行数据集合
        List<List<String>> exportDatas = ExcelDownUtil.getExportDatas(param.getExportOptionList(), projects);
        // sheet列表集合
        List<List<List<String>>> sheets = new ArrayList<>();
        sheets.add(exportDatas);
        excelExportWriter.setHeads(sheets);
        excelExportWriter.setDatas(exportDatas);
        excelExportWriter.setSheets(sheetsNames);
        excelExportWriter.setHeadPropertyClass(List.class);
        excelExportWriter.setHeadPropertyFlag(Boolean.FALSE);
        ExcelDownUtil.excelExportFile(response, excelExportWriter);
    }
}
