package com.njcn.rdms.module.project.dal.mysql.project.task;

import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.ProjectTaskStatusBoardReqVO;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.ProjectTaskPageReqVO;
import com.njcn.rdms.module.project.dal.dataobject.project.task.ProjectTaskDO;
import com.njcn.rdms.module.project.service.project.permission.VisibilityScope;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Mapper
public interface ProjectTaskMapper extends BaseMapperX<ProjectTaskDO> {

    default ProjectTaskDO selectByProjectIdAndExecutionIdAndId(Long projectId, Long executionId, Long taskId) {
        return selectOne(new LambdaQueryWrapperX<ProjectTaskDO>()
                .eq(ProjectTaskDO::getProjectId, projectId)
                .eq(ProjectTaskDO::getExecutionId, executionId)
                .eq(ProjectTaskDO::getId, taskId));
    }

    default PageResult<ProjectTaskDO> selectPageByExecutionId(Long projectId, Long executionId,
                                                              VisibilityScope scope,
                                                              ProjectTaskPageReqVO reqVO) {
        // 可见性短路：非 seesAll 且无任何可见任务 → 空页
        if (!scope.seesAll() && scope.taskIds().isEmpty()) {
            return PageResult.empty();
        }
        LambdaQueryWrapperX<ProjectTaskDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eq(ProjectTaskDO::getProjectId, projectId);
        queryWrapper.eq(ProjectTaskDO::getExecutionId, executionId);
        queryWrapper.eqIfPresent(ProjectTaskDO::getParentTaskId, reqVO.getParentTaskId());
        queryWrapper.eqIfPresent(ProjectTaskDO::getOwnerId, reqVO.getOwnerId());
        queryWrapper.eqIfPresent(ProjectTaskDO::getStatusCode, reqVO.getStatusCode());
        queryWrapper.betweenIfPresent(BaseDO::getUpdateTime, reqVO.getUpdateTime());
        queryWrapper.orderByAsc(ProjectTaskDO::getParentTaskId);
        queryWrapper.orderByDesc(BaseDO::getCreateTime);
        queryWrapper.orderByDesc(ProjectTaskDO::getId);
        if (StringUtils.hasText(reqVO.getKeyword())) {
            queryWrapper.and(wrapper -> wrapper.like(ProjectTaskDO::getTaskTitle, reqVO.getKeyword()));
        }
        if (!scope.seesAll()) {
            queryWrapper.in(ProjectTaskDO::getId, scope.taskIds());
        }
        return selectPage(reqVO, queryWrapper);
    }

    default int updateStatusByIdAndStatus(Long id, String fromStatus, String toStatus, String lastStatusReason) {
        ProjectTaskDO update = new ProjectTaskDO();
        update.setStatusCode(toStatus);
        update.setLastStatusReason(lastStatusReason);
        return update(update, new LambdaQueryWrapperX<ProjectTaskDO>()
                .eq(ProjectTaskDO::getId, id)
                .eq(ProjectTaskDO::getStatusCode, fromStatus));
    }

    /**
     * 软删任务（按状态 CAS）。仅在 statusCode 与传入 fromStatus 匹配时才标 deleted=1。
     * 返回 1 表示成功；返回 0 视为并发修改。
     */
    default int deleteByIdAndStatus(Long id, String fromStatus) {
        return delete(new LambdaQueryWrapperX<ProjectTaskDO>()
                .eq(ProjectTaskDO::getId, id)
                .eq(ProjectTaskDO::getStatusCode, fromStatus));
    }

    /**
     * 仅更新实际开始/结束日期。null 字段依据全局 FieldStrategy 不会被覆盖。
     */
    default int updateActualDatesById(Long id, LocalDate actualStartDate, LocalDate actualEndDate) {
        if (actualStartDate == null && actualEndDate == null) {
            return 0;
        }
        ProjectTaskDO update = new ProjectTaskDO();
        update.setActualStartDate(actualStartDate);
        update.setActualEndDate(actualEndDate);
        return update(update, new LambdaQueryWrapperX<ProjectTaskDO>()
                .eq(ProjectTaskDO::getId, id));
    }

    default Integer countChildrenNotInStatus(Long parentTaskId, List<String> terminalStatusCodes) {
        LambdaQueryWrapperX<ProjectTaskDO> queryWrapper = new LambdaQueryWrapperX<ProjectTaskDO>()
                .eq(ProjectTaskDO::getParentTaskId, parentTaskId);
        if (terminalStatusCodes != null && !terminalStatusCodes.isEmpty()) {
            queryWrapper.notIn(ProjectTaskDO::getStatusCode, terminalStatusCodes);
        }
        return Math.toIntExact(selectCount(queryWrapper));
    }

    /**
     * 统计指定执行下处于非终态的任务数。用于执行 complete 前置校验（要求所有任务必须终态）。
     */
    default Integer countByExecutionIdNotInStatus(Long executionId, List<String> terminalStatusCodes) {
        LambdaQueryWrapperX<ProjectTaskDO> queryWrapper = new LambdaQueryWrapperX<ProjectTaskDO>()
                .eq(ProjectTaskDO::getExecutionId, executionId);
        if (terminalStatusCodes != null && !terminalStatusCodes.isEmpty()) {
            queryWrapper.notIn(ProjectTaskDO::getStatusCode, terminalStatusCodes);
        }
        return Math.toIntExact(selectCount(queryWrapper));
    }

    /**
     * 数指定父任务下的直接子任务（不区分状态）。用于"是否叶子任务"判定与进度汇总。
     */
    default int countChildrenByParentTaskId(Long parentTaskId) {
        if (parentTaskId == null) {
            return 0;
        }
        return Math.toIntExact(selectCount(new LambdaQueryWrapperX<ProjectTaskDO>()
                .eq(ProjectTaskDO::getParentTaskId, parentTaskId)));
    }

    /**
     * 取直接子任务的 progressRate 列表（用于父任务进度简单平均汇总）。
     * 只读取必要字段，避免拉回整行；逻辑删除的子任务由 BaseMapper 自动过滤。
     */
    default List<ProjectTaskDO> selectChildrenProgressByParentTaskId(Long parentTaskId) {
        if (parentTaskId == null) {
            return Collections.emptyList();
        }
        return selectList(new LambdaQueryWrapperX<ProjectTaskDO>()
                .select(ProjectTaskDO::getId, ProjectTaskDO::getProgressRate)
                .eq(ProjectTaskDO::getParentTaskId, parentTaskId));
    }

    /**
     * 执行详情进度：按当前执行下一级任务 progressRate 简单平均；无一级任务时 SQL 返回 null。
     */
    @Select("""
            SELECT AVG(COALESCE(progress_rate, 0))
            FROM rdms_task
            WHERE deleted = b'0'
              AND project_id = #{projectId}
              AND execution_id = #{executionId}
              AND parent_task_id IS NULL
            """)
    BigDecimal selectRootTaskAvgProgressByExecutionId(@Param("projectId") Long projectId,
                                                      @Param("executionId") Long executionId);

    /**
     * 执行分页进度：按当前页 executionId 批量聚合一级任务 progressRate，避免列表 N+1。
     */
    @Select("""
            <script>
            SELECT execution_id AS executionId, AVG(COALESCE(progress_rate, 0)) AS progressRate
            FROM rdms_task
            WHERE deleted = b'0'
              AND project_id = #{projectId}
              AND execution_id IN
                <foreach collection="executionIds" item="id" open="(" separator="," close=")">#{id}</foreach>
              AND parent_task_id IS NULL
            GROUP BY execution_id
            </script>
            """)
    List<Map<String, Object>> selectRootTaskAvgProgressGroupByExecutionIds(
            @Param("projectId") Long projectId,
            @Param("executionIds") Collection<Long> executionIds);

    /**
     * 仅更新单个任务的 progressRate，不动其他字段（避免污染 lastStatusReason 等）。
     */
    default int updateProgressRateById(Long id, BigDecimal progressRate) {
        ProjectTaskDO update = new ProjectTaskDO();
        update.setProgressRate(progressRate);
        return update(update, new LambdaQueryWrapperX<ProjectTaskDO>()
                .eq(ProjectTaskDO::getId, id));
    }

    /**
     * 递归 CTE：从"userId 在指定项目下作为 owner_id 的全部任务"出发，向下展开包含所有子孙的任务 ID。
     * 用于 VisibilityScopeResolver 中"任务负责人 → 自己 + 全部子孙"规则的 scope 收集。
     *
     * 任务表已逻辑删除的行不参与递归（WHERE 子句过滤 deleted）。
     * 单棵子树最大深度受 MySQL `cte_max_recursion_depth`（默认 1000）限制，业务实际任务树远低于此。
     */
    @Select("""
            WITH RECURSIVE owned (id) AS (
                SELECT id FROM rdms_task
                 WHERE deleted = b'0'
                   AND project_id = #{projectId}
                   AND owner_id   = #{userId}
                UNION ALL
                SELECT t.id FROM rdms_task t
                  JOIN owned o ON t.parent_task_id = o.id
                 WHERE t.deleted = b'0'
            )
            SELECT id FROM owned
            """)
    List<Long> selectOwnedTaskAndDescendantIdsByProjectIdAndUserId(
            @Param("projectId") Long projectId,
            @Param("userId") Long userId);

    /**
     * 同 selectOwnedTaskAndDescendantIdsByProjectIdAndUserId 但再加 execution_id 维度。
     * 注意：递归向下展开只跟着 parent_task_id，子任务必然与父任务在同一 execution 下，
     * 因此 execution_id 过滤仅作用于种子（owned）那一步即可。
     */
    @Select("""
            WITH RECURSIVE owned (id) AS (
                SELECT id FROM rdms_task
                 WHERE deleted = b'0'
                   AND project_id   = #{projectId}
                   AND execution_id = #{executionId}
                   AND owner_id     = #{userId}
                UNION ALL
                SELECT t.id FROM rdms_task t
                  JOIN owned o ON t.parent_task_id = o.id
                 WHERE t.deleted = b'0'
            )
            SELECT id FROM owned
            """)
    List<Long> selectOwnedTaskAndDescendantIdsByExecutionIdAndUserId(
            @Param("projectId") Long projectId,
            @Param("executionId") Long executionId,
            @Param("userId") Long userId);

    default Integer countByProjectIdAndExecutionIdAndStatusCode(Long projectId, Long executionId,
                                                                VisibilityScope scope,
                                                                ProjectTaskStatusBoardReqVO reqVO,
                                                                String statusCode) {
        // 可见性短路：非 seesAll 且无任何可见任务 → 0
        if (!scope.seesAll() && scope.taskIds().isEmpty()) {
            return 0;
        }
        LambdaQueryWrapperX<ProjectTaskDO> queryWrapper = new LambdaQueryWrapperX<ProjectTaskDO>()
                .eq(ProjectTaskDO::getProjectId, projectId)
                .eq(ProjectTaskDO::getExecutionId, executionId)
                .eq(ProjectTaskDO::getStatusCode, statusCode)
                .eqIfPresent(ProjectTaskDO::getParentTaskId, reqVO.getParentTaskId())
                .eqIfPresent(ProjectTaskDO::getOwnerId, reqVO.getOwnerId())
                .betweenIfPresent(BaseDO::getUpdateTime, reqVO.getUpdateTime());
        if (StringUtils.hasText(reqVO.getKeyword())) {
            queryWrapper.and(wrapper -> wrapper.like(ProjectTaskDO::getTaskTitle, reqVO.getKeyword()));
        }
        if (!scope.seesAll()) {
            queryWrapper.in(ProjectTaskDO::getId, scope.taskIds());
        }
        return Math.toIntExact(selectCount(queryWrapper));
    }

}
