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

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.task.vo.ProjectTaskLifecycleActionRespVO;
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.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 java.util.Map;

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.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class ProjectTaskStatusViewServiceTest extends BaseMockitoUnitTest {

    @InjectMocks
    private ProjectTaskStatusViewService projectTaskStatusViewService;
    @Mock
    private ObjectStatusModelMapper objectStatusModelMapper;
    @Mock
    private ObjectStatusTransitionMapper objectStatusTransitionMapper;

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

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

            ProjectTaskStatusViewService.ProjectTaskLifecycleView view =
                    projectTaskStatusViewService.getLifecycle("active", ownerId, new BigDecimal("100.00"));

            assertEquals("进行中", view.statusName());
            assertFalse(view.terminal());
            assertTrue(view.allowEdit());
            assertEquals(2, view.availableActions().size());
            assertEquals("complete", view.availableActions().get(0).getActionCode());
            assertEquals("完成", view.availableActions().get(0).getActionName());
        }
    }

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

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

            ProjectTaskStatusViewService.ProjectTaskLifecycleView view =
                    projectTaskStatusViewService.getLifecycle("active", ownerId, new BigDecimal("100.00"));

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

    @Test
    void getLifecycle_whenOwnerButProgressBelow100_shouldFilterCompleteOnly() {
        Long ownerId = 3001L;
        ObjectStatusModelDO statusModel = createStatusModel();
        ObjectStatusTransitionDO complete = createTransition("complete", "完成");
        ObjectStatusTransitionDO pause = createTransition("pause", "暂停");
        ObjectStatusTransitionDO resume = createTransition("resume", "恢复");
        when(objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled("task", "active")).thenReturn(statusModel);
        when(objectStatusTransitionMapper.selectListByObjectTypeAndFromStatus("task", "active"))
                .thenReturn(List.of(complete, pause, resume));

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

            ProjectTaskStatusViewService.ProjectTaskLifecycleView view =
                    projectTaskStatusViewService.getLifecycle("active", ownerId, new BigDecimal("99.99"));

            assertEquals(List.of("pause", "resume"), view.availableActions().stream()
                    .map(ProjectTaskLifecycleActionRespVO::getActionCode)
                    .toList());
        }
    }

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

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

            ProjectTaskStatusViewService.ProjectTaskLifecycleView view =
                    projectTaskStatusViewService.getLifecycle("active", ownerId, new BigDecimal("100.00"));

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

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

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

            ProjectTaskStatusViewService.ProjectTaskLifecycleView view =
                    projectTaskStatusViewService.getLifecycle("pending", ownerId, BigDecimal.ZERO);

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

    @Test
    void getLifecycleMap_shouldBatchLoadTransitionsAndApplyActionFilters() {
        Long ownerId = 3001L;
        Long collaboratorOwnerId = 3002L;
        ProjectTaskDO ownerTask = createTask(1L, ownerId, "active", new BigDecimal("100.00"));
        ProjectTaskDO collaboratorTask = createTask(2L, collaboratorOwnerId, "active", new BigDecimal("100.00"));
        ObjectStatusModelDO statusModel = createStatusModel();
        ObjectStatusTransitionDO pause = createTransition("active", "pause", "暂停");
        ObjectStatusTransitionDO complete = createTransition("active", "complete", "完成");
        ObjectStatusTransitionDO autoStart = createTransition("active", "auto_start", "自动开始");
        when(objectStatusModelMapper.selectListByObjectTypeEnabled("task")).thenReturn(List.of(statusModel));
        when(objectStatusTransitionMapper.selectListByObjectTypeAndFromStatuses("task", List.of("active")))
                .thenReturn(List.of(pause, complete, autoStart));

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

            Map<Long, ProjectTaskStatusViewService.ProjectTaskLifecycleView> map =
                    projectTaskStatusViewService.getLifecycleMap(List.of(ownerTask, collaboratorTask));

            assertEquals("进行中", map.get(1L).statusName());
            assertFalse(map.get(1L).terminal());
            assertTrue(map.get(1L).allowEdit());
            assertEquals(List.of("pause", "complete"), map.get(1L).availableActions().stream()
                    .map(ProjectTaskLifecycleActionRespVO::getActionCode)
                    .toList());
            assertTrue(map.get(2L).availableActions().isEmpty());
            verify(objectStatusTransitionMapper).selectListByObjectTypeAndFromStatuses("task", List.of("active"));
            verify(objectStatusTransitionMapper, never()).selectListByObjectTypeAndFromStatus("task", "active");
        }
    }

    @Test
    void getLifecycle_whenStatusModelMissing_shouldThrow() {
        when(objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled("task", "missing")).thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> projectTaskStatusViewService.getLifecycle("missing", 3001L, BigDecimal.ZERO));

        assertEquals(ErrorCodeConstants.PROJECT_TASK_STATUS_MODEL_NOT_EXISTS_OR_DISABLED.getCode(), ex.getCode());
    }

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

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

    private ObjectStatusTransitionDO createTransition(String fromStatusCode, String actionCode, String actionName) {
        ObjectStatusTransitionDO transition = createTransition(actionCode, actionName);
        transition.setFromStatusCode(fromStatusCode);
        return transition;
    }

    private ProjectTaskDO createTask(Long id, Long ownerId, String statusCode, BigDecimal progressRate) {
        ProjectTaskDO task = new ProjectTaskDO();
        task.setId(id);
        task.setOwnerId(ownerId);
        task.setStatusCode(statusCode);
        task.setProgressRate(progressRate);
        return task;
    }

}
