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

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.dal.dataobject.project.ProjectDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
import java.util.Map;

@Mapper
public interface ProjectMapper extends BaseMapperX<ProjectDO> {

    default ProjectDO selectByCode(String projectCode) {
        return selectOne(ProjectDO::getProjectCode, projectCode);
    }

    default ProjectDO selectActiveByProductIdAndName(Long productId, String projectName, String excludedStatusCode) {
        LambdaQueryWrapperX<ProjectDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eq(ProjectDO::getProjectName, projectName)
                .ne(ProjectDO::getStatusCode, excludedStatusCode);
        if (productId == null) {
            queryWrapper.isNull(ProjectDO::getProductId);
        } else {
            queryWrapper.eq(ProjectDO::getProductId, productId);
        }
        return selectOne(queryWrapper);
    }

    default List<ProjectDO> selectActiveMainlineListByProductId(Long productId, Collection<String> projectTypes,
                                                                String excludedStatusCode) {
        return selectList(new LambdaQueryWrapperX<ProjectDO>()
                .eq(ProjectDO::getProductId, productId)
                .in(ProjectDO::getProjectType, projectTypes)
                .ne(ProjectDO::getStatusCode, excludedStatusCode));
    }

    @Select("""
            SELECT status_code AS statusCode, COUNT(*) AS countValue
            FROM rdms_project
            WHERE deleted = b'0'
            GROUP BY status_code
            """)
    List<Map<String, Object>> selectStatusCountList();

    default List<ProjectDO> selectListByCodePrefix(String codePrefix) {
        return selectList(new LambdaQueryWrapperX<ProjectDO>()
                .likeRight(ProjectDO::getProjectCode, codePrefix)
                .orderByDesc(ProjectDO::getProjectCode));
    }

    default List<ProjectDO> selectListByProductId(Long productId) {
        return selectList(new LambdaQueryWrapperX<ProjectDO>()
                .eq(ProjectDO::getProductId, productId)
                .orderByDesc(BaseDO::getCreateTime));
    }

    default List<ProjectDO> selectListByManagerUserId(Long managerUserId) {
        return selectList(new LambdaQueryWrapperX<ProjectDO>()
                .eq(ProjectDO::getManagerUserId, managerUserId)
                .orderByDesc(BaseDO::getCreateTime));
    }

    default List<ProjectDO> selectListByStatusCodesNotIn(Collection<String> statusCodes) {
        LambdaQueryWrapperX<ProjectDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.isNotNull(ProjectDO::getManagerUserId);
        queryWrapper.orderByDesc(BaseDO::getCreateTime);
        if (statusCodes != null && !statusCodes.isEmpty()) {
            queryWrapper.notIn(ProjectDO::getStatusCode, statusCodes);
        }
        return selectList(queryWrapper);
    }

    default List<ProjectDO> selectListByManagerUserIdsAndStatusCodesNotIn(Collection<Long> managerUserIds,
                                                                          Collection<String> statusCodes) {
        if (managerUserIds == null || managerUserIds.isEmpty()) {
            return List.of();
        }
        LambdaQueryWrapperX<ProjectDO> queryWrapper = new LambdaQueryWrapperX<ProjectDO>()
                .in(ProjectDO::getManagerUserId, managerUserIds)
                .orderByDesc(BaseDO::getCreateTime);
        if (statusCodes != null && !statusCodes.isEmpty()) {
            queryWrapper.notIn(ProjectDO::getStatusCode, statusCodes);
        }
        return selectList(queryWrapper);
    }

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

    default int deleteByIdAndStatus(Long id, String statusCode) {
        return delete(new LambdaQueryWrapperX<ProjectDO>()
                .eq(ProjectDO::getId, id)
                .eq(ProjectDO::getStatusCode, statusCode));
    }

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

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

    /**
     * 临期/逾期告警候选：计划完成日非空且 <= maxPlannedEndDate，状态不在排除集（终态+paused）。
     * excludeStatusCodes 为空时不排除任何状态（空集守卫，与既有口径一致）。
     * 设计见 docs/domains/project/2026-06-12-临期逾期告警-设计.html。
     */
    default List<ProjectDO> selectDueAlertCandidateList(LocalDate maxPlannedEndDate,
                                                        Collection<String> excludeStatusCodes) {
        return selectList(new LambdaQueryWrapperX<ProjectDO>()
                .isNotNull(ProjectDO::getPlannedEndDate)
                .le(ProjectDO::getPlannedEndDate, maxPlannedEndDate)
                .notIn(excludeStatusCodes != null && !excludeStatusCodes.isEmpty(),
                        ProjectDO::getStatusCode, excludeStatusCodes));
    }

}
