package com.yimeng.okr.service;

import com.yimeng.okr.entity.AssessmentPeriod;
import com.yimeng.okr.entity.KrKeyResult;
import com.yimeng.okr.entity.OkrObjective;
import com.yimeng.okr.entity.SysDepartment;
import com.yimeng.okr.entity.SysUser;
import com.yimeng.okr.enums.KrStatus;
import com.yimeng.okr.enums.PeriodStatus;
import com.yimeng.okr.exception.BusinessException;
import com.yimeng.okr.mapper.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationEventPublisher;

import java.util.Collection;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class OkrServiceTest {

    @Mock OkrObjectiveMapper objectiveMapper;
    @Mock KrKeyResultMapper krMapper;
    @Mock KrProgressLogMapper progressLogMapper;
    @Mock AssessmentPeriodMapper periodMapper;
    @Mock SysUserMapper userMapper;
    @Mock SysDepartmentMapper departmentMapper;
    @Mock AuthorizationService authorizationService;
    @Mock ApplicationEventPublisher publisher;
    @InjectMocks OkrService okrService;

    @Test
    void updateKrProgressRejectsUnrelatedUser() {
        KrKeyResult kr = new KrKeyResult();
        kr.setId(5L);
        kr.setObjectiveId(6L);
        kr.setAssignedTo(null);
        when(krMapper.selectById(5L)).thenReturn(kr);
        OkrObjective obj = new OkrObjective();
        obj.setId(6L);
        obj.setUserId(2L);
        obj.setPeriodId(1L);
        when(objectiveMapper.selectById(6L)).thenReturn(obj);
        AssessmentPeriod period = new AssessmentPeriod();
        period.setId(1L);
        period.setStatus(PeriodStatus.EXECUTING);
        when(periodMapper.selectById(1L)).thenReturn(period);

        BusinessException ex = assertThrows(BusinessException.class,
                () -> okrService.updateKrProgress(5L, 50.0, KrStatus.IN_PROGRESS, "", 9L));

        assertEquals(403, ex.getCode());
        verify(progressLogMapper, never()).insert(any());
    }

    @Test
    void enrichObjectivesLoadsOnlyReferencedUsersAndDepartments() {
        OkrObjective objective = new OkrObjective();
        objective.setId(6L);
        objective.setUserId(2L);
        objective.setDeptId(10L);
        objective.setReviewedBy(3L);

        KrKeyResult kr = new KrKeyResult();
        kr.setId(5L);
        kr.setObjectiveId(6L);
        kr.setAssignedTo(4L);
        kr.setStatus(KrStatus.IN_PROGRESS);
        kr.setWeight(100);

        when(krMapper.selectList(any())).thenReturn(List.of(kr));
        when(userMapper.selectBatchIds(argThat(ids -> containsAll(ids, 2L, 3L, 4L))))
                .thenReturn(List.of(user(2L, "员工"), user(3L, "上级"), user(4L, "协作人")));
        when(departmentMapper.selectBatchIds(argThat(ids -> containsAll(ids, 10L))))
                .thenReturn(List.of(department(10L, "研发部")));

        assertEquals(1, okrService.enrichObjectives(List.of(objective)).size());

        verify(userMapper).selectBatchIds(argThat(ids -> containsAll(ids, 2L, 3L, 4L)));
        verify(departmentMapper).selectBatchIds(argThat(ids -> containsAll(ids, 10L)));
        verify(userMapper, never()).selectList(isNull());
        verify(departmentMapper, never()).selectList(isNull());
    }

    private boolean containsAll(Collection<?> ids, Long... expected) {
        return ids != null && ids.containsAll(List.of(expected));
    }

    private SysUser user(Long id, String name) {
        SysUser user = new SysUser();
        user.setId(id);
        user.setRealName(name);
        return user;
    }

    private SysDepartment department(Long id, String name) {
        SysDepartment department = new SysDepartment();
        department.setId(id);
        department.setName(name);
        return department;
    }
}
