package tech.easyflow.approval.service.impl;

import com.mybatisflex.core.query.QueryWrapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.MockitoJUnitRunner;
import tech.easyflow.approval.entity.ApprovalInstance;
import tech.easyflow.approval.entity.ApprovalTask;
import tech.easyflow.approval.entity.vo.ApprovalInstanceDetailVo;
import tech.easyflow.approval.enums.ApprovalInstanceStatus;
import tech.easyflow.approval.enums.ApprovalTaskStatus;
import tech.easyflow.approval.mapper.ApprovalFlowStepMapper;
import tech.easyflow.approval.mapper.ApprovalInstanceMapper;
import tech.easyflow.approval.mapper.ApprovalLogMapper;
import tech.easyflow.approval.mapper.ApprovalTaskMapper;
import tech.easyflow.approval.service.ApprovalActionFacade;
import tech.easyflow.approval.service.ApprovalAssigneeService;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.system.service.CategoryPermissionService;
import tech.easyflow.system.service.SysAccountService;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

/**
 * 审批撤回入口查询测试。
 */
@RunWith(MockitoJUnitRunner.class)
public class ApprovalRevokeQueryTest {

    private static final BigInteger INSTANCE_ID = BigInteger.valueOf(101);
    private static final BigInteger TENANT_ID = BigInteger.valueOf(42);

    @Mock
    private ApprovalInstanceMapper approvalInstanceMapper;

    @Mock
    private ApprovalTaskMapper approvalTaskMapper;

    @Mock
    private ApprovalLogMapper approvalLogMapper;

    @Mock
    private ApprovalFlowStepMapper approvalFlowStepMapper;

    @Mock
    private ApprovalAssigneeService approvalAssigneeService;

    @Mock
    private ApprovalActionFacade approvalActionFacade;

    @Mock
    private CategoryPermissionService categoryPermissionService;

    @Mock
    private SysAccountService sysAccountService;

    @InjectMocks
    private ApprovalQueryServiceImpl service;

    /**
     * 验证活动审批的发起人只能看到撤回入口。
     */
    @Test
    public void detailShouldExposeRevokeOnlyToApplicant() {
        LoginAccount account = account(7);
        ApprovalInstance instance = instance(7);
        stubDetail(instance, List.of());

        try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
            saToken.when(SaTokenUtil::getLoginAccount).thenReturn(account);
            ApprovalInstanceDetailVo detail = service.detail(INSTANCE_ID);
            assertTrue(detail.isCanRevoke());
            assertFalse(detail.isCanApprove());
            assertFalse(detail.isCanReject());
        }
    }

    /**
     * 验证当前审批人只能看到通过和驳回入口，不能撤回他人申请。
     */
    @Test
    public void detailShouldNotExposeRevokeToCurrentReviewer() {
        LoginAccount account = account(7);
        ApprovalInstance instance = instance(8);
        ApprovalTask task = new ApprovalTask();
        task.setInstanceId(INSTANCE_ID);
        task.setStepNo(1);
        task.setStatus(ApprovalTaskStatus.PENDING.getCode());
        stubDetail(instance, List.of(task));
        when(approvalAssigneeService.canHandleTask(task, account.getId(), Set.of())).thenReturn(true);

        try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
            saToken.when(SaTokenUtil::getLoginAccount).thenReturn(account);
            ApprovalInstanceDetailVo detail = service.detail(INSTANCE_ID);
            assertTrue(detail.isCanApprove());
            assertTrue(detail.isCanReject());
            assertFalse(detail.isCanRevoke());
        }
    }

    /**
     * 配置详情查询所需的兼容桩。
     *
     * @param instance 审批实例
     * @param tasks 审批任务
     */
    private void stubDetail(ApprovalInstance instance, List<ApprovalTask> tasks) {
        lenient().when(approvalInstanceMapper.selectOneById(INSTANCE_ID)).thenReturn(instance);
        lenient().when(approvalInstanceMapper.selectOneByQuery(any(QueryWrapper.class))).thenReturn(instance);
        when(approvalTaskMapper.selectListByQuery(any(QueryWrapper.class))).thenReturn(tasks);
        when(approvalLogMapper.selectListByQuery(any(QueryWrapper.class))).thenReturn(List.of());
        when(approvalAssigneeService.getAvailableRoleIds(any(BigInteger.class))).thenReturn(Set.of());
        lenient().when(sysAccountService.listByIds(any())).thenReturn(List.of());
        lenient().when(sysAccountService.list(any(QueryWrapper.class))).thenReturn(List.of());
    }

    /**
     * 构造活动审批实例。
     *
     * @param applicantId 申请人 ID
     * @return 活动审批实例
     */
    private ApprovalInstance instance(long applicantId) {
        ApprovalInstance instance = new ApprovalInstance();
        instance.setId(INSTANCE_ID);
        instance.setTenantId(TENANT_ID);
        instance.setFlowId(BigInteger.valueOf(301));
        instance.setFlowVersion(1);
        instance.setResourceType("KNOWLEDGE");
        instance.setResourceId(BigInteger.valueOf(501));
        instance.setActionType("PUBLISH");
        instance.setStatus(ApprovalInstanceStatus.PENDING.getCode());
        instance.setCurrentStepNo(1);
        instance.setApplicantId(BigInteger.valueOf(applicantId));
        instance.setSnapshotJson(Map.of(
                "steps", List.of(Map.of(
                        "stepNo", 1,
                        "stepName", "审核"
                ))
        ));
        return instance;
    }

    /**
     * 构造当前登录账号。
     *
     * @param accountId 账号 ID
     * @return 登录账号
     */
    private LoginAccount account(long accountId) {
        LoginAccount account = new LoginAccount();
        account.setId(BigInteger.valueOf(accountId));
        account.setTenantId(TENANT_ID);
        return account;
    }
}
