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

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.rdms.module.project.constant.WorkReportConstants;
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportPageReqVO;
import com.njcn.rdms.module.project.dal.dataobject.workreport.common.WorkReportMemberSnapshotItem;
import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportDO;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.util.StringUtils;

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

@Mapper
public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {

    String JACKSON_TYPE_HANDLER_MAPPING = "typeHandler=" + JacksonTypeHandler.class.getCanonicalName();

    default ProjectReportDO selectByProjectIdAndPeriodKeyAndProjectOwnerId(Long projectId, String periodKey,
            Long projectOwnerId) {
        return selectOne(new LambdaQueryWrapperX<ProjectReportDO>()
                .eq(ProjectReportDO::getProjectId, projectId)
                .eq(ProjectReportDO::getPeriodKey, periodKey)
                .eq(ProjectReportDO::getProjectOwnerId, projectOwnerId));
    }

    default List<ProjectReportDO> selectListByProjectOwnerIdsAndPeriodKey(Collection<Long> projectOwnerIds,
            String periodKey,
            Collection<String> allowedStatusCodes) {
        if (projectOwnerIds == null || projectOwnerIds.isEmpty() || !StringUtils.hasText(periodKey)) {
            return List.of();
        }
        LambdaQueryWrapperX<ProjectReportDO> wrapper = new LambdaQueryWrapperX<ProjectReportDO>()
                .in(ProjectReportDO::getProjectOwnerId, projectOwnerIds)
                .eq(ProjectReportDO::getPeriodKey, periodKey);
        if (allowedStatusCodes != null) {
            if (allowedStatusCodes.isEmpty()) {
                return List.of();
            }
            wrapper.in(ProjectReportDO::getStatusCode, allowedStatusCodes);
        }
        return selectList(wrapper);
    }

    /**
     * 按项目负责人 ID 列表与周期日期区间查询项目半月报（periodStartDate 落在 [startDate, endDate] 内）。
     */
    default List<ProjectReportDO> selectListByProjectOwnerIdsAndPeriodDateRange(Collection<Long> projectOwnerIds,
            LocalDate startDate, LocalDate endDate,
            Collection<String> allowedStatusCodes) {
        if (projectOwnerIds == null || projectOwnerIds.isEmpty()) {
            return List.of();
        }
        LambdaQueryWrapperX<ProjectReportDO> wrapper = new LambdaQueryWrapperX<ProjectReportDO>()
                .in(ProjectReportDO::getProjectOwnerId, projectOwnerIds)
                .geIfPresent(ProjectReportDO::getPeriodStartDate, startDate)
                .leIfPresent(ProjectReportDO::getPeriodStartDate, endDate);
        if (allowedStatusCodes != null) {
            if (allowedStatusCodes.isEmpty()) {
                return List.of();
            }
            wrapper.in(ProjectReportDO::getStatusCode, allowedStatusCodes);
        }
        return selectList(wrapper);
    }

    default PageResult<ProjectReportDO> selectReporterPage(Long reporterId, ProjectReportPageReqVO reqVO) {
        return selectReporterPage(reporterId, reqVO, null);
    }

    default PageResult<ProjectReportDO> selectReporterPage(Long reporterId, ProjectReportPageReqVO reqVO,
            Collection<String> allowedStatusCodes) {
        if (allowedStatusCodes != null && allowedStatusCodes.isEmpty()) {
            return new PageResult<>(List.of(), 0L);
        }
        LambdaQueryWrapperX<ProjectReportDO> wrapper = buildPageQuery(reqVO)
                .eq(ProjectReportDO::getProjectOwnerId, reporterId)
                .orderByDesc(ProjectReportDO::getPeriodStartDate)
                .orderByDesc(ProjectReportDO::getId);
        if (allowedStatusCodes != null) {
            wrapper.in(ProjectReportDO::getStatusCode, allowedStatusCodes);
        }
        return selectPage(reqVO, wrapper);
    }

    default PageResult<ProjectReportDO> selectReporterPage(Collection<Long> reporterIds, ProjectReportPageReqVO reqVO,
            Collection<String> allowedStatusCodes) {
        if (reporterIds == null || reporterIds.isEmpty()
                || (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
            return new PageResult<>(List.of(), 0L);
        }
        LambdaQueryWrapperX<ProjectReportDO> wrapper = buildPageQuery(reqVO)
                .in(ProjectReportDO::getProjectOwnerId, reporterIds)
                .orderByDesc(ProjectReportDO::getPeriodStartDate)
                .orderByDesc(ProjectReportDO::getId);
        if (allowedStatusCodes != null) {
            wrapper.in(ProjectReportDO::getStatusCode, allowedStatusCodes);
        }
        return selectPage(reqVO, wrapper);
    }

    default PageResult<ProjectReportDO> selectApprovalPage(Long supervisorUserId, ProjectReportPageReqVO reqVO) {
        LambdaQueryWrapperX<ProjectReportDO> wrapper = buildPageQuery(reqVO)
                .eq(ProjectReportDO::getSupervisorUserId, supervisorUserId)
                .eq(ProjectReportDO::getStatusCode, WorkReportConstants.STATUS_PENDING_APPROVAL)
                .orderByDesc(ProjectReportDO::getSubmitTime)
                .orderByDesc(ProjectReportDO::getId);
        return selectPage(reqVO, wrapper);
    }

    default int updateByIdAndStatus(ProjectReportDO update, Long id, String fromStatus) {
        return update(update, new LambdaQueryWrapperX<ProjectReportDO>()
                .eq(ProjectReportDO::getId, id)
                .eq(ProjectReportDO::getStatusCode, fromStatus));
    }

    default int updateSubmitFieldsByIdAndStatus(Long id, String fromStatus, String projectName,
            Long projectOwnerId, String projectOwnerName,
            List<WorkReportMemberSnapshotItem> projectMemberSnapshot,
            Long supervisorUserId, String supervisorName, String toStatus,
            LocalDateTime submitTime, String updater) {
        return update(null, new LambdaUpdateWrapper<ProjectReportDO>()
                .set(ProjectReportDO::getProjectName, projectName)
                .set(ProjectReportDO::getProjectOwnerId, projectOwnerId)
                .set(ProjectReportDO::getProjectOwnerName, projectOwnerName)
                .set(ProjectReportDO::getProjectMemberSnapshot, projectMemberSnapshot, JACKSON_TYPE_HANDLER_MAPPING)
                .set(ProjectReportDO::getSupervisorUserId, supervisorUserId)
                .set(ProjectReportDO::getSupervisorName, supervisorName)
                .set(ProjectReportDO::getStatusCode, toStatus)
                .set(ProjectReportDO::getSubmitTime, submitTime)
                .set(ProjectReportDO::getApprovalTime, null)
                .set(ProjectReportDO::getApprovalComment, null)
                .set(ProjectReportDO::getLastStatusReason, null)
                .set(ProjectReportDO::getUpdateTime, submitTime)
                .set(ProjectReportDO::getUpdater, updater)
                .eq(ProjectReportDO::getId, id)
                .eq(ProjectReportDO::getStatusCode, fromStatus));
    }

    default int updateApprovalFieldsByIdAndStatus(Long id, String fromStatus, String toStatus,
            LocalDateTime approvalTime, String approvalComment,
            String lastStatusReason, String updater) {
        return update(null, new LambdaUpdateWrapper<ProjectReportDO>()
                .set(ProjectReportDO::getStatusCode, toStatus)
                .set(ProjectReportDO::getApprovalTime, approvalTime)
                .set(ProjectReportDO::getApprovalComment, approvalComment)
                .set(ProjectReportDO::getLastStatusReason, lastStatusReason)
                .set(ProjectReportDO::getUpdateTime, approvalTime)
                .set(ProjectReportDO::getUpdater, updater)
                .eq(ProjectReportDO::getId, id)
                .eq(ProjectReportDO::getStatusCode, fromStatus));
    }

    default int updateByIdAndStatusesAndReporterId(ProjectReportDO update, Long id, Collection<String> statuses,
            Long reporterId) {
        return update(update, new LambdaQueryWrapperX<ProjectReportDO>()
                .eq(ProjectReportDO::getId, id)
                .eq(ProjectReportDO::getProjectOwnerId, reporterId)
                .in(ProjectReportDO::getStatusCode, statuses));
    }

    default int deleteByIdAndStatusesAndReporterId(Long id, Collection<String> statuses, Long reporterId) {
        return delete(new LambdaQueryWrapperX<ProjectReportDO>()
                .eq(ProjectReportDO::getId, id)
                .eq(ProjectReportDO::getProjectOwnerId, reporterId)
                .in(ProjectReportDO::getStatusCode, statuses));
    }

    default List<ProjectReportDO> selectListByPendingSubmitReminder(LocalDateTime cutoffTime,
            Collection<String> statusCodes) {
        if (cutoffTime == null || statusCodes == null || statusCodes.isEmpty()) {
            return List.of();
        }
        return selectList(new LambdaQueryWrapperX<ProjectReportDO>()
                .in(ProjectReportDO::getStatusCode, statusCodes)
                .le(ProjectReportDO::getCreateTime, cutoffTime)
                .orderByAsc(ProjectReportDO::getCreateTime)
                .orderByAsc(ProjectReportDO::getId));
    }

    private LambdaQueryWrapperX<ProjectReportDO> buildPageQuery(ProjectReportPageReqVO reqVO) {
        LambdaQueryWrapperX<ProjectReportDO> wrapper = new LambdaQueryWrapperX<ProjectReportDO>()
                .eqIfPresent(ProjectReportDO::getProjectId, reqVO.getProjectId())
                .eqIfPresent(ProjectReportDO::getFlag, reqVO.getFlag())
                .eqIfPresent(ProjectReportDO::getStatusCode, reqVO.getStatusCode())
                .betweenIfPresent(ProjectReportDO::getPeriodStartDate, reqVO.getPeriodStartDate())
                .betweenIfPresent(ProjectReportDO::getSubmitTime, reqVO.getSubmitTime());
        if (StringUtils.hasText(reqVO.getKeyword())) {
            wrapper.and(w -> w.like(ProjectReportDO::getProjectName, reqVO.getKeyword())
                    .or()
                    .like(ProjectReportDO::getPeriodLabel, reqVO.getKeyword())
                    .or()
                    .like(ProjectReportDO::getProjectOwnerName, reqVO.getKeyword())
                    .or()
                    .like(ProjectReportDO::getSupervisorName, reqVO.getKeyword()));
        }
        return wrapper;
    }
}
