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

import com.njcn.rdms.framework.common.exception.ServiceException;
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.framework.test.core.ut.BaseMockitoUnitTest;
import com.njcn.rdms.module.project.controller.admin.project.execution.vo.execution.ProjectExecutionLifecycleActionRespVO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusTransitionMapper;
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;

import java.math.BigDecimal;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

class ProjectExecutionStatusViewServiceTest extends BaseMockitoUnitTest {

    @InjectMocks
    private ProjectExecutionStatusViewService projectExecutionStatusViewService;
    @Mock
    private ObjectStatusModelMapper objectStatusModelMapper;
    @Mock
    private ObjectStatusTransitionMapper objectStatusTransitionMapper;

    @Test
    void getLifecycle_whenLoginUserIsOwner_shouldReturnOwnerOnlyAction() {
        Long ownerId = 3001L;
        ObjectStatusModelDO statusModel = createStatusModel();
        ObjectStatusTransitionDO pause = createTransition("pause", "暂停", true);
        when(objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled("execution", "active"))
                .thenReturn(statusModel);
        when(objectStatusTransitionMapper.selectListByObjectTypeAndFromStatus("execution", "active"))
                .thenReturn(List.of(pause));

        try (MockedStatic<SecurityFrameworkUtils> mocked = mockStatic(SecurityFrameworkUtils.class)) {
            mocked.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(ownerId);

            ProjectExecutionStatusViewService.ProjectExecutionLifecycleView result =
                    projectExecutionStatusViewService.getLifecycle("active", ownerId, BigDecimal.ZERO, false);

            assertEquals("进行中", result.statusName());
            assertFalse(result.terminal());
            assertTrue(result.allowEdit());
            assertEquals(1, result.availableActions().size());
            assertEquals("pause", result.availableActions().get(0).getActionCode());
            assertEquals("暂停", result.availableActions().get(0).getActionName());
            assertTrue(result.availableActions().get(0).getNeedReason());
        }
    }

    @Test
    void getLifecycle_whenLoginUserNotOwner_shouldFilterOwnerOnlyActions() {
        Long ownerId = 3001L;
        Long otherUserId = 3002L;
        ObjectStatusModelDO statusModel = createStatusModel();
        ObjectStatusTransitionDO pause = createTransition("pause", "暂停", true);
        ObjectStatusTransitionDO complete = createTransition("complete", "完成", false);
        when(objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled("execution", "active"))
                .thenReturn(statusModel);
        when(objectStatusTransitionMapper.selectListByObjectTypeAndFromStatus("execution", "active"))
                .thenReturn(List.of(pause, complete));

        try (MockedStatic<SecurityFrameworkUtils> mocked = mockStatic(SecurityFrameworkUtils.class)) {
            mocked.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(otherUserId);

            ProjectExecutionStatusViewService.ProjectExecutionLifecycleView result =
                    projectExecutionStatusViewService.getLifecycle("active", ownerId, new BigDecimal("100.00"), true);

            assertTrue(result.availableActions().isEmpty());
        }
    }

    @Test
    void getLifecycle_whenOwnerButProgressBelow100_shouldFilterCompleteOnly() {
        Long ownerId = 3001L;
        ObjectStatusModelDO statusModel = createStatusModel();
        ObjectStatusTransitionDO complete = createTransition("complete", "完成", false);
        ObjectStatusTransitionDO pause = createTransition("pause", "暂停", true);
        ObjectStatusTransitionDO cancel = createTransition("cancel", "取消", true);
        when(objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled("execution", "active"))
                .thenReturn(statusModel);
        when(objectStatusTransitionMapper.selectListByObjectTypeAndFromStatus("execution", "active"))
                .thenReturn(List.of(complete, pause, cancel));

        try (MockedStatic<SecurityFrameworkUtils> mocked = mockStatic(SecurityFrameworkUtils.class)) {
            mocked.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(ownerId);

            ProjectExecutionStatusViewService.ProjectExecutionLifecycleView result =
                    projectExecutionStatusViewService.getLifecycle("active", ownerId, new BigDecimal("99.99"), true);

            assertEquals(List.of("pause", "cancel"), result.availableActions().stream()
                    .map(ProjectExecutionLifecycleActionRespVO::getActionCode)
                    .toList());
        }
    }

    @Test
    void getLifecycle_whenOwnerAndProgress100AndRootTasksAllCompleted_shouldReturnComplete() {
        Long ownerId = 3001L;
        ObjectStatusModelDO statusModel = createStatusModel();
        ObjectStatusTransitionDO complete = createTransition("complete", "完成", false);
        when(objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled("execution", "active"))
                .thenReturn(statusModel);
        when(objectStatusTransitionMapper.selectListByObjectTypeAndFromStatus("execution", "active"))
                .thenReturn(List.of(complete));

        try (MockedStatic<SecurityFrameworkUtils> mocked = mockStatic(SecurityFrameworkUtils.class)) {
            mocked.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(ownerId);

            ProjectExecutionStatusViewService.ProjectExecutionLifecycleView result =
                    projectExecutionStatusViewService.getLifecycle("active", ownerId, new BigDecimal("100.00"), true);

            assertEquals(1, result.availableActions().size());
            assertEquals("complete", result.availableActions().get(0).getActionCode());
        }
    }

    @Test
    void getLifecycle_whenOwnerAndProgress100ButRootTasksNotAllCompleted_shouldFilterComplete() {
        // 任务进度算到 100 但任务状态尚未流转到 completed（或空集）时，complete 按钮不下发；
        // 同时验证 pause / cancel 等不受根任务完成态影响。
        Long ownerId = 3001L;
        ObjectStatusModelDO statusModel = createStatusModel();
        ObjectStatusTransitionDO complete = createTransition("complete", "完成", false);
        ObjectStatusTransitionDO pause = createTransition("pause", "暂停", true);
        ObjectStatusTransitionDO cancel = createTransition("cancel", "取消", true);
        when(objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled("execution", "active"))
                .thenReturn(statusModel);
        when(objectStatusTransitionMapper.selectListByObjectTypeAndFromStatus("execution", "active"))
                .thenReturn(List.of(complete, pause, cancel));

        try (MockedStatic<SecurityFrameworkUtils> mocked = mockStatic(SecurityFrameworkUtils.class)) {
            mocked.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(ownerId);

            ProjectExecutionStatusViewService.ProjectExecutionLifecycleView result =
                    projectExecutionStatusViewService.getLifecycle("active", ownerId, new BigDecimal("100.00"), false);

            assertEquals(List.of("pause", "cancel"), result.availableActions().stream()
                    .map(ProjectExecutionLifecycleActionRespVO::getActionCode)
                    .toList());
        }
    }

    @Test
    void getLifecycle_shouldExcludeAutoStartAction() {
        Long ownerId = 3001L;
        ObjectStatusModelDO statusModel = new ObjectStatusModelDO();
        statusModel.setObjectType("execution");
        statusModel.setStatusCode("pending");
        statusModel.setStatusName("待开始");
        statusModel.setTerminalFlag(false);
        statusModel.setAllowEdit(true);
        ObjectStatusTransitionDO autoStart = createTransition("auto_start", "开始推进", false);
        when(objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled("execution", "pending"))
                .thenReturn(statusModel);
        when(objectStatusTransitionMapper.selectListByObjectTypeAndFromStatus("execution", "pending"))
                .thenReturn(List.of(autoStart));

        try (MockedStatic<SecurityFrameworkUtils> mocked = mockStatic(SecurityFrameworkUtils.class)) {
            mocked.when(SecurityFrameworkUtils::getLoginUserId).thenReturn(ownerId);

            ProjectExecutionStatusViewService.ProjectExecutionLifecycleView result =
                    projectExecutionStatusViewService.getLifecycle("pending", ownerId, BigDecimal.ZERO, false);

            assertTrue(result.availableActions().isEmpty());
        }
    }

    @Test
    void getLifecycle_whenStatusModelMissing_shouldThrowException() {
        when(objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled("execution", "active"))
                .thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> projectExecutionStatusViewService.getLifecycle("active", 3001L, BigDecimal.ZERO, false));
        assertEquals(ErrorCodeConstants.PROJECT_EXECUTION_STATUS_MODEL_NOT_EXISTS_OR_DISABLED.getCode(), ex.getCode());
    }

    private ObjectStatusModelDO createStatusModel() {
        ObjectStatusModelDO statusModel = new ObjectStatusModelDO();
        statusModel.setObjectType("execution");
        statusModel.setStatusCode("active");
        statusModel.setStatusName("进行中");
        statusModel.setTerminalFlag(false);
        statusModel.setAllowEdit(true);
        return statusModel;
    }

    private ObjectStatusTransitionDO createTransition(String actionCode, String actionName, boolean needReason) {
        ObjectStatusTransitionDO transition = new ObjectStatusTransitionDO();
        transition.setActionCode(actionCode);
        transition.setActionName(actionName);
        transition.setNeedReason(needReason);
        return transition;
    }

}
