package com.yimeng.okr.integration;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yimeng.okr.entity.AssessmentPeriod;
import com.yimeng.okr.entity.KpiConfig;
import com.yimeng.okr.entity.KrKeyResult;
import com.yimeng.okr.entity.OkrObjective;
import com.yimeng.okr.entity.PerformanceFeedback;
import com.yimeng.okr.entity.PerformanceScore;
import com.yimeng.okr.entity.PeriodParticipant;
import com.yimeng.okr.entity.ScoreAppeal;
import com.yimeng.okr.entity.SysDepartment;
import com.yimeng.okr.entity.SysUser;
import com.yimeng.okr.enums.PeriodStatus;
import com.yimeng.okr.enums.PeriodType;
import com.yimeng.okr.enums.ScoreType;
import com.yimeng.okr.enums.UserRole;
import com.yimeng.okr.mapper.KpiConfigMapper;
import com.yimeng.okr.mapper.OkrObjectiveMapper;
import com.yimeng.okr.mapper.PerformanceFeedbackMapper;
import com.yimeng.okr.mapper.PerformanceScoreMapper;
import com.yimeng.okr.mapper.PeriodParticipantMapper;
import com.yimeng.okr.mapper.ScoreAppealMapper;
import com.yimeng.okr.mapper.SysDepartmentMapper;
import com.yimeng.okr.mapper.SysUserMapper;
import com.yimeng.okr.service.AssessmentPeriodService;
import com.yimeng.okr.service.FeedbackService;
import com.yimeng.okr.service.KpiService;
import com.yimeng.okr.service.OkrService;
import com.yimeng.okr.service.PerformanceService;
import com.yimeng.okr.service.ScoreAppealService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;

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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

@SpringBootTest
@TestPropertySource(properties = {
        "spring.datasource.url=jdbc:sqlite:file:okr_lifecycle?mode=memory&cache=shared",
        "app.seed.demo-data-enabled=false"
})
class PerformanceLifecycleIntegrationTest {

    @Autowired AssessmentPeriodService periodService;
    @Autowired OkrService okrService;
    @Autowired KpiService kpiService;
    @Autowired PerformanceService performanceService;
    @Autowired FeedbackService feedbackService;
    @Autowired ScoreAppealService appealService;
    @Autowired SysUserMapper userMapper;
    @Autowired SysDepartmentMapper departmentMapper;
    @Autowired OkrObjectiveMapper objectiveMapper;
    @Autowired KpiConfigMapper kpiConfigMapper;
    @Autowired PerformanceScoreMapper scoreMapper;
    @Autowired PerformanceFeedbackMapper feedbackMapper;
    @Autowired PeriodParticipantMapper participantMapper;
    @Autowired ScoreAppealMapper appealMapper;

    @Test
    void fullCompanyLifecycleCanArchiveAfterAppealAndFeedbackClosure() {
        SysUser hr = insertUser("hr_lifecycle", "HR", UserRole.HR_ADMIN, null, null);
        SysDepartment dept = insertDepartment("研发部");
        SysUser manager = insertUser("manager_lifecycle", "主管", UserRole.DEPT_LEADER, dept.getId(), null);
        dept.setLeaderId(manager.getId());
        departmentMapper.updateById(dept);
        SysUser employee = insertUser("employee_lifecycle", "员工", UserRole.EMPLOYEE, dept.getId(), manager.getId());

        AssessmentPeriod period = new AssessmentPeriod();
        period.setName("2026年6月绩效");
        period.setPeriodType(PeriodType.MONTHLY);
        period.setStartDate(LocalDate.of(2026, 6, 1));
        period = periodService.create(period);

        periodService.transitionStatus(period.getId(), PeriodStatus.OKR_ALIGN);
        assertEquals(2, participantMapper.selectCount(new LambdaQueryWrapper<PeriodParticipant>()
                .eq(PeriodParticipant::getPeriodId, period.getId())
                .eq(PeriodParticipant::getStatus, "ACTIVE")));

        OkrObjective managerOkr = okrService.submitOkr(period.getId(), manager.getId(), "提升研发交付质量",
                List.of(kr("缺陷率下降", 50), kr("准时交付率提升", 50)), List.of());
        OkrObjective employeeOkr = okrService.submitOkr(period.getId(), employee.getId(), "完成核心模块上线",
                List.of(kr("模块按期上线", 50), kr("线上问题闭环", 50)), List.of(managerOkr.getId()));
        okrService.reviewOkr(employeeOkr.getId(), true, "通过", manager.getId());

        KpiConfig managerKpi = saveAndLockKpi(period.getId(), manager.getId(), hr.getId());
        KpiConfig employeeKpi = saveAndLockKpi(period.getId(), employee.getId(), manager.getId());
        assertNotNull(managerKpi.getLockedAt());
        assertNotNull(employeeKpi.getLockedAt());

        periodService.transitionStatus(period.getId(), PeriodStatus.EXECUTING);
        periodService.transitionStatus(period.getId(), PeriodStatus.ASSESSING);

        performanceService.submitSelfScore(period.getId(), employee.getId(), 54.0, 32.0, 0.0, 0.0, "{}");
        performanceService.submitSuperiorScore(period.getId(), employee.getId(), manager.getId(), 55.0, 33.0, 0.0, 0.0, "{}");
        performanceService.publishFinalScore(period.getId(), employee.getId(), hr.getId());
        ScoreAppeal appeal = appealService.submitAppeal(period.getId(), employee.getId(), "希望复核评分依据");
        appealService.resolveAppeal(appeal.getId(), hr.getId(), "REJECTED", "评分依据充分");
        performanceService.confirmScore(period.getId(), employee.getId());

        feedbackService.saveInterviewNotes(period.getId(), employee.getId(), manager.getId(),
                "完成绩效面谈", "交付稳定", "文档仍需加强", "补齐关键设计文档");
        feedbackService.submitEmployeeFeedback(period.getId(), employee.getId(), "已确认改进计划");
        feedbackService.closeFeedback(period.getId(), employee.getId(), manager.getId());

        periodService.exemptParticipant(period.getId(), manager.getId(), hr.getId(), "管理岗不参与个人评分");
        periodService.transitionStatus(period.getId(), PeriodStatus.ARCHIVED);

        AssessmentPeriod archived = periodService.getById(period.getId());
        assertEquals(PeriodStatus.ARCHIVED, archived.getStatus());
        assertEquals(1, scoreMapper.selectCount(new LambdaQueryWrapper<PerformanceScore>()
                .eq(PerformanceScore::getPeriodId, period.getId())
                .eq(PerformanceScore::getUserId, employee.getId())
                .eq(PerformanceScore::getType, ScoreType.FINAL)
                .isNotNull(PerformanceScore::getConfirmedAt)));
        assertEquals("CLOSED", feedbackMapper.selectOne(new LambdaQueryWrapper<PerformanceFeedback>()
                .eq(PerformanceFeedback::getPeriodId, period.getId())
                .eq(PerformanceFeedback::getUserId, employee.getId())).getStatus());
        assertEquals("REJECTED", appealMapper.selectById(appeal.getId()).getStatus());
        assertNotNull(objectiveMapper.selectById(employeeOkr.getId()).getSnapshotJson());
        assertNotNull(kpiConfigMapper.selectById(employeeKpi.getId()).getSnapshotJson());
    }

    private SysDepartment insertDepartment(String name) {
        SysDepartment department = new SysDepartment();
        department.setName(name + System.nanoTime());
        department.setParentId(0L);
        departmentMapper.insert(department);
        return department;
    }

    private SysUser insertUser(String username, String realName, UserRole role, Long departmentId, Long superiorId) {
        SysUser user = new SysUser();
        user.setUsername(username + "_" + System.nanoTime());
        user.setPasswordHash("test");
        user.setRealName(realName);
        user.setRole(role);
        user.setDepartmentId(departmentId);
        user.setSuperiorId(superiorId);
        user.setStatus("ACTIVE");
        userMapper.insert(user);
        return user;
    }

    private KrKeyResult kr(String title, int weight) {
        KrKeyResult kr = new KrKeyResult();
        kr.setTitle(title);
        kr.setTargetValue(100.0);
        kr.setCurrentValue(0.0);
        kr.setWeight(weight);
        return kr;
    }

    private KpiConfig saveAndLockKpi(Long periodId, Long userId, Long actorId) {
        KpiConfig config = new KpiConfig();
        config.setPeriodId(periodId);
        config.setUserId(userId);
        config.setItemsJson("[{\"name\":\"交付质量\",\"weight\":100}]");
        KpiConfig saved = kpiService.saveConfig(config, actorId);
        kpiService.lockConfig(saved.getId(), actorId);
        return kpiConfigMapper.selectById(saved.getId());
    }
}
