package com.njcn.rdms.module.project.service.project.task;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.module.project.constant.ProjectObjectConstants;
import com.njcn.rdms.module.project.constant.ProjectTaskConstants;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.ProjectTaskBoardPageRespVO;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.ProjectTaskRespVO;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.ProjectTaskStatusBoardRespVO;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.aggregate.ProjectTaskAggregateBoardPageReqVO;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.aggregate.ProjectTaskAggregatePageReqVO;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.aggregate.ProjectTaskAggregateStatusBoardReqVO;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.aggregate.ProjectTaskSummaryReqVO;
import com.njcn.rdms.module.project.controller.admin.project.task.vo.aggregate.ProjectTaskSummaryRespVO;
import com.njcn.rdms.module.project.dal.dataobject.project.task.ProjectTaskDO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import com.njcn.rdms.module.project.dal.mysql.project.task.ProjectTaskMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
import com.njcn.rdms.module.project.framework.security.annotation.CheckObjectPermission;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ProjectTaskAggregateServiceImpl implements ProjectTaskAggregateService {

    private static final ZoneId SERVER_ZONE = ZoneId.of("Asia/Shanghai");

    @Resource
    private ProjectTaskMapper projectTaskMapper;
    @Resource
    private ProjectTaskService projectTaskService;
    @Resource
    private ObjectStatusModelMapper objectStatusModelMapper;

    // ========= 公共 helper =========

    private LocalDate today() {
        return LocalDate.now(SERVER_ZONE);
    }

    private LocalDate weekStart(LocalDate today) {
        return today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
    }

    private LocalDate weekEnd(LocalDate today) {
        return today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
    }

    private List<String> loadTerminalStatusCodes() {
        return objectStatusModelMapper.selectTerminalStatusCodesByObjectTypeEnabled(ProjectTaskConstants.OBJECT_TYPE);
    }

    // ========= page =========

    @Override
    @CheckObjectPermission(objectType = ProjectObjectConstants.OBJECT_TYPE, objectId = "#projectId", accessible = true)
    public PageResult<ProjectTaskRespVO> getAggregateTaskPage(Long projectId, ProjectTaskAggregatePageReqVO reqVO) {
        // 空数组语义短路:前端明确"按 0 个执行状态/执行 id 过滤" → 返空集合,不让 MyBatis 退化成"不过滤"
        if ((reqVO.getExecutionStatusCodes() != null && reqVO.getExecutionStatusCodes().isEmpty())
                || (reqVO.getExecutionIds() != null && reqVO.getExecutionIds().isEmpty())) {
            return PageResult.empty();
        }
        LocalDate today = today();
        LocalDate weekStart = weekStart(today);
        LocalDate weekEnd = weekEnd(today);
        List<String> terminalStatusCodes = loadTerminalStatusCodes();

        Page<ProjectTaskDO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<ProjectTaskDO> ipage = projectTaskMapper.selectAggregatePageByProjectId(
                projectId, reqVO, terminalStatusCodes, weekStart, weekEnd, today, page);
        PageResult<ProjectTaskDO> doPage = new PageResult<>(ipage.getRecords(), ipage.getTotal());
        return projectTaskService.assembleTaskRespVOPageCrossExecution(projectId, doPage);
    }

    // ========= status-board =========

    @Override
    @CheckObjectPermission(objectType = ProjectObjectConstants.OBJECT_TYPE, objectId = "#projectId", accessible = true)
    public ProjectTaskStatusBoardRespVO getAggregateTaskStatusBoard(Long projectId, ProjectTaskAggregateStatusBoardReqVO reqVO) {
        // 空数组语义短路:跳过 SQL,但保留状态列骨架(前端看板依赖全列表渲染),count 全部 0
        if ((reqVO.getExecutionStatusCodes() != null && reqVO.getExecutionStatusCodes().isEmpty())
                || (reqVO.getExecutionIds() != null && reqVO.getExecutionIds().isEmpty())) {
            List<ObjectStatusModelDO> emptyStatusModels =
                    objectStatusModelMapper.selectListByObjectTypeEnabled(ProjectTaskConstants.OBJECT_TYPE);
            return buildStatusBoardResponse(emptyStatusModels, Collections.emptyMap());
        }
        LocalDate today = today();
        LocalDate weekStart = weekStart(today);
        LocalDate weekEnd = weekEnd(today);
        List<String> terminalStatusCodes = loadTerminalStatusCodes();

        List<ObjectStatusModelDO> statusModels =
                objectStatusModelMapper.selectListByObjectTypeEnabled(ProjectTaskConstants.OBJECT_TYPE);

        List<ProjectTaskMapper.StatusCountRow> rows = projectTaskMapper.selectAggregateStatusCount(
                projectId, reqVO, terminalStatusCodes, weekStart, weekEnd, today);
        Map<String, Long> countMap = rows.stream()
                .collect(Collectors.toMap(
                        ProjectTaskMapper.StatusCountRow::getStatusCode,
                        ProjectTaskMapper.StatusCountRow::getCount));

        return buildStatusBoardResponse(statusModels, countMap);
    }

    /**
     * 按状态模型全量列出，count=0 的状态也输出（前端看板始终显示所有列）。
     */
    private ProjectTaskStatusBoardRespVO buildStatusBoardResponse(
            List<ObjectStatusModelDO> statusModels, Map<String, Long> countMap) {
        List<ProjectTaskStatusBoardRespVO.ProjectStatusBoardItemVO> items = statusModels.stream()
                .map(sm -> {
                    ProjectTaskStatusBoardRespVO.ProjectStatusBoardItemVO item =
                            new ProjectTaskStatusBoardRespVO.ProjectStatusBoardItemVO();
                    item.setStatusCode(sm.getStatusCode());
                    item.setStatusName(sm.getStatusName());
                    item.setCount(countMap.getOrDefault(sm.getStatusCode(), 0L));
                    item.setSort(sm.getSort());
                    item.setTerminal(sm.getTerminalFlag());
                    return item;
                })
                .collect(Collectors.toList());

        long total = items.stream().mapToLong(i -> i.getCount() == null ? 0L : i.getCount()).sum();

        ProjectTaskStatusBoardRespVO resp = new ProjectTaskStatusBoardRespVO();
        resp.setTotal(total);
        resp.setItems(items);
        return resp;
    }

    // ========= board-page =========

    @Override
    @CheckObjectPermission(objectType = ProjectObjectConstants.OBJECT_TYPE, objectId = "#projectId", accessible = true)
    public ProjectTaskBoardPageRespVO getAggregateTaskBoardPage(Long projectId, ProjectTaskAggregateBoardPageReqVO reqVO) {
        // 空数组语义短路:executionStatusCodes 或 executionIds 为空数组 → 该范围明确为空,跳过 SQL,保留列骨架(每列 list=[] / total=0)
        boolean emptyExecScope = (reqVO.getExecutionStatusCodes() != null && reqVO.getExecutionStatusCodes().isEmpty())
                || (reqVO.getExecutionIds() != null && reqVO.getExecutionIds().isEmpty());

        List<ObjectStatusModelDO> allStatusModels =
                objectStatusModelMapper.selectListByObjectTypeEnabled(ProjectTaskConstants.OBJECT_TYPE);
        List<ObjectStatusModelDO> targetStatusModels;
        if (reqVO.getStatusCodes() == null || reqVO.getStatusCodes().isEmpty()) {
            targetStatusModels = allStatusModels;
        } else {
            Set<String> filter = new HashSet<>(reqVO.getStatusCodes());
            targetStatusModels = allStatusModels.stream()
                    .filter(sm -> filter.contains(sm.getStatusCode()))
                    .collect(Collectors.toList());
        }

        if (emptyExecScope) {
            List<ProjectTaskBoardPageRespVO.ColumnItemVO> emptyItems = targetStatusModels.stream()
                    .map(sm -> buildColumnItemVO(sm, PageResult.empty()))
                    .collect(Collectors.toList());
            ProjectTaskBoardPageRespVO emptyResp = new ProjectTaskBoardPageRespVO();
            emptyResp.setItems(emptyItems);
            return emptyResp;
        }

        LocalDate today = today();
        LocalDate weekStart = weekStart(today);
        LocalDate weekEnd = weekEnd(today);
        List<String> terminalStatusCodes = loadTerminalStatusCodes();

        List<ProjectTaskBoardPageRespVO.ColumnItemVO> items = targetStatusModels.stream()
                .map(sm -> buildAggregateBoardColumn(projectId, reqVO, sm,
                        terminalStatusCodes, today, weekStart, weekEnd))
                .collect(Collectors.toList());

        ProjectTaskBoardPageRespVO resp = new ProjectTaskBoardPageRespVO();
        resp.setItems(items);
        return resp;
    }

    private ProjectTaskBoardPageRespVO.ColumnItemVO buildAggregateBoardColumn(
            Long projectId,
            ProjectTaskAggregateBoardPageReqVO reqVO, ObjectStatusModelDO sm,
            List<String> terminalStatusCodes, LocalDate today, LocalDate weekStart, LocalDate weekEnd) {

        // 复用 selectAggregatePageByProjectId，构造单列过滤的 innerReq
        ProjectTaskAggregatePageReqVO innerReq = new ProjectTaskAggregatePageReqVO();
        innerReq.setPageNo(reqVO.getPageNo());
        innerReq.setPageSize(reqVO.getPageSize());
        innerReq.setKeyword(reqVO.getKeyword());
        innerReq.setExecutionIds(reqVO.getExecutionIds());
        innerReq.setExecutionStatusCodes(reqVO.getExecutionStatusCodes());
        innerReq.setInvolveUserId(reqVO.getInvolveUserId());
        innerReq.setExecutionInvolveUserId(reqVO.getExecutionInvolveUserId());
        innerReq.setOwnerId(reqVO.getOwnerId());
        innerReq.setStatusCodes(Collections.singletonList(sm.getStatusCode()));
        innerReq.setPriority(reqVO.getPriority());
        innerReq.setParentTaskId(reqVO.getParentTaskId());
        innerReq.setDueRange(reqVO.getDueRange());
        innerReq.setUpdateTime(reqVO.getUpdateTime());
        // board-page 不传 sortBy / sortOrder，用 page 查询的默认排序（plannedEndDate ASC）

        Page<ProjectTaskDO> page = new Page<>(innerReq.getPageNo(), innerReq.getPageSize());
        IPage<ProjectTaskDO> ipage = projectTaskMapper.selectAggregatePageByProjectId(
                projectId, innerReq, terminalStatusCodes, weekStart, weekEnd, today, page);
        PageResult<ProjectTaskDO> doPage = new PageResult<>(ipage.getRecords(), ipage.getTotal());
        PageResult<ProjectTaskRespVO> voPage =
                projectTaskService.assembleTaskRespVOPageCrossExecution(projectId, doPage);

        return buildColumnItemVO(sm, voPage);
    }

    private ProjectTaskBoardPageRespVO.ColumnItemVO buildColumnItemVO(
            ObjectStatusModelDO sm, PageResult<ProjectTaskRespVO> voPage) {
        ProjectTaskBoardPageRespVO.ColumnItemVO col = new ProjectTaskBoardPageRespVO.ColumnItemVO();
        col.setStatusCode(sm.getStatusCode());
        col.setStatusName(sm.getStatusName());
        col.setSort(sm.getSort());
        col.setTerminal(sm.getTerminalFlag());
        col.setList(voPage.getList());
        col.setTotal(voPage.getTotal());
        return col;
    }

    // ========= summary =========

    @Override
    @CheckObjectPermission(objectType = ProjectObjectConstants.OBJECT_TYPE, objectId = "#projectId", accessible = true)
    public ProjectTaskSummaryRespVO getAggregateTaskSummary(Long projectId, ProjectTaskSummaryReqVO reqVO) {
        LocalDate today = today();
        LocalDate weekStart = weekStart(today);
        LocalDate weekEnd = weekEnd(today);
        List<String> terminalStatusCodes = loadTerminalStatusCodes();

        Map<String, Long> counts = projectTaskMapper.selectAggregateSummaryCounts(
                projectId,
                reqVO.getInvolveUserId(),
                terminalStatusCodes, ProjectTaskConstants.STATUS_COMPLETED,
                today, weekStart, weekEnd);

        ProjectTaskSummaryRespVO resp = new ProjectTaskSummaryRespVO();
        resp.setOverdue(zeroIfNull(counts.get("overdue")));
        resp.setDueToday(zeroIfNull(counts.get("dueToday")));
        resp.setDueThisWeek(zeroIfNull(counts.get("dueThisWeek")));
        resp.setDoneThisWeek(zeroIfNull(counts.get("doneThisWeek")));
        resp.setToday(today);
        resp.setWeekStart(weekStart);
        resp.setWeekEnd(weekEnd);
        return resp;
    }

    private Long zeroIfNull(Long v) {
        return v == null ? 0L : v;
    }
}
