package com.ningdatech.pmapi.projectlib.manage;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.basic.util.StrPool;
import com.ningdatech.pmapi.common.constant.CommonConst;
import com.ningdatech.pmapi.common.enumeration.CommonEnum;
import com.ningdatech.pmapi.common.helper.UserInfoHelper;
import com.ningdatech.pmapi.common.model.entity.ExcelExportWriter;
import com.ningdatech.pmapi.common.statemachine.util.StateMachineUtils;
import com.ningdatech.pmapi.common.util.ExcelDownUtil;
import com.ningdatech.pmapi.datascope.model.DataScopeDTO;
import com.ningdatech.pmapi.datascope.utils.DataScopeUtil;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectLibFlagEnum;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectRenewalApprovalStatusEnum;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectStatusEnum;
import com.ningdatech.pmapi.projectlib.helper.ProjectHelper;
import com.ningdatech.pmapi.projectlib.model.dto.AnnualLibExportDTO;
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.entity.ProjectRenewalFundDeclaration;
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.IProjectApplicationService;
import com.ningdatech.pmapi.projectlib.service.IProjectRenewalFundDeclarationService;
import com.ningdatech.pmapi.projectlib.service.IProjectService;
import com.ningdatech.pmapi.todocenter.constant.TodoCenterConstant;
import com.ningdatech.pmapi.user.security.auth.model.UserFullInfoDTO;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
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.*;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import static com.ningdatech.pmapi.projectlib.enumeration.ProjectStatusEnum.*;

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

    private final IProjectService projectService;

    private final IProjectApplicationService applicationService;
    private final StateMachineUtils stateMachine;
    private final UserInfoHelper userInfoHelper;
    private final IProjectRenewalFundDeclarationService projectRenewalFundDeclarationService;

    /**
     * 年度计划查询状态
     */
    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.eq(Project::getNewest, Boolean.TRUE);
        query.orderByDesc(Project::getAnnualPlanAddTime);
        query.in(Project::getStatus, CollUtils.fieldList(ANNUAL_PLAN_LIST_STATUS, ProjectStatusEnum::getCode));
        // 数据权限
        buildProjectLibPermission(query);
        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.setApprovalAmount(w.getApprovalAmount());
            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.pass(project);
            } catch (Exception e) {
                throw new BizException("开启方案申报失败");
            }
        });
        projectService.updateBatchById(projects);
    }

    @Transactional(rollbackFor = Exception.class)
    public void projectApproved(ProjectApprovedReq req) {
        Project oldProject = projectService.getById(req.getProjectId());
        Project project = BeanUtil.copyProperties(oldProject, Project.class);
        // 被撤回重新申报的项目,项目ID要置空
        project.setCreateOn(LocalDateTime.now());
        project.setUpdateOn(LocalDateTime.now());
        stateMachine.pass(project);
        //计划出 计划验收时间
        LocalDateTime planAcceptanceTime = getPlanAcceptanceTime(req);
        project.setApprovalAmount(req.getApprovedAmount());
        project.setApprovedFile(String.valueOf(req.getApprovedFileId()));
        project.setBuildCycle(String.valueOf(req.getBuildCycle()));
        project.setOriginBuildCycle(req.getBuildCycle());
        project.setApprovedConstructionPlanFile(String.valueOf(req.getBuildPlanFileId()));
        project.setApprovalDate(req.getApprovedDate());
        project.setPlanAcceptanceTime(planAcceptanceTime);
        project.setOriginPlanAcceptanceTime(planAcceptanceTime);
        // 项目编号不变，版本号加1
        project.setProjectCode(oldProject.getProjectCode());
        project.setVersion(oldProject.getVersion() + 1);
        project.setInstCode(TodoCenterConstant.Declared.NULL_INST_CODE);
        project.setId(null);
        if(projectService.save(project)){
            applicationService.saveApplication(project,oldProject,Boolean.TRUE);
            // 将旧的项目版本置为不是最新
            projectService.update(Wrappers.lambdaUpdate(Project.class)
                    .set(Project::getNewest, Boolean.FALSE)
                    .ne(Project::getId, project.getId())
                    .eq(Project::getProjectCode, project.getProjectCode()));
        }
    }

    public LocalDateTime getPlanAcceptanceTime(ProjectApprovedReq req){
        if (Objects.nonNull(req.getApprovedDate()) && Objects.nonNull(req.getBuildCycle())) {
            return  req.getApprovedDate().plusMonths(req.getBuildCycle());
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public void suspendAnnualPlan(ProjectIdReq req) {
        Project project = projectService.getById(req.getProjectId());
        stateMachine.reject(project);
        projectService.updateById(project);
    }

    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);
        Integer isTemporaryAugment = param.getIsTemporaryAugment();
        if (Objects.isNull(isTemporaryAugment)) {
            throw new BizException("请传入是否临时增补标志!");
        }
        param.setIsTemporaryAugment(isTemporaryAugment);
        param.setStatusList(CollUtils.fieldList(ANNUAL_PLAN_LIST_STATUS, ProjectStatusEnum::getCode));
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(param);
        query.eq(Project::getNewest, Boolean.TRUE);
        query.orderByDesc(Project::getAnnualPlanAddTime);
        List<Project> projects = projectService.list(query);

        ExcelExportWriter excelExportWriter = new ExcelExportWriter();
        String fileName = null;
        if (CommonEnum.NO.getCode().equals(isTemporaryAugment)) {
            fileName = "年度计划库列表";
        } else if (CommonEnum.YES.getCode().equals(isTemporaryAugment)) {
            fileName = "年度计划增补库列表";
        }
        excelExportWriter.setFileName(fileName);
        List<String> sheetsNames = new ArrayList<>();
        sheetsNames.add(fileName);
        // 表体行数据集合
        List<List<String>> exportDatas = ExcelDownUtil.getProjectExportDatas(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);
    }

    /**
     * build 项目的角色权限到req
     *
     * @param query
     */
    public UserFullInfoDTO buildProjectLibPermission(LambdaQueryWrapper<Project> query) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        Optional<DataScopeDTO> currentUserDataScope = DataScopeUtil.getCurrentUserDataScope(user);
        if (!currentUserDataScope.isPresent()) {
            log.warn("没有取到权限信息 当前查询 没有权限条件");
            return user;
        }
        switch (currentUserDataScope.get().getRole()) {
            case NORMAL_MEMBER:
                // 普通用户 只能看到自己单位去申报的
                query.eq(Project::getBuildOrgCode, user.getEmpPosUnitCode());
                break;
            case COMPANY_MANAGER:
                // 单位管理员 看到自己单位去申报的 + 待预审的主管单位是自己单位的项目
                query.eq(Project::getBuildOrgCode, user.getEmpPosUnitCode());
                break;
            case SUPER_ADMIN:
                // 超级管理员 看到丽水全市的 并且也要判断他 同时是不是单位管理员
                break;
            case REGION_MANAGER:
                // 区域管理员 看到自己区域的项目
                query.eq(Project::getAreaCode, user.getRegionCode());
                break;
            default:
                // 没有权限的话 就让它查不到
                query.eq(Project::getId, "NULL");
                break;
        }
        return user;
    }

    @Transactional(rollbackFor = Exception.class)
    public void importAnnualPlan(ProjectLibFlagEnum importFlag, MultipartFile file) {
        Long userId = LoginUserUtil.getUserId();
        ImportParams params = new ImportParams();
        // 标题行数
        params.setTitleRows(2);
        // 从第几行开始，因为第一、二个大标题被上面的参数给占了，所以不是5
        params.setHeadRows(3);
        // 表格数量
        params.setSheetNum(2);
        List<AnnualLibImportDTO> list = null;
        try {
            list = ExcelImportUtil.importExcel(file.getInputStream(), AnnualLibImportDTO.class, params);
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
        // 筛选出导入的新建项目
        List<AnnualLibImportDTO> newList =
            list.stream().filter(d -> CommonConst.NEW_CONSTRUCTION.equals(d.getIsFirst())).collect(Collectors.toList());
        List<Project> projectList = newList.stream().map(n -> {
            Project project = new Project();
            assemblyProjectInfo(n, project);
            // 根据传入标志判断是否临时增补
            if (ProjectLibFlagEnum.ANNUAL_PLAN.equals(importFlag)) {
                project.setIsTemporaryAugment(CommonEnum.NO.getCode());
            } else if (ProjectLibFlagEnum.ANNUAL_PLAN_SUPPLEMENT.equals(importFlag)) {
                project.setIsTemporaryAugment(CommonEnum.YES.getCode());
            }
            project.setCreateBy(userId);
            project.setUpdateBy(userId);
            project.setCreateOn(LocalDateTime.now());
            project.setUpdateOn(LocalDateTime.now());
            return project;
        }).collect(Collectors.toList());
        // 保存到项目库中
        projectService.saveOrUpdateBatch(projectList);

        // 筛选出导入的续建项目
        List<AnnualLibImportDTO> continuedList = list.stream()
            .filter(d -> CommonConst.CONTINUED_CONSTRUCTION.equals(d.getIsFirst())).collect(Collectors.toList());
        List<ProjectRenewalFundDeclaration> renewalFundDeclarationList = continuedList.stream().map(c -> {
            ProjectRenewalFundDeclaration renewalFundDeclaration = new ProjectRenewalFundDeclaration();
            BeanUtils.copyProperties(c, renewalFundDeclaration);
            renewalFundDeclaration.setAnnualPaymentAmount(c.getAnnualPlanAmount());
            renewalFundDeclaration.setOtherAmount(c.getDeclareOtherAmount());
            renewalFundDeclaration.setApprovalStatus(ProjectRenewalApprovalStatusEnum.PENDING.name());
            renewalFundDeclaration.setCreateOn(LocalDateTime.now());
            renewalFundDeclaration.setUpdateOn(LocalDateTime.now());
            return renewalFundDeclaration;
        }).collect(Collectors.toList());
        // 保存到续建项目资金库中
        projectRenewalFundDeclarationService.saveBatch(renewalFundDeclarationList);
    }

    private void assemblyProjectInfo(AnnualLibImportDTO data, Project project) {
        Long projectId = data.getProjectId();
        // 从项目库中先查询出对应的项目ID的项目
        Project projectInfo = projectService.getById(projectId);
        if (Objects.nonNull(projectInfo)){
            BeanUtils.copyProperties(projectInfo,project);
        }else {
            // 新增的年度计划库项目
            throw new BizException("项目库中不存在项目ID为：" + projectId + "的项目");
        }
        project.setId(data.getProjectId());
        project.setProjectName(data.getProjectName());
        project.setProjectIntroduction(data.getProjectIntroduction());
        // 建设依据忽略
        project.setIsFirst(CommonConst.NEW_CONSTRUCTION.equals(data.getIsFirst()) ? 1 : 0);
        String[] dataArr = data.getBuildCycle().split(CommonConst.ZHI);
        if (CollectionUtils.isEmpty(Arrays.asList(dataArr))) {
            throw new BizException("项目ID为:" + data.getProjectId() + "的建设起止年限格式不正确,请按照xx年xx月至xx年xx月的格式输入!");
        }
        project.setBeginTime(dataArr[0].trim());
        project.setEndTime(dataArr[1].trim());
        project.setDeclareAmount(data.getDeclaredAmount());
        project.setAnnualPlanAmount(data.getAnnualPlanAmount());
        project.setDeclareHaveAmount(data.getDeclareHaveAmount());
        project.setDeclareGovOwnFinanceAmount(data.getDeclareGovOwnFinanceAmount());
        project.setDeclareGovSuperiorFinanceAmount(data.getDeclareGovSuperiorFinanceAmount());
        project.setDeclareBankLendingAmount(data.getDeclareBankLendingAmount());
        project.setDeclareOtherAmount(data.getDeclareOtherAmount());
        project.setEngineeringSpeedOne(data.getFirstQuarter());
        project.setEngineeringSpeedTwo(data.getSecondQuarter());
        project.setEngineeringSpeedThree(data.getThirdQuarter());
        project.setEngineeringSpeedFour(data.getFourthQuarter());
        project.setBuildOrgName(data.getBuildUnitName());
        project.setContactName(data.getContactName());
        project.setResponsibleMan(data.getResponsibleMan());
        project.setProjectRemarks(data.getProjectRemarks());
    }

    public void exportModifyList(ProjectListReq param, HttpServletResponse response) {
        int year = LocalDateTime.now().getYear();
        Integer isTemporaryAugment = param.getIsTemporaryAugment();
        if (Objects.isNull(isTemporaryAugment)) {
            throw new BizException("请传入是否临时增补标志!");
        }
        param.setIsTemporaryAugment(isTemporaryAugment);
        String fileName = null;
        // 设置excel的文件名称和是否增补
        if (CommonEnum.NO.getCode().equals(isTemporaryAugment)) {
            fileName = "丽水市" + year + "年数字化项目年度计划库编辑表";
        } else if (CommonEnum.YES.getCode().equals(isTemporaryAugment)) {
            fileName = "丽水市" + year + "年数字化项目年度计划增补库编辑表";
        }
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(param);
        query.eq(Project::getNewest,Boolean.TRUE);
        query.eq(Project::getIsTemporaryAugment, isTemporaryAugment);
        query.orderByDesc(Project::getAnnualPlanAddTime);
        query.in(Project::getStatus, CollUtils.fieldList(ANNUAL_PLAN_LIST_STATUS, ProjectStatusEnum::getCode));
        // 数据权限
        buildProjectLibPermission(query);
        List<Project> projects = projectService.list(query);
        List<AnnualLibExportDTO> list = projects.stream().map(p -> {
            AnnualLibExportDTO dto = new AnnualLibExportDTO();
            BeanUtils.copyProperties(p, dto);
            String beginTime = p.getBeginTime();
            String begin = beginTime.replace(StrPool.DASH, CommonConst.YEAR);
            begin = begin + CommonConst.MONTH;
            String endTime = p.getEndTime();
            String end = endTime.replace(StrPool.DASH, CommonConst.YEAR);
            end = end + CommonConst.MONTH;
            String buildCycle = begin + CommonConst.ZHI + end;
            dto.setBuildCycle(buildCycle);
            dto.setProjectId(p.getId());
            dto.setDeclaredAmount(p.getDeclareAmount());
            dto.setBuildUnitName(p.getBuildOrgName());
            String buildBasis = p.getBuildBasis();
            List<JSONObject> fileArray = JSON.parseArray(buildBasis, JSONObject.class);
            List<String> nameList = CollUtils.fieldList(fileArray, w -> w.getString(CommonConst.TITLE)
                + StrPool.LEFT_BRACKET + w.getString(CommonConst.BASIS_FILE_NAME) + StrPool.RIGHT_BRACKET);
            String basis = nameList.stream().collect(Collectors.joining(StrPool.COMMA));
            dto.setBuildBasis(basis);
            if (CommonEnum.YES.getCode().equals(p.getIsFirst())) {
                dto.setIsFirst(CommonConst.NEW_CONSTRUCTION);
            } else if (CommonEnum.NO.getCode().equals(p.getIsFirst())) {
                dto.setIsFirst(CommonConst.CONTINUED_CONSTRUCTION);
            }
            dto.setFirstQuarter(p.getEngineeringSpeedOne());
            dto.setSecondQuarter(p.getEngineeringSpeedTwo());
            dto.setThirdQuarter(p.getEngineeringSpeedThree());
            dto.setFourthQuarter(p.getEngineeringSpeedFour());
            return dto;
        }).collect(Collectors.toList());

        int count = 0;
        for (AnnualLibExportDTO annualLibExportDTO : list) {
            count++;
            annualLibExportDTO.setSerialNumber(count);
        }

        // 获取本地目录的年度计划编辑表Excel模板
        File directory = new File("");
        String templateName = "丽水市" + year + "年数字化项目年度计划编辑表";
        String templatePath =
            directory.getAbsolutePath() + File.separator + "template" + File.separator + templateName + ".xls";
        TemplateExportParams temp = new TemplateExportParams(templatePath);
        temp.setSheetNum(new Integer[] {0, 1});
        temp.setSheetName(new String[] {"实施类（新建）", "实施类（续建）"});
        Map<String, Object> map = new HashMap<>(4);
        map.put("mapList", list);
        map.put("mapList1", Lists.newArrayList());
        Workbook workbook = ExcelExportUtil.exportExcel(temp, map);
        if (CollUtil.isEmpty(list)) {
            // 输出空模板
            // 获取本地目录的年度计划编辑表空Excel模板
            String emptyTemplate = "丽水市" + year + "年数字化项目年度计划编辑表(空)";
            try {
                InputStream templateInputStream =
                        this.getClass().getClassLoader().getResourceAsStream("template" + File.separator + emptyTemplate + ".xls");

                OutputStream outputStream = response.getOutputStream();
                // 设置要下载的文件的名称
                response.setHeader("Content-disposition", "attachment;fileName=" + new String(templateName.getBytes("UTF-8"),"ISO8859-1"));
                // 设置文件的MIME类型
                response.setContentType("application/vnd.ms-excel;charset=UTF-8");

                byte[] b = new byte[2048];
                int len;
                while ((len = templateInputStream.read(b)) != -1) {
                    outputStream.write(b, 0, len);
                }
                templateInputStream.close();
                outputStream.flush();
                outputStream.close();
            } catch (IOException e) {
                throw new BizException("读取模板失败!");
            }
            return;
        }
        if (workbook == null){
            throw new BizException("读取编辑表模板失败!");
        }
        // 重置响应对象
        response.reset();
        try {
            response.setHeader("Content-disposition",
                "attachment;filename*=utf-8''" + URLEncoder.encode(Objects.requireNonNull(fileName), "UTF-8") + ".xls");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        response.setContentType(ExcelUtil.XLS_CONTENT_TYPE);
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        // 写出数据输出流到页面
        try {
            OutputStream output = response.getOutputStream();
            BufferedOutputStream bufferedOutPut = new BufferedOutputStream(output);
            workbook.write(bufferedOutPut);
            bufferedOutPut.flush();
            bufferedOutPut.close();
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
