package com.yimeng.okr.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yimeng.okr.dto.OkrDetailDto;
import com.yimeng.okr.dto.OkrDetailDto.KrWithProgress;
import com.yimeng.okr.entity.*;
import com.yimeng.okr.enums.KrStatus;
import com.yimeng.okr.enums.PeriodStatus;
import com.yimeng.okr.enums.UserRole;
import com.yimeng.okr.event.NotificationEvent;
import com.yimeng.okr.exception.BusinessException;
import com.yimeng.okr.mapper.*;
import com.yimeng.okr.security.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class OkrService {

    private final OkrObjectiveMapper objectiveMapper;
    private final KrKeyResultMapper krMapper;
    private final KrProgressLogMapper progressLogMapper;
    private final AssessmentPeriodMapper periodMapper;
    private final SysUserMapper userMapper;
    private final SysDepartmentMapper departmentMapper;
    private final AuthorizationService authorizationService;
    private final ApplicationEventPublisher publisher;

    @Transactional
    public OkrObjective submitOkr(Long periodId, Long userId, String title,
                                   List<KrKeyResult> krs, List<Long> alignedObjectiveIds) {
        AssessmentPeriod period = periodMapper.selectById(periodId);
        if (period == null || period.getStatus() != PeriodStatus.OKR_ALIGN) {
            throw new BusinessException("当前周期不在OKR对齐阶段");
        }

        // Enforce 1O + 2~5 KR (3 recommended)
        if (krs == null || krs.size() < 2 || krs.size() > 5) {
            throw new BusinessException("个人OKR需配置2~5条关键结果(KR)，推荐3条，当前为" + (krs == null ? 0 : krs.size()) + "条");
        }
        // Validate KR weights sum to 100
        int weightSum = 0;
        for (KrKeyResult kr : krs) {
            weightSum += (kr.getWeight() != null ? kr.getWeight() : 0);
        }
        if (weightSum != 100) {
            throw new BusinessException("KR权重之和必须为100，当前为" + weightSum);
        }
        if (title == null || title.trim().isEmpty()) {
            throw new BusinessException("请填写目标(O)");
        }

        // Check cascading: superior must have approved OKRs first
        SysUser user = userMapper.selectById(userId);
        if (user != null && user.getSuperiorId() != null && user.getSuperiorId() > 0) {
            Long superiorId = user.getSuperiorId();
            long superiorApproved = objectiveMapper.selectCount(
                    new LambdaQueryWrapper<OkrObjective>()
                            .eq(OkrObjective::getPeriodId, periodId)
                            .eq(OkrObjective::getUserId, superiorId)
                            .eq(OkrObjective::getStatus, "APPROVED"));
            if (superiorApproved == 0) {
                SysUser superior = userMapper.selectById(superiorId);
                String name = superior != null ? superior.getRealName() : "上级";
                throw new BusinessException("请等待" + name + "先完成 OKR 制定后再提交");
            }
        }

        // Check duplicate
        long existingCount = objectiveMapper.selectCount(
                new LambdaQueryWrapper<OkrObjective>()
                        .eq(OkrObjective::getPeriodId, periodId)
                        .eq(OkrObjective::getUserId, userId)
                        .ne(OkrObjective::getStatus, "REJECTED"));
        if (existingCount > 0) {
            throw new BusinessException("你已提交过本周期 OKR，如需修改请先由上级驳回");
        }

        // Validate aligned objectives belong to superior
        if (alignedObjectiveIds != null && !alignedObjectiveIds.isEmpty() && user.getSuperiorId() != null) {
            for (Long alignedId : alignedObjectiveIds) {
                OkrObjective aligned = objectiveMapper.selectById(alignedId);
                if (aligned == null) continue;
                if (!aligned.getUserId().equals(user.getSuperiorId())) {
                    throw new BusinessException("只能对齐直属上级的 OKR");
                }
            }
        }

        // Top-level users (no superior) auto-approve
        boolean isTopLevel = user == null || user.getSuperiorId() == null || user.getSuperiorId() <= 0;
        String initialStatus = isTopLevel ? "APPROVED" : "SUBMITTED";

        OkrObjective obj = new OkrObjective();
        obj.setPeriodId(periodId);
        obj.setUserId(userId);
        obj.setDeptId(user != null ? user.getDepartmentId() : null);
        obj.setTitle(title);
        obj.setStatus(initialStatus);
        obj.setVersion(1);
        if (isTopLevel) {
            obj.setReviewedBy(userId);
            obj.setReviewedAt(LocalDateTime.now());
        }
        if (alignedObjectiveIds != null && !alignedObjectiveIds.isEmpty()) {
            obj.setAlignedObjectiveIds(alignedObjectiveIds.stream()
                    .map(String::valueOf).collect(Collectors.joining(",")));
        }
        objectiveMapper.insert(obj);

        for (KrKeyResult kr : krs) {
            kr.setObjectiveId(obj.getId());
            kr.setStatus(KrStatus.NOT_STARTED);
            krMapper.insert(kr);
        }

        if (!isTopLevel && user.getSuperiorId() != null) {
            publisher.publishEvent(new NotificationEvent(
                    this, user.getSuperiorId(), "OKR_REVIEW",
                    "新的OKR待审核", user.getRealName() + " 提交了OKR，请审核",
                    "objective", obj.getId()));
        }

        return obj;
    }

    @Transactional
    public OkrObjective submitHierarchyOkr(Long periodId, Long userId, String title,
                                            List<KrKeyResult> krs, String level,
                                            Long parentObjectiveId, List<Long> alignedObjectiveIds) {
        AssessmentPeriod period = periodMapper.selectById(periodId);
        if (period == null || period.getStatus() != PeriodStatus.OKR_ALIGN) {
            throw new BusinessException("当前周期不在OKR对齐阶段");
        }
        SysUser user = userMapper.selectById(userId);
        if (user == null) throw new BusinessException(404, "用户不存在");

        if ("COMPANY".equals(level) && !SecurityUtils.hasRole(UserRole.HR_ADMIN)) {
            throw new BusinessException(403, "仅HR管理员可创建公司级OKR");
        }
        if ("DEPARTMENT".equals(level) && !SecurityUtils.hasRole(UserRole.DEPT_LEADER)) {
            throw new BusinessException(403, "仅部门负责人可创建部门级OKR");
        }

        if ("INDIVIDUAL".equals(level)) {
            if (krs == null || krs.size() < 2 || krs.size() > 5) {
                throw new BusinessException("个人OKR需配置2~5条KR");
            }
            // Check cascading: superior must have approved OKRs first
            if (user.getSuperiorId() != null && user.getSuperiorId() > 0) {
                Long superiorId = user.getSuperiorId();
                long superiorApproved = objectiveMapper.selectCount(
                        new LambdaQueryWrapper<OkrObjective>()
                                .eq(OkrObjective::getPeriodId, periodId)
                                .eq(OkrObjective::getUserId, superiorId)
                                .eq(OkrObjective::getStatus, "APPROVED"));
                if (superiorApproved == 0) {
                    SysUser superior = userMapper.selectById(superiorId);
                    String name = superior != null ? superior.getRealName() : "上级";
                    throw new BusinessException("请等待" + name + "先完成 OKR 制定后再提交");
                }
            }
            long existingCount = objectiveMapper.selectCount(
                    new LambdaQueryWrapper<OkrObjective>()
                            .eq(OkrObjective::getPeriodId, periodId)
                            .eq(OkrObjective::getUserId, userId)
                            .eq(OkrObjective::getLevel, "INDIVIDUAL")
                            .ne(OkrObjective::getStatus, "REJECTED"));
            if (existingCount > 0) {
                throw new BusinessException("你已提交过本周期个人OKR，如需修改请先由上级驳回");
            }
        }

        int weightSum = krs.stream().mapToInt(k -> k.getWeight() != null ? k.getWeight() : 0).sum();
        if (weightSum != 100) throw new BusinessException("KR权重之和必须为100");

        if (parentObjectiveId != null) {
            OkrObjective parent = objectiveMapper.selectById(parentObjectiveId);
            if (parent == null) throw new BusinessException(404, "对齐目标不存在");
            if (!Objects.equals(parent.getPeriodId(), periodId)) {
                throw new BusinessException("只能对齐同一考核周期内的OKR");
            }
            authorizationService.requireCanViewOkr(userId, parent);
        }
        if (alignedObjectiveIds != null && !alignedObjectiveIds.isEmpty()) {
            for (Long alignedId : alignedObjectiveIds) {
                OkrObjective aligned = objectiveMapper.selectById(alignedId);
                if (aligned == null) throw new BusinessException(404, "对齐目标不存在");
                if (!Objects.equals(aligned.getPeriodId(), periodId)) {
                    throw new BusinessException("只能对齐同一考核周期内的OKR");
                }
                authorizationService.requireCanViewOkr(userId, aligned);
            }
        }

        OkrObjective obj = new OkrObjective();
        obj.setPeriodId(periodId);
        obj.setUserId(userId);
        obj.setDeptId(user.getDepartmentId());
        obj.setTitle(title);
        obj.setLevel(level);
        obj.setParentObjectiveId(parentObjectiveId);
        obj.setSortOrder(0);
        obj.setProgress(0.0);
        obj.setVersion(1);
        boolean isTopLevel = user.getSuperiorId() == null || user.getSuperiorId() <= 0;
        if ("INDIVIDUAL".equals(level)) {
            obj.setStatus(isTopLevel ? "APPROVED" : "SUBMITTED");
        } else {
            obj.setStatus("APPROVED");
        }
        if (!"INDIVIDUAL".equals(level) || isTopLevel) {
            obj.setReviewedBy(userId);
            obj.setReviewedAt(LocalDateTime.now());
        }
        if (alignedObjectiveIds != null && !alignedObjectiveIds.isEmpty()) {
            obj.setAlignedObjectiveIds(alignedObjectiveIds.stream()
                    .map(String::valueOf).collect(Collectors.joining(",")));
        }
        objectiveMapper.insert(obj);

        for (KrKeyResult kr : krs) {
            kr.setObjectiveId(obj.getId());
            kr.setStatus(KrStatus.NOT_STARTED);
            krMapper.insert(kr);
        }
        if ("INDIVIDUAL".equals(level) && !isTopLevel && user.getSuperiorId() != null) {
            publisher.publishEvent(new NotificationEvent(
                    this, user.getSuperiorId(), "OKR_REVIEW",
                    "新的OKR待审核", user.getRealName() + " 提交了OKR，请审核",
                    "objective", obj.getId()));
        }
        return obj;
    }

    @Transactional
    public OkrObjective updateMyOkr(Long objectiveId, Long userId, String title,
                                      List<KrKeyResult> krs, List<Long> alignedObjectiveIds) {
        OkrObjective obj = objectiveMapper.selectById(objectiveId);
        if (obj == null) throw new BusinessException(404, "OKR不存在");
        if (!obj.getUserId().equals(userId)) throw new BusinessException("只能修改自己的OKR");

        AssessmentPeriod period = periodMapper.selectById(obj.getPeriodId());
        if (period == null || period.getStatus() == PeriodStatus.EXECUTING
                || period.getStatus() == PeriodStatus.ASSESSING
                || period.getStatus() == PeriodStatus.ARCHIVED) {
            throw new BusinessException("OKR已锁定，当前阶段不可编辑");
        }

        if (title == null || title.trim().isEmpty()) throw new BusinessException("请填写目标(O)");
        if (krs == null || krs.size() < 2 || krs.size() > 5)
            throw new BusinessException("需配置2~5条KR，当前为" + (krs == null ? 0 : krs.size()) + "条");
        int weightSum = krs.stream().mapToInt(k -> k.getWeight() != null ? k.getWeight() : 0).sum();
        if (weightSum != 100) throw new BusinessException("KR权重之和必须为100，当前为" + weightSum);

        SysUser user = userMapper.selectById(userId);
        boolean isTopLevel = user == null || user.getSuperiorId() == null || user.getSuperiorId() <= 0;
        obj.setTitle(title);
        obj.setStatus(isTopLevel ? "APPROVED" : "SUBMITTED");
        if (isTopLevel) { obj.setReviewedBy(userId); obj.setReviewedAt(LocalDateTime.now()); }
        obj.setVersion((obj.getVersion() != null ? obj.getVersion() : 1) + 1);
        if (alignedObjectiveIds != null && !alignedObjectiveIds.isEmpty()) {
            obj.setAlignedObjectiveIds(alignedObjectiveIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
        } else {
            obj.setAlignedObjectiveIds(null);
        }
        objectiveMapper.updateById(obj);

        // Delete old KRs and re-insert
        krMapper.delete(new LambdaQueryWrapper<KrKeyResult>().eq(KrKeyResult::getObjectiveId, objectiveId));
        for (KrKeyResult kr : krs) {
            kr.setObjectiveId(objectiveId);
            kr.setStatus(KrStatus.NOT_STARTED);
            krMapper.insert(kr);
        }
        return obj;
    }

    @Transactional
    public void reviewOkr(Long objectiveId, boolean approved, String remark, Long reviewerId) {
        OkrObjective obj = objectiveMapper.selectById(objectiveId);
        if (obj == null) throw new BusinessException(404, "OKR不存在");
        if (!"SUBMITTED".equals(obj.getStatus())) {
            throw new BusinessException("该OKR不在待审核状态");
        }

        // Permission: reviewer must be the OKR owner's superior, or dept leader (but not self)
        SysUser owner = userMapper.selectById(obj.getUserId());
        if (owner == null) throw new BusinessException(404, "OKR属主不存在");
        if (reviewerId.equals(obj.getUserId())) {
            throw new BusinessException("不能审核自己的OKR");
        }
        boolean isSuperior = reviewerId.equals(owner.getSuperiorId());
        boolean isDeptLeader = false;
        if (owner.getDepartmentId() != null) {
            SysDepartment dept = departmentMapper.selectById(owner.getDepartmentId());
            isDeptLeader = dept != null && reviewerId.equals(dept.getLeaderId());
        }
        if (!isSuperior && !isDeptLeader) {
            throw new BusinessException("你无权审核此OKR");
        }

        obj.setStatus(approved ? "APPROVED" : "REJECTED");
        obj.setReviewedBy(reviewerId);
        obj.setReviewedAt(LocalDateTime.now());
        objectiveMapper.updateById(obj);

        String result = approved ? "已通过" : "已驳回";
        publisher.publishEvent(new NotificationEvent(
                this, obj.getUserId(), "OKR_REVIEWED",
                "OKR审核" + result, "你的OKR已被" + result + (remark != null && !remark.isEmpty() ? "。备注：" + remark : ""),
                "objective", obj.getId()));
    }

    @Transactional
    public void updateKrProgress(Long krId, Double newValue, KrStatus newStatus, String remark, Long userId) {
        KrKeyResult kr = krMapper.selectById(krId);
        if (kr == null) throw new BusinessException(404, "KR不存在");
        OkrObjective obj = objectiveMapper.selectById(kr.getObjectiveId());
        if (obj == null) throw new BusinessException(404, "OKR目标不存在");
        AssessmentPeriod period = periodMapper.selectById(obj.getPeriodId());
        if (period == null || period.getStatus() != PeriodStatus.EXECUTING) {
            throw new BusinessException("当前周期不在执行阶段");
        }
        boolean isOwner = Objects.equals(userId, obj.getUserId());
        boolean isAssignee = kr.getAssignedTo() != null && Objects.equals(userId, kr.getAssignedTo());
        if (!isOwner && !isAssignee) {
            throw new BusinessException(403, "无权更新该KR进度");
        }
        KrProgressLog log = new KrProgressLog();
        log.setKrId(krId);
        log.setOldValue(kr.getCurrentValue());
        log.setNewValue(newValue);
        log.setOldStatus(kr.getStatus());
        log.setNewStatus(newStatus);
        log.setRemark(remark);
        log.setUpdatedBy(userId);
        progressLogMapper.insert(log);
        kr.setCurrentValue(newValue);
        kr.setStatus(newStatus);
        krMapper.updateById(kr);

        // Recalculate O progress
        List<KrKeyResult> allKrs = krMapper.selectList(
                new LambdaQueryWrapper<KrKeyResult>().eq(KrKeyResult::getObjectiveId, kr.getObjectiveId()));
        double totalWeight = allKrs.stream().mapToDouble(k -> k.getWeight() != null ? k.getWeight() : 0).sum();
        double weightedProgress = 0;
        if (totalWeight > 0) {
            for (KrKeyResult k : allKrs) {
                double krProgress = k.getTargetValue() > 0
                        ? (k.getCurrentValue() / k.getTargetValue()) * 100 : 0;
                weightedProgress += krProgress * (k.getWeight() / totalWeight);
            }
        }
        obj.setProgress(Math.min(100, Math.round(weightedProgress * 10.0) / 10.0));
        objectiveMapper.updateById(obj);
    }

    // --- Queries ---

    public List<OkrDetailDto> getSuperiorOkrs(Long periodId, Long userId) {
        SysUser user = userMapper.selectById(userId);
        if (user == null || user.getSuperiorId() == null || user.getSuperiorId() <= 0) {
            return List.of(); // no superior = top level, nothing to align to
        }
        List<OkrObjective> objectives = objectiveMapper.selectList(
                new LambdaQueryWrapper<OkrObjective>()
                        .eq(OkrObjective::getPeriodId, periodId)
                        .eq(OkrObjective::getUserId, user.getSuperiorId())
                        .eq(OkrObjective::getStatus, "APPROVED"));
        return enrichObjectives(objectives);
    }

    public List<OkrDetailDto> getTopLevelOkrs(Long periodId) {
        List<SysUser> topUsers = userMapper.selectList(
                new LambdaQueryWrapper<SysUser>()
                        .isNull(SysUser::getSuperiorId).or().eq(SysUser::getSuperiorId, 0));
        if (topUsers.isEmpty()) return List.of();
        List<Long> topIds = topUsers.stream().map(SysUser::getId).toList();
        List<OkrObjective> objectives = objectiveMapper.selectList(
                new LambdaQueryWrapper<OkrObjective>()
                        .eq(OkrObjective::getPeriodId, periodId)
                        .in(OkrObjective::getUserId, topIds)
                        .eq(OkrObjective::getStatus, "APPROVED"));
        return enrichObjectives(objectives);
    }

    public OkrDetailDto getMyOkr(Long periodId, Long userId) {
        OkrObjective obj = objectiveMapper.selectOne(
                new LambdaQueryWrapper<OkrObjective>()
                        .eq(OkrObjective::getPeriodId, periodId)
                        .eq(OkrObjective::getUserId, userId)
                        .ne(OkrObjective::getStatus, "REJECTED")
                        .orderByDesc(OkrObjective::getCreatedAt)
                        .last("LIMIT 1"));
        if (obj == null) return null;
        return enrichObjectives(List.of(obj)).get(0);
    }

    public boolean canSubmitOkr(Long periodId, Long userId) {
        SysUser user = userMapper.selectById(userId);
        if (user == null) return false;
        // Top-level users can always submit
        if (user.getSuperiorId() == null || user.getSuperiorId() <= 0) return true;
        // Others: superior must have approved OKR first
        long count = objectiveMapper.selectCount(
                new LambdaQueryWrapper<OkrObjective>()
                        .eq(OkrObjective::getPeriodId, periodId)
                        .eq(OkrObjective::getUserId, user.getSuperiorId())
                        .eq(OkrObjective::getStatus, "APPROVED"));
        return count > 0;
    }

    public List<OkrDetailDto> getSubordinateOkrs(Long periodId, Long superiorId) {
        List<SysUser> subs = userMapper.selectList(
                new LambdaQueryWrapper<SysUser>().eq(SysUser::getSuperiorId, superiorId));
        if (subs.isEmpty()) return List.of();
        List<Long> subIds = subs.stream().map(SysUser::getId).toList();
        List<OkrObjective> objectives = objectiveMapper.selectList(
                new LambdaQueryWrapper<OkrObjective>()
                        .eq(OkrObjective::getPeriodId, periodId)
                        .in(OkrObjective::getUserId, subIds));
        return enrichObjectives(objectives);
    }

    public List<OkrDetailDto> getDeptMemberOkrs(Long periodId, Long deptId, Long actorId) {
        authorizationService.requireCanViewDepartment(actorId, deptId);
        List<SysUser> members = userMapper.selectList(
                new LambdaQueryWrapper<SysUser>().eq(SysUser::getDepartmentId, deptId));
        if (members.isEmpty()) return List.of();
        List<Long> memberIds = members.stream().map(SysUser::getId).toList();
        List<OkrObjective> objectives = objectiveMapper.selectList(
                new LambdaQueryWrapper<OkrObjective>()
                        .eq(OkrObjective::getPeriodId, periodId)
                        .in(OkrObjective::getUserId, memberIds));
        return enrichObjectives(objectives);
    }

    public List<OkrDetailDto> getPendingReviews(Long periodId, Long reviewerId) {
        List<SysUser> directSubs = userMapper.selectList(
                new LambdaQueryWrapper<SysUser>().eq(SysUser::getSuperiorId, reviewerId));
        List<SysDepartment> ledDepts = departmentMapper.selectList(
                new LambdaQueryWrapper<SysDepartment>().eq(SysDepartment::getLeaderId, reviewerId));
        Set<Long> reviewableUserIds = new LinkedHashSet<>();
        directSubs.forEach(u -> reviewableUserIds.add(u.getId()));
        for (SysDepartment dept : ledDepts) {
            userMapper.selectList(
                    new LambdaQueryWrapper<SysUser>().eq(SysUser::getDepartmentId, dept.getId()))
                    .forEach(u -> reviewableUserIds.add(u.getId()));
        }
        if (reviewableUserIds.isEmpty()) return List.of();
        List<OkrObjective> objectives = objectiveMapper.selectList(
                new LambdaQueryWrapper<OkrObjective>()
                        .eq(OkrObjective::getPeriodId, periodId)
                        .in(OkrObjective::getUserId, new ArrayList<>(reviewableUserIds))
                        .eq(OkrObjective::getStatus, "SUBMITTED"));
        return enrichObjectives(objectives);
    }

    public OkrDetailDto getOkrDetail(Long objectiveId, Long actorId) {
        OkrObjective obj = objectiveMapper.selectById(objectiveId);
        if (obj == null) throw new BusinessException(404, "OKR不存在");
        authorizationService.requireCanViewOkr(actorId, obj);
        return enrichObjectives(List.of(obj)).get(0);
    }

    public List<KrProgressLog> getKrProgressHistory(Long krId, Long actorId) {
        KrKeyResult kr = krMapper.selectById(krId);
        if (kr == null) throw new BusinessException(404, "KR不存在");
        OkrObjective obj = objectiveMapper.selectById(kr.getObjectiveId());
        if (obj == null) throw new BusinessException(404, "OKR目标不存在");
        authorizationService.requireCanViewOkr(actorId, obj);
        return progressLogMapper.selectList(
                new LambdaQueryWrapper<KrProgressLog>()
                        .eq(KrProgressLog::getKrId, krId)
                        .orderByDesc(KrProgressLog::getCreatedAt));
    }

    public List<OkrDetailDto> getAllOkrsForPeriod(Long periodId, Long actorId) {
        List<OkrObjective> objectives = objectiveMapper.selectList(
                new LambdaQueryWrapper<OkrObjective>().eq(OkrObjective::getPeriodId, periodId));
        return enrichObjectives(filterVisibleObjectives(objectives, actorId));
    }

    public List<OkrDetailDto> getDeptOkrsForPeriod(Long periodId, Long deptId) {
        return List.of(); // deprecated
    }

    public List<OkrDetailDto> getOkrTree(Long periodId, Long actorId) {
        List<OkrObjective> all = objectiveMapper.selectList(
                new LambdaQueryWrapper<OkrObjective>().eq(OkrObjective::getPeriodId, periodId));
        List<OkrDetailDto> enriched = enrichObjectives(filterVisibleObjectives(all, actorId));

        Map<Long, OkrDetailDto> map = enriched.stream()
                .collect(Collectors.toMap(OkrDetailDto::getId, o -> o, (a, b) -> a));
        List<OkrDetailDto> roots = new ArrayList<>();

        for (OkrDetailDto dto : enriched) {
            if (dto.getParentObjectiveId() == null || "COMPANY".equals(dto.getLevel())) {
                roots.add(dto);
            } else {
                OkrDetailDto parent = map.get(dto.getParentObjectiveId());
                if (parent != null) {
                    if (parent.getChildren() == null) parent.setChildren(new ArrayList<>());
                    parent.getChildren().add(dto);
                } else {
                    roots.add(dto);
                }
            }
        }
        return roots;
    }

    public List<OkrDetailDto> getOrphanOkrs(Long periodId, Long actorId) {
        List<OkrObjective> individuals = objectiveMapper.selectList(
                new LambdaQueryWrapper<OkrObjective>()
                        .eq(OkrObjective::getPeriodId, periodId)
                        .eq(OkrObjective::getLevel, "INDIVIDUAL")
                        .isNull(OkrObjective::getParentObjectiveId));
        return enrichObjectives(filterVisibleObjectives(individuals, actorId));
    }

    public List<OkrDetailDto> getChildren(Long objectiveId, Long actorId) {
        OkrObjective parent = objectiveMapper.selectById(objectiveId);
        if (parent == null) throw new BusinessException(404, "OKR不存在");
        authorizationService.requireCanViewOkr(actorId, parent);
        List<OkrObjective> children = objectiveMapper.selectList(
                new LambdaQueryWrapper<OkrObjective>().eq(OkrObjective::getParentObjectiveId, objectiveId));
        return enrichObjectives(filterVisibleObjectives(children, actorId));
    }

    @Transactional
    public void setAlignment(Long objectiveId, Long parentId, Long actorId) {
        OkrObjective obj = objectiveMapper.selectById(objectiveId);
        if (obj == null) throw new BusinessException(404, "OKR不存在");
        if (!Objects.equals(obj.getUserId(), actorId) && !authorizationService.isHrOrAbove(actorId)) {
            throw new BusinessException(403, "只能调整自己的OKR对齐关系");
        }
        AssessmentPeriod period = periodMapper.selectById(obj.getPeriodId());
        if (period == null || period.getStatus() != PeriodStatus.OKR_ALIGN) {
            throw new BusinessException("当前周期不在OKR对齐阶段");
        }
        if (parentId != null) {
            OkrObjective parent = objectiveMapper.selectById(parentId);
            if (parent == null) throw new BusinessException(404, "对齐目标不存在");
            if (!Objects.equals(parent.getPeriodId(), obj.getPeriodId())) {
                throw new BusinessException("只能对齐同一考核周期内的OKR");
            }
            authorizationService.requireCanViewOkr(actorId, parent);
        }
        obj.setParentObjectiveId(parentId);
        objectiveMapper.updateById(obj);
    }

    private List<OkrObjective> filterVisibleObjectives(List<OkrObjective> objectives, Long actorId) {
        return objectives.stream()
                .filter(obj -> authorizationService.canViewOkr(actorId, obj))
                .toList();
    }

    // --- Helper ---

    public List<OkrDetailDto> enrichObjectives(List<OkrObjective> objectives) {
        if (objectives.isEmpty()) return List.of();
        List<Long> objIds = objectives.stream().map(OkrObjective::getId).toList();
        Map<Long, List<KrKeyResult>> krMap = krMapper.selectList(
                        new LambdaQueryWrapper<KrKeyResult>().in(KrKeyResult::getObjectiveId, objIds))
                .stream().collect(Collectors.groupingBy(KrKeyResult::getObjectiveId));
        List<Long> krIds = krMap.values().stream().flatMap(List::stream).map(KrKeyResult::getId).toList();
        Map<Long, List<KrProgressLog>> logMap = Collections.emptyMap();
        if (!krIds.isEmpty()) {
            logMap = progressLogMapper.selectList(
                            new LambdaQueryWrapper<KrProgressLog>().in(KrProgressLog::getKrId, krIds)
                                    .orderByDesc(KrProgressLog::getCreatedAt))
                    .stream().collect(Collectors.groupingBy(KrProgressLog::getKrId));
        }

        // Collect all aligned objective IDs
        Set<Long> allAlignedIds = new LinkedHashSet<>();
        for (OkrObjective obj : objectives) {
            if (obj.getAlignedObjectiveIds() != null && !obj.getAlignedObjectiveIds().isEmpty()) {
                Arrays.stream(obj.getAlignedObjectiveIds().split(","))
                        .map(String::trim).filter(s -> !s.isEmpty())
                        .map(Long::parseLong).forEach(allAlignedIds::add);
            }
        }
        List<OkrObjective> alignedObjs = List.of();
        if (!allAlignedIds.isEmpty()) {
            alignedObjs = objectiveMapper.selectBatchIds(allAlignedIds);
        }

        Set<Long> sourceKrIds = krMap.values().stream()
                .flatMap(List::stream)
                .map(KrKeyResult::getSourceKrId)
                .filter(Objects::nonNull)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        Map<Long, KrKeyResult> sourceKrMap = new LinkedHashMap<>();
        if (!sourceKrIds.isEmpty()) {
            sourceKrMap = krMapper.selectBatchIds(sourceKrIds).stream()
                    .collect(Collectors.toMap(KrKeyResult::getId, kr -> kr, (a, b) -> a, LinkedHashMap::new));
        }

        Set<Long> sourceObjectiveIds = sourceKrMap.values().stream()
                .map(KrKeyResult::getObjectiveId)
                .filter(Objects::nonNull)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        Map<Long, OkrObjective> sourceObjectiveMap = new LinkedHashMap<>();
        if (!sourceObjectiveIds.isEmpty()) {
            sourceObjectiveMap = objectiveMapper.selectBatchIds(sourceObjectiveIds).stream()
                    .collect(Collectors.toMap(OkrObjective::getId, obj -> obj, (a, b) -> a, LinkedHashMap::new));
        }

        Set<Long> userIds = new LinkedHashSet<>();
        Set<Long> deptIds = new LinkedHashSet<>();
        objectives.forEach(obj -> {
            addIfPresent(userIds, obj.getUserId());
            addIfPresent(userIds, obj.getReviewedBy());
            addIfPresent(deptIds, obj.getDeptId());
        });
        alignedObjs.forEach(obj -> addIfPresent(userIds, obj.getUserId()));
        krMap.values().stream().flatMap(List::stream)
                .forEach(kr -> addIfPresent(userIds, kr.getAssignedTo()));
        sourceObjectiveMap.values().forEach(obj -> addIfPresent(userIds, obj.getUserId()));

        Map<Long, String> userNames = userIds.isEmpty()
                ? Collections.emptyMap()
                : userMapper.selectBatchIds(userIds).stream()
                .collect(Collectors.toMap(SysUser::getId, SysUser::getRealName, (a, b) -> a));
        Map<Long, String> deptNames = deptIds.isEmpty()
                ? Collections.emptyMap()
                : departmentMapper.selectBatchIds(deptIds).stream()
                .collect(Collectors.toMap(SysDepartment::getId, SysDepartment::getName, (a, b) -> a));

        Map<Long, OkrDetailDto> alignedObjMap = new LinkedHashMap<>();
        if (!alignedObjs.isEmpty()) {
            for (OkrObjective ao : alignedObjs) {
                List<KrKeyResult> akrs = krMap.getOrDefault(ao.getId(),
                        krMapper.selectList(new LambdaQueryWrapper<KrKeyResult>().eq(KrKeyResult::getObjectiveId, ao.getId())));
                List<KrWithProgress> akrDtos = akrs.stream().map(kr -> {
                    KrWithProgress k = new KrWithProgress();
                    k.setId(kr.getId()); k.setTitle(kr.getTitle());
                    k.setTargetValue(kr.getTargetValue()); k.setCurrentValue(kr.getCurrentValue());
                    k.setStatus(kr.getStatus().name()); k.setWeight(kr.getWeight());
                    return k;
                }).toList();
                OkrDetailDto adto = OkrDetailDto.from(ao, akrDtos);
                adto.setUserName(userNames.get(ao.getUserId()));
                alignedObjMap.put(ao.getId(), adto);
            }
        }

        // Collect all parent objective IDs for title lookup
        Set<Long> parentIds = objectives.stream()
                .map(OkrObjective::getParentObjectiveId)
                .filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, String> parentTitleMap = new HashMap<>();
        if (!parentIds.isEmpty()) {
            objectiveMapper.selectBatchIds(parentIds)
                    .forEach(p -> parentTitleMap.put(p.getId(), p.getTitle()));
        }

        List<OkrDetailDto> result = new ArrayList<>();
        for (OkrObjective obj : objectives) {
            List<KrKeyResult> krs = krMap.getOrDefault(obj.getId(), List.of());
            List<KrWithProgress> krDtos = new ArrayList<>();
            for (KrKeyResult kr : krs) {
                KrWithProgress k = new KrWithProgress();
                k.setId(kr.getId()); k.setTitle(kr.getTitle());
                k.setTargetValue(kr.getTargetValue()); k.setCurrentValue(kr.getCurrentValue());
                k.setStatus(kr.getStatus().name()); k.setWeight(kr.getWeight());
                k.setAssignedTo(kr.getAssignedTo());
                k.setSourceKrId(kr.getSourceKrId());
                k.setProgressLogs(logMap.getOrDefault(kr.getId(), List.of()));
                // Resolve names
                if (kr.getAssignedTo() != null) {
                    k.setAssignedToName(userNames.get(kr.getAssignedTo()));
                }
                if (kr.getSourceKrId() != null) {
                    KrKeyResult src = sourceKrMap.get(kr.getSourceKrId());
                    if (src != null) {
                        k.setSourceKrTitle(src.getTitle());
                        OkrObjective srcObj = sourceObjectiveMap.get(src.getObjectiveId());
                        if (srcObj != null && srcObj.getUserId() != null) {
                            k.setSourceKrUserName(userNames.get(srcObj.getUserId()));
                        }
                    }
                }
                krDtos.add(k);
            }
            OkrDetailDto dto = OkrDetailDto.from(obj, krDtos);
            dto.setUserName(userNames.get(obj.getUserId()));
            dto.setDeptName(deptNames.get(obj.getDeptId()));
            if (obj.getReviewedBy() != null) dto.setReviewerName(userNames.get(obj.getReviewedBy()));
            if (obj.getParentObjectiveId() != null) {
                dto.setParentObjectiveTitle(parentTitleMap.get(obj.getParentObjectiveId()));
            }
            if (obj.getAlignedObjectiveIds() != null && !obj.getAlignedObjectiveIds().isEmpty()) {
                List<OkrDetailDto> aligned = Arrays.stream(obj.getAlignedObjectiveIds().split(","))
                        .map(String::trim).filter(s -> !s.isEmpty())
                        .map(Long::parseLong).map(alignedObjMap::get).filter(Objects::nonNull).toList();
                dto.setAlignedObjectives(aligned);
            }
            result.add(dto);
        }
        return result;
    }

    private void addIfPresent(Set<Long> target, Long value) {
        if (value != null) {
            target.add(value);
        }
    }

    public com.yimeng.okr.dto.OkrWorkbenchDto getWorkbench(Long periodId, Long userId) {
        com.yimeng.okr.dto.OkrWorkbenchDto dto = new com.yimeng.okr.dto.OkrWorkbenchDto();
        dto.setMyObjectives(getMyOkrForPeriod(periodId, userId));
        dto.setWorkflowState(determineWorkflowState(periodId, userId));
        dto.setVersion(System.currentTimeMillis());
        return dto;
    }

    private String determineWorkflowState(Long periodId, Long userId) {
        var period = periodMapper.selectById(periodId);
        if (period == null) return "NO_PERIOD";
        var objectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<OkrObjective>()
                .eq(OkrObjective::getPeriodId, periodId)
                .eq(OkrObjective::getUserId, userId));
        if (objectives.isEmpty()) return "DRAFT_REQUIRED";
        boolean hasSubmitted = objectives.stream().anyMatch(o -> "SUBMITTED".equals(o.getStatus()));
        return hasSubmitted ? "SUBMITTED" : "DRAFT";
    }

    private java.util.List<com.yimeng.okr.dto.OkrWorkbenchDto.ObjectiveDto> getMyOkrForPeriod(Long periodId, Long userId) {
        return objectiveMapper.selectList(
            new LambdaQueryWrapper<OkrObjective>()
                .eq(OkrObjective::getPeriodId, periodId)
                .eq(OkrObjective::getUserId, userId)).stream()
            .map(o -> {
                com.yimeng.okr.dto.OkrWorkbenchDto.ObjectiveDto od = new com.yimeng.okr.dto.OkrWorkbenchDto.ObjectiveDto();
                od.setId(o.getId());
                od.setTitle(o.getTitle());
                od.setStatus(o.getStatus());
                od.setProgress(o.getProgress());
                return od;
            }).toList();
    }
}
