package com.ruoyi.oa.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.oa.domain.bo.SysOaWarehouseDetailBo;
import com.ruoyi.oa.domain.dto.ProjectActivityDTO;
import com.ruoyi.oa.domain.dto.ProjectDataDTO;
import com.ruoyi.oa.domain.vo.*;
import com.ruoyi.oa.service.CodeGeneratorService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.oa.domain.bo.SysOaProjectBo;
import com.ruoyi.oa.domain.SysOaProject;
import com.ruoyi.oa.mapper.SysOaProjectMapper;
import com.ruoyi.oa.service.ISysOaProjectService;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目管理Service业务层处理
 *
 * @author huangxing
 * @date 2024-01-11
 */
@RequiredArgsConstructor
@Service
public class SysOaProjectServiceImpl implements ISysOaProjectService {

    private final SysOaProjectMapper baseMapper;

    @Autowired
    private CodeGeneratorService codeGeneratorService;


    // 1. 定义常量列表（最好放到某个常量类里）
    private static final Set<String> VALID_PREFIXES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
            "PL", "TC", "GI", "GL", "ZAM", "CC", "CR", "STL", "SM", "TH", "RJ"
    )));


    /**
     * 查询项目管理
     */
    @Override
    public SysOaProjectVo queryById(Long projectId) {
        return baseMapper.selectVoByIdPlus(projectId);
    }

    /**
     * 查询项目管理列表
     */
    @Override
    public TableDataInfo<SysOaProjectVo> queryPageList(SysOaProjectBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<SysOaProject> lqw = buildQueryWrapper(bo);
        Page<SysOaProjectVo> result = baseMapper.selectVoPlus(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询项目管理列表
     */
    @Override
    public List<SysOaProjectVo> queryList(SysOaProjectBo bo) {
        LambdaQueryWrapper<SysOaProject> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<SysOaProject> buildQueryWrapper(SysOaProjectBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<SysOaProject> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getProjectName()), SysOaProject::getProjectName, bo.getProjectName());
        lqw.like(StringUtils.isNotBlank(bo.getProjectNum()), SysOaProject::getProjectNum, bo.getProjectNum());
        lqw.like(StringUtils.isNotBlank(bo.getProjectCode()), SysOaProject::getProjectCode, bo.getProjectCode());

        lqw.eq(bo.getTradeType() != null, SysOaProject::getTradeType, bo.getTradeType());
        lqw.gt(bo.getPrePay() != null && bo.getPrePay() > 0, SysOaProject::getPrePay, 0);
        lqw.eq(StringUtils.isNotBlank(bo.getProjectType()), SysOaProject::getProjectType, bo.getProjectType());
        lqw.eq(StringUtils.isNotBlank(bo.getProjectStatus()), SysOaProject::getProjectStatus, bo.getProjectStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), SysOaProject::getCreateBy, bo.getCreateBy());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
                SysOaProject::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.orderByDesc(SysOaProject::getCreateTime);
        return lqw;
    }

    /**
     * 新增项目管理
     */
    @Override
    public Boolean insertByBo(SysOaProjectBo bo) {
        if (bo.getProjectCode() != null) {
            bo.setProjectCode(codeGeneratorService.nextCode(bo.getProjectCode()));
        }
        SysOaProject add = BeanUtil.toBean(bo, SysOaProject.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setProjectId(add.getProjectId());
        }
        return flag;
    }

    /**
     * 修改项目管理
     */
    @Override
    public Boolean updateByBo(SysOaProjectBo bo) {
        String prefix = bo.getProjectCode();
        if (prefix != null && VALID_PREFIXES.contains(prefix)) {
            // 只有当前缀在上面列表里，才真正去生成新编号
            bo.setProjectCode(codeGeneratorService.nextCode(prefix));
        }
        SysOaProject update = BeanUtil.toBean(bo, SysOaProject.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    // 2. 在你的逻辑里做判断

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(SysOaProject entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除项目管理
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 查询项目出库记录
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<SysOaOutWarehouseListVo> queryOutWarePageList(SysOaWarehouseDetailBo bo, PageQuery pageQuery) {
        Page<SysOaOutWarehouseListVo> page = baseMapper.selectPageOutList(pageQuery.build(), this.buildOutWareQueryWrapper(bo));
        return TableDataInfo.build(page);
    }

    /**
     * 获取当月每个项目的人力资源成本
     *
     * @return
     */
    @Override
    public List<SysOaProjectVo> getProjectDataByMonth(Date date) {
        if (date == null) {
            date = new Date();
        }
        return baseMapper.getProjectDataByMonth(getFirstDay(date), getLastDay(date));
    }

    /**
     * 统计数据需要
     *
     * @return
     */
    @Override
    public List<SysOaProjectVo> getProjectDataByMonthAndDate() {
        Date date = new Date();
        List<SysOaProjectVo> projectVos = baseMapper.getProjectDataByMonthAndDate(getFirstDay(date), getLastDay(date));

        return projectVos;
    }

    @Override
    public TableDataInfo<SysOaProjectVo> queryFiles(SysOaWarehouseDetailBo bo, PageQuery pageQuery) {
        QueryWrapper<SysOaProject> sysOaProjectQueryWrapper = buildOutWareQueryWrapper(bo);
        sysOaProjectQueryWrapper.groupBy("sop.project_id", "sop.project_name", "t.task_id", "t.task_type", "t.status", "nick_name", "ti.item_id", "ti.sign_time", "sop.create_time");
//        sysOaProjectQueryWrapper.having("file_urls IS NOT NULL");
        Page<SysOaProjectVo> result = baseMapper.selectFileVoList(pageQuery.build(), sysOaProjectQueryWrapper);
        List<SysOaProjectVo> projects = result.getRecords();
        // 2) 遍历每个项目，对其 taskList 做补充
        for (SysOaProjectVo project : projects) {
            List<SysOaTaskVo> taskList = project.getTaskList();
            if (taskList == null || taskList.isEmpty()) {
                continue;
            }

            // 2.1 取出已经出现的 taskType
            Set<Long> existingTypes = taskList.stream()
                    .map(SysOaTaskVo::getTaskType)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());

            // 2.2 找出最大 taskType
            Long maxType = existingTypes.stream().max(Long::compareTo).orElse(0L);
            if (maxType <= 1) {
                // 如果最大只有1，且你仍想补齐1..1，那其实啥都不补。
                // 也可以补其它逻辑
                continue;
            }

            // 2.3 在 1..maxType 范围内检查是否缺失
            for (Long type = 1L; type <= maxType; type++) {
                if (!existingTypes.contains(type)) {
                    // 缺失则插入一个空对象
                    SysOaTaskVo emptyTask = new SysOaTaskVo();
                    emptyTask.setTaskType(type);
                    // 例如给 status 默认 0, 或者不赋值
                    emptyTask.setStatus(null);
                    // 任务item列表为空
                    emptyTask.setTaskItemVoList(new ArrayList<>());
                    // 其他字段如 nickName, createTime, etc. 也可设为空或默认值

                    taskList.add(emptyTask);
                }
            }

            // 此时taskList已经包含了 1..maxType 全部类型
            // 如果你想让结果中的 taskType 从大到小排序或从小到大排序，再手动sort一下
            taskList.sort(Comparator.comparing(SysOaTaskVo::getTaskType));
        }
        return TableDataInfo.build(result);
    }

    /**
     * 进度使用的查询接口
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<SysOaProjectVo> queryPageList2(SysOaProjectBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<SysOaProject> lqw = buildQueryWrapper(bo);
        Page<SysOaProjectVo> result = baseMapper.selectVoAndProgress(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询项目 进度使用
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<SysOaProjectVo> listProjects(SysOaProjectBo bo, PageQuery pageQuery) {
        QueryWrapper<SysOaProject> qw = new QueryWrapper<SysOaProject>();
        qw.like(StringUtils.isNotBlank(bo.getProjectName()), "sop.project_name", bo.getProjectName());
        qw.eq("sop.trade_type", bo.getTradeType());
        qw.like(StringUtils.isNotBlank(bo.getProjectNum()), "sop.project_num", bo.getProjectNum());
        qw.groupBy("sop.project_id");
        Page<SysOaProjectVo> result = baseMapper.selectVoListPage(pageQuery.build(), qw);
        return TableDataInfo.build(result);
    }

    /**
     * 查看存在出库的项目
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<SysOaProjectVo> listWareProject(SysOaProjectBo bo, PageQuery pageQuery) {
        QueryWrapper<SysOaProject> lqw = Wrappers.query();
        lqw.like(StringUtils.isNotBlank(bo.getProjectName()), "sop.project_name", bo.getProjectName());
        lqw.like(StringUtils.isNotBlank(bo.getProjectCode()), "sop.project_code", bo.getProjectCode());
        lqw.like(StringUtils.isNotBlank(bo.getProjectNum()), "sop.project_num", bo.getProjectNum());
        Page<SysOaProjectVo> result = baseMapper.listWareProject(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public DashboardMetricsVo getMetrics(Date start, Date end,Long tradeType) {

        // 本期
        BigDecimal curFunds = baseMapper.selectTotalFunds(start, end,tradeType);
        Integer curCount = baseMapper.selectContractCount(start, end,tradeType);
        Integer curExpire = baseMapper.selectExpiringCount(start, end,tradeType);

        // 前期平均
        BigDecimal avgFunds = baseMapper.selectPrevAvgTotalFunds(start, end,tradeType);
        BigDecimal avgCount = baseMapper.selectPrevAvgContractCount(start, end,tradeType);
        BigDecimal avgExpire = baseMapper.selectPrevAvgExpiringCount(start, end,tradeType);

        // 计算增长率 = (cur - avg) / avg * 100
        BigDecimal fundRate = avgFunds.compareTo(BigDecimal.ZERO) == 0
                ? BigDecimal.ZERO
                : curFunds.subtract(avgFunds)
                .divide(avgFunds, 2, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));

        BigDecimal countRate = avgCount.compareTo(BigDecimal.ZERO) == 0
                ? BigDecimal.ZERO
                : BigDecimal.valueOf(curCount).subtract(avgCount)
                .divide(avgCount, 2, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));

        BigDecimal expireRate = avgExpire.compareTo(BigDecimal.ZERO) == 0
                ? BigDecimal.ZERO
                : BigDecimal.valueOf(curExpire).subtract(avgExpire)
                .divide(avgExpire, 2, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));

        // 组装 VO
        DashboardMetricsVo vo = new DashboardMetricsVo();
        vo.setTotalFunds(curFunds);
        vo.setGrowthFunds(fundRate);
        vo.setContractCount(curCount);
        vo.setGrowthCount(countRate);
        vo.setExpiringCount(curExpire);
        vo.setGrowthExpiring(expireRate);

        // 用本月金额 ÷ 历史平均 × 100 得到 completionRate
        BigDecimal completionRate;
        if (avgFunds.compareTo(BigDecimal.ZERO) > 0) {
            completionRate = curFunds
                    .divide(avgFunds, 2, RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(100));
        } else {
            completionRate = BigDecimal.ZERO;
        }
        vo.setCompletionRate(completionRate);
        return vo;
    }

    @Override
    public DashboardChartsVo getCharts(Date start, Date end,Long tradeType) {
        DashboardChartsVo vo = new DashboardChartsVo();
        vo.setContractAmountTrend(baseMapper.selectContractAmountTrend(start, end,tradeType));
        vo.setProjectStatusDistribution(baseMapper.selectProjectStatusDistribution(tradeType));
        vo.setMonthlyContractComparison(baseMapper.selectMonthlyContractComparison(start, end,tradeType));
        return vo;
    }

    @Override
    public List<SysOaProjectVo> getExpiringForeignProjects(Integer expireDays,Long tradeType) {
        Date now = new Date();
        return baseMapper.selectExpiringForeignProjects(now, expireDays,tradeType);
    }

    @Override
    public List<ProjectDataDTO> getProjectData(String month) {
        return baseMapper.getProjectData(month);
    }

    @Override
    public List<ProjectActivityDTO> getProjectActivities() {
        return baseMapper.getProjectActivities();
    }

    private QueryWrapper<SysOaProject> buildOutWareQueryWrapper(SysOaWarehouseDetailBo bo) {
        QueryWrapper<SysOaProject> lqw = Wrappers.query();
        lqw.like(StringUtils.isNotBlank(bo.getProjectName()), "sop.projec_name", bo.getProjectName());
        lqw.orderByDesc("sowm.create_time");
        return lqw;
    }


    private Date getFirstDay(Date time) {
        String firstDay = DateUtils.parseDateToStr("yyyy-MM-01", time);
        return DateUtils.parseDate(firstDay);
    }

    private Date getLastDay(Date time) {
        String format = "yyyy-MM-";
        // 计算某月的天数
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(time);
        String dateLength = String.valueOf(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        // 将字符串拼接
        format += dateLength;

        return DateUtils.parseDate(DateUtils.parseDateToStr(format, time));
    }
}
