package com.ningdatech.pmapi.projectdeclared.manage;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
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.function.VUtils;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.basic.util.NdDateUtils;
import com.ningdatech.pmapi.common.constant.BizConst;
import com.ningdatech.pmapi.common.helper.UserInfoHelper;
import com.ningdatech.pmapi.common.statemachine.util.StateMachineUtils;
import com.ningdatech.pmapi.common.util.ExcelDownUtil;
import com.ningdatech.pmapi.common.util.ExcelExportStyle;
import com.ningdatech.pmapi.projectdeclared.model.dto.ContractSaveDTO;
import com.ningdatech.pmapi.projectdeclared.model.dto.DeclaredProjectExportDTO;
import com.ningdatech.pmapi.projectdeclared.model.dto.PreInsSaveDTO;
import com.ningdatech.pmapi.projectdeclared.model.entity.Contract;
import com.ningdatech.pmapi.projectdeclared.model.entity.PaymentPlan;
import com.ningdatech.pmapi.projectdeclared.model.entity.PreInsAcceptancePerson;
import com.ningdatech.pmapi.projectdeclared.model.entity.Purchase;
import com.ningdatech.pmapi.projectdeclared.model.vo.ContractVO;
import com.ningdatech.pmapi.projectdeclared.model.vo.PaymentPlanVO;
import com.ningdatech.pmapi.projectdeclared.model.vo.PreInsAcceptancePersonVO;
import com.ningdatech.pmapi.projectdeclared.model.vo.PreInsVO;
import com.ningdatech.pmapi.projectdeclared.service.IContractService;
import com.ningdatech.pmapi.projectdeclared.service.IPaymentPlanService;
import com.ningdatech.pmapi.projectdeclared.service.IPreInsAcceptancePersonService;
import com.ningdatech.pmapi.projectdeclared.service.IPurchaseService;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectStatusEnum;
import com.ningdatech.pmapi.projectlib.enumeration.ProjectTypeEnum;
import com.ningdatech.pmapi.projectlib.helper.ProjectHelper;
import com.ningdatech.pmapi.projectlib.model.entity.Project;
import com.ningdatech.pmapi.projectlib.model.req.ProjectListReq;
import com.ningdatech.pmapi.projectlib.model.vo.ProjectLibListItemVO;
import com.ningdatech.pmapi.projectlib.service.IProjectService;
import com.ningdatech.pmapi.user.security.auth.model.UserFullInfoDTO;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @Classname ContructionManage
 * @Description
 * @Date 2023/5/29 14:48
 * @Author PoffyZhang
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class ConstructionManage {

    private final IProjectService projectService;
    private final UserInfoHelper userInfoHelper;

    private final IPurchaseService purchaseService;

    private final IContractService contractService;

    private final IPaymentPlanService paymentPlanService;

    private final IPreInsAcceptancePersonService acceptancePersonService;

    private final StateMachineUtils stateMachineUtils;

    /**
     * 待采购的-项目列表
     *
     * @param req
     * @return
     */
    public PageVo<ProjectLibListItemVO> projectLibList(ProjectListReq req) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        VUtils.isTrue(Objects.isNull(user)).throwMessage("获取登录用户失败!");
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        //建设中状态
        query.eq(Project::getStatus, ProjectStatusEnum.UNDER_CONSTRUCTION.getCode());
        query.eq(Project::getStage, ProjectStatusEnum.PROJECT_APPROVED.getCode());
        query.eq(Project::getNewest, Boolean.TRUE);
        query.orderByAsc(Project::getTransactionTime);
        Page<Project> page = projectService.page(req.page(), query);
        long total;
        if ((total = page.getTotal()) == 0) {
            return PageVo.empty();
        }
        List<ProjectLibListItemVO> records = CollUtils.convert(page.getRecords(), w -> {
            ProjectLibListItemVO item = new ProjectLibListItemVO();
            item.setId(w.getId());
            item.setProjectName(w.getProjectName());
            item.setCreateOn(w.getCreateOn());
            item.setDeclaredAmount(w.getDeclareAmount());
            item.setStage(w.getStage());
            item.setStatus(w.getStatus());
            item.setProjectType(w.getProjectType());
            item.setProjectYear(w.getProjectYear());
            item.setBuildOrg(w.getBuildOrgName());
            item.setBizDomain(w.getBizDomain());
            item.setProcessStatus(w.getProcessStatus());
            item.setInstCode(w.getInstCode());
            item.setIsHigherSuperOrg(w.getIsHigherSuperOrg());
            item.setApprovedAmount(w.getApprovalAmount());
            item.setApprovalDate(w.getApprovalDate());
            item.setTransactionAmount(w.getTransactionAmount());
            item.setTransactionTime(w.getTransactionTime());
            return item;
        });
        return PageVo.of(records, total);
    }

    public void exportList(HttpServletResponse response, ProjectListReq param) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        VUtils.isTrue(Objects.isNull(user)).throwMessage("获取登录用户失败!");
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(param);
        //待采购状态
        query.eq(Project::getStatus, ProjectStatusEnum.UNDER_CONSTRUCTION.getCode());
        query.eq(Project::getStage, ProjectStatusEnum.PROJECT_APPROVED.getCode());
        query.eq(Project::getNewest, Boolean.TRUE);
        query.orderByAsc(Project::getTransactionTime);
        List<Project> records = projectService.list(query);

        AtomicInteger serialNumber = new AtomicInteger(0);
        List<DeclaredProjectExportDTO> collect = Lists.newArrayList();
        if (CollUtil.isNotEmpty(records)) {
            List<Long> projectIds = records.stream().map(Project::getId).collect(Collectors.toList());

            List<Purchase> purchases = purchaseService.list(Wrappers.lambdaQuery(Purchase.class)
                    .in(Purchase::getProjectId, projectIds));
            Map<Long, Purchase> purchaseMap = purchases.stream().collect(Collectors.toMap(Purchase::getProjectId, v -> v));

            collect = records.stream().map(r -> {
                DeclaredProjectExportDTO exportDTO = new DeclaredProjectExportDTO();
                BeanUtils.copyProperties(r, exportDTO);
                exportDTO.setProjectTypeName(ProjectTypeEnum.getDesc(r.getProjectType()));
                exportDTO.setStatusName(ProjectStatusEnum.getDesc(r.getStatus()));
                String createOnStr = NdDateUtils.format(r.getCreateOn(), "yyyy-MM-dd HH:mm");
                exportDTO.setCreateOn(createOnStr);
                exportDTO.setSerialNumber(serialNumber.incrementAndGet());
                exportDTO.setApprovedAmount(r.getApprovalAmount());
                exportDTO.setApprovalDate(r.getApprovalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                if (purchaseMap.containsKey(r.getId())) {
                    Purchase purchase = purchaseMap.get(r.getId());
                    exportDTO.setTransactionAmount(purchase.getTransactionAmount());
                    exportDTO.setTransactionTime(purchase.getTransactionTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                }
                return exportDTO;
            }).collect(Collectors.toList());
        }

        String fileName = "合同备案项目列表";
        ExcelDownUtil.setFileName(fileName, response);
        //数据导出处理函数
        try {
            EasyExcel.write(response.getOutputStream(), DeclaredProjectExportDTO.class)
                    .autoCloseStream(false)
                    .registerWriteHandler(ExcelExportStyle.formalStyle())
                    .sheet(fileName)
                    .doWrite(collect);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取合同备案详情
     *
     * @param projectId
     * @return
     */
    public ContractVO detailContractByProjectId(Long projectId) {
        Contract contract = contractService.getOne(Wrappers.lambdaQuery(Contract.class)
                .eq(Contract::getProjectId, projectId)
                .last(BizConst.LIMIT_1));

        ContractVO vo = BeanUtil.copyProperties(contract, ContractVO.class);

        List<PaymentPlan> payments = paymentPlanService.list(Wrappers.lambdaQuery(PaymentPlan.class)
                .eq(PaymentPlan::getProjectId, projectId)
                .orderByAsc(PaymentPlan::getPaymentTime));

        if (CollUtil.isNotEmpty(payments)) {
            vo.setPayments(convertPayments(payments, contract.getTotalAmount()));
        }

        return vo;
    }

    private List<PaymentPlanVO> convertPayments(List<PaymentPlan> payments, BigDecimal totalAmount) {
        if (CollUtil.isEmpty(payments)) {
            return Collections.emptyList();
        }
        return payments.stream().map(p -> {
                    PaymentPlanVO vo = BeanUtil.copyProperties(p, PaymentPlanVO.class);
                    vo.setRatio((Objects.isNull(totalAmount) || totalAmount.compareTo(BigDecimal.ZERO) == 0) ? "0%"
                            : p.getPaymentAmount().multiply(BigDecimal.valueOf(100))
                            .divide(totalAmount, BigDecimal.ROUND_CEILING, BigDecimal.ROUND_CEILING) + "%");
                    return vo;
                })
                .collect(Collectors.toList());
    }

    /**
     * 填写合同信息
     *
     * @param dto
     * @return
     */
    public String submitContract(ContractSaveDTO dto) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        String employeeCode = user.getEmployeeCode();
        VUtils.isTrue(StringUtils.isBlank(employeeCode)).throwMessage("获取登录用户 员工号 失败!");

        Long projectId = dto.getProjectId();
        VUtils.isTrue(Objects.isNull(projectId)).throwMessage("提交失败 缺少项目ID!");
        Project project = projectService.getNewProject(dto.getProjectId());
        VUtils.isTrue(Objects.isNull(project)).throwMessage("提交失败 此项目不存在!");

        //首先要判断 项目当前状态 是不是 采购结果备案
        VUtils.isTrue(!ProjectStatusEnum.UNDER_CONSTRUCTION.getCode().equals(project.getStatus()) ||
                        !ProjectStatusEnum.PROJECT_APPROVED.getCode().equals(project.getStage()))
                .throwMessage("提交失败 该项目不是 建设中或者已立项阶段");

        Contract contractEntity = new Contract();
        Contract contract = contractService.getOne(Wrappers.lambdaQuery(Contract.class)
                .eq(Contract::getProjectId, projectId)
                .last(BizConst.LIMIT_1));

        BeanUtil.copyProperties(dto, contractEntity);
        if (Objects.isNull(contract)) {
            contractEntity.setCreateOn(LocalDateTime.now());
            contractEntity.setCreateBy(employeeCode);
        } else {
            contractEntity.setId(contract.getId());
        }
        contractEntity.setProjectId(projectId);
        if (contractService.saveOrUpdate(contractEntity)) {
            //存 付款计划信心
            if (Objects.nonNull(dto.getPayments())) {
                //先删除
                paymentPlanService.remove(Wrappers.lambdaQuery(PaymentPlan.class)
                        .eq(PaymentPlan::getProjectId, projectId));
                if (CollUtil.isNotEmpty(dto.getPayments())) {
                    List<PaymentPlan> payments = dto.getPayments().stream().map(d -> {
                        PaymentPlan plan = BeanUtil.copyProperties(d, PaymentPlan.class);
                        plan.setProjectId(projectId);
                        plan.setCreateOn(LocalDateTime.now());
                        plan.setUpdateOn(LocalDateTime.now());
                        plan.setCreateBy(employeeCode);
                        plan.setUpdateBy(employeeCode);
                        return plan;
                    }).collect(Collectors.toList());

                    paymentPlanService.saveBatch(payments);
                }
            }
        }

        //判断下 如果和初验都完成了 才进入下一阶段
        if (StringUtils.isNotBlank(project.getPreliminaryInspectionMaterials())) {
            //进入到下一状态
            stateMachineUtils.pass(project);
        }
        project.setUpdateOn(LocalDateTime.now());
        if (Objects.nonNull(dto.getDeliveryTime())) {
            project.setDeliveryTime(dto.getDeliveryTime());
        }
        if (Objects.nonNull(dto.getTotalAmount())) {
            project.setContractAmount(dto.getTotalAmount());
        }
        projectService.updateById(project);

        return "填写成功";
    }

    /**
     * 待初验项目列表
     *
     * @param req
     * @return
     */
    public PageVo<ProjectLibListItemVO> preProjectlist(ProjectListReq req) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        VUtils.isTrue(Objects.isNull(user)).throwMessage("获取登录用户失败!");
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(req);
        //建设中状态
        query.eq(Project::getStatus, ProjectStatusEnum.UNDER_CONSTRUCTION.getCode());
        query.eq(Project::getStage, ProjectStatusEnum.PROJECT_APPROVED.getCode());
        query.eq(Project::getNewest, Boolean.TRUE);
        //交货时间 排序
        query.isNotNull(Project::getDeliveryTime);
        query.orderByAsc(Project::getDeliveryTime);
        Page<Project> page = projectService.page(req.page(), query);
        long total;
        if ((total = page.getTotal()) == 0) {
            return PageVo.empty();
        }
        List<ProjectLibListItemVO> records = CollUtils.convert(page.getRecords(), w -> {
            ProjectLibListItemVO item = new ProjectLibListItemVO();
            item.setId(w.getId());
            item.setProjectName(w.getProjectName());
            item.setCreateOn(w.getCreateOn());
            item.setDeclaredAmount(w.getDeclareAmount());
            item.setStage(w.getStage());
            item.setStatus(w.getStatus());
            item.setProjectType(w.getProjectType());
            item.setProjectYear(w.getProjectYear());
            item.setBuildOrg(w.getBuildOrgName());
            item.setBizDomain(w.getBizDomain());
            item.setProcessStatus(w.getProcessStatus());
            item.setInstCode(w.getInstCode());
            item.setIsHigherSuperOrg(w.getIsHigherSuperOrg());
            item.setApprovedAmount(w.getApprovalAmount());
            item.setApprovalDate(w.getApprovalDate());
            item.setDeliveryTime(w.getDeliveryTime());
            item.setContractAmount(w.getContractAmount());
            item.setAnnualPlanAmount(w.getAnnualPlanAmount());
            item.setTransactionTime(w.getTransactionTime());
            item.setTransactionAmount(w.getTransactionAmount());
            return item;
        });
        return PageVo.of(records, total);
    }

    public void exportPreList(HttpServletResponse response, ProjectListReq param) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        VUtils.isTrue(Objects.isNull(user)).throwMessage("获取登录用户失败!");
        LambdaQueryWrapper<Project> query = ProjectHelper.projectQuery(param);
        //待采购状态
        query.eq(Project::getStatus, ProjectStatusEnum.UNDER_CONSTRUCTION.getCode());
        query.eq(Project::getStage, ProjectStatusEnum.PROJECT_APPROVED.getCode());
        query.eq(Project::getNewest, Boolean.TRUE);
        //交货时间 排序
        query.isNotNull(Project::getDeliveryTime);
        query.orderByAsc(Project::getDeliveryTime);
        List<Project> records = projectService.list(query);

        List<DeclaredProjectExportDTO> collect = Lists.newArrayList();
        if (CollUtil.isNotEmpty(records)) {
            AtomicInteger serialNumber = new AtomicInteger(0);
            collect = records.stream().map(r -> {
                DeclaredProjectExportDTO exportDTO = new DeclaredProjectExportDTO();
                BeanUtils.copyProperties(r, exportDTO);
                exportDTO.setProjectTypeName(ProjectTypeEnum.getDesc(r.getProjectType()));
                exportDTO.setStatusName(ProjectStatusEnum.getDesc(r.getStatus()));
                String createOnStr = NdDateUtils.format(r.getCreateOn(), "yyyy-MM-dd HH:mm");
                exportDTO.setCreateOn(createOnStr);
                exportDTO.setSerialNumber(serialNumber.incrementAndGet());
                exportDTO.setDeliveryTime(r.getDeliveryTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                exportDTO.setContractAmount(r.getContractAmount());
                exportDTO.setAnnualPlanAmount(r.getAnnualPlanAmount());
                if(Objects.nonNull(r.getTransactionTime())){
                    exportDTO.setTransactionTime(r.getTransactionTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                }
                exportDTO.setTransactionAmount(r.getTransactionAmount());
                return exportDTO;
            }).collect(Collectors.toList());
        }

        String fileName = "合同备案项目列表";
        ExcelDownUtil.setFileName(fileName, response);
        //数据导出处理函数
        try {
            EasyExcel.write(response.getOutputStream(), DeclaredProjectExportDTO.class)
                    .autoCloseStream(false)
                    .registerWriteHandler(ExcelExportStyle.formalStyle())
                    .sheet(fileName)
                    .doWrite(collect);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询初验信息
     *
     * @param projectId
     * @return
     */
    public PreInsVO detailPreInsByProjectId(Long projectId) {
        Project project = projectService.getById(projectId);
        PreInsVO vo = new PreInsVO();
        vo.setProjectId(projectId);
        vo.setPreliminaryInspectionMaterials(project.getPreliminaryInspectionMaterials());
        LambdaQueryWrapper<PreInsAcceptancePerson> query = Wrappers.lambdaQuery(PreInsAcceptancePerson.class)
                .eq(PreInsAcceptancePerson::getProjectId, projectId)
                .orderByAsc(PreInsAcceptancePerson::getCreateOn);
        List<PreInsAcceptancePerson> acceptancePersons = acceptancePersonService.list(query);
        vo.setAcceptancePersons(convertPersons(acceptancePersons));
        return vo;
    }

    private List<PreInsAcceptancePersonVO> convertPersons(List<PreInsAcceptancePerson> acceptancePersons) {
        if (CollUtil.isEmpty(acceptancePersons)) {
            return Collections.emptyList();
        }
        return acceptancePersons.stream()
                .map(u -> BeanUtil.copyProperties(u, PreInsAcceptancePersonVO.class))
                .collect(Collectors.toList());
    }

    /**
     * 填写初验信息
     *
     * @param dto
     * @return
     */
    public String submitPreIns(PreInsSaveDTO dto) {
        UserFullInfoDTO user = userInfoHelper.getUserFullInfo(LoginUserUtil.getUserId());
        String employeeCode = user.getEmployeeCode();
        VUtils.isTrue(StringUtils.isBlank(employeeCode)).throwMessage("获取登录用户 员工号 失败!");

        Long projectId = dto.getProjectId();
        VUtils.isTrue(Objects.isNull(projectId)).throwMessage("提交失败 缺少项目ID!");
        Project project = projectService.getNewProject(dto.getProjectId());
        VUtils.isTrue(Objects.isNull(project)).throwMessage("提交失败 此项目不存在!");

        //首先要判断 项目当前状态 是不是 采购结果备案
        VUtils.isTrue(!ProjectStatusEnum.UNDER_CONSTRUCTION.getCode().equals(project.getStatus()) ||
                        !ProjectStatusEnum.PROJECT_APPROVED.getCode().equals(project.getStage()))
                .throwMessage("提交失败 该项目不是 建设中或者已立项阶段");

        //初验人员
        if (Objects.nonNull(dto.getAcceptancePersons())) {
            //先删除
            acceptancePersonService.remove(Wrappers.lambdaQuery(PreInsAcceptancePerson.class)
                    .eq(PreInsAcceptancePerson::getProjectId, projectId));
            if (CollUtil.isNotEmpty(dto.getAcceptancePersons())) {
                List<PreInsAcceptancePerson> persons = dto.getAcceptancePersons().stream().map(d -> {
                    PreInsAcceptancePerson person = BeanUtil.copyProperties(d, PreInsAcceptancePerson.class);
                    person.setProjectId(projectId);
                    person.setCreateOn(LocalDateTime.now());
                    person.setUpdateOn(LocalDateTime.now());
                    person.setCreateBy(employeeCode);
                    person.setUpdateBy(employeeCode);
                    return person;
                }).collect(Collectors.toList());

                acceptancePersonService.saveBatch(persons);
            }
        }


        //判断下 如果和初验都完成了 才进入下一阶段
        if (Objects.nonNull(project.getDeliveryTime())) {
            //进入到下一状态
            stateMachineUtils.pass(project);
        }
        project.setUpdateOn(LocalDateTime.now());
        project.setPreliminaryInspectionMaterials(dto.getPreliminaryInspectionMaterials());
        projectService.updateById(project);

        return "填写成功";
    }
}
