package tech.easyflow.agent.config;

import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.service.CategoryPermissionService;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Agent 内置工具默认值、兼容语义和 Shell 高风险确认测试。
 */
public class AgentBuiltinToolsConfigResolverTest {

    /**
     * 验证新草稿缺失配置时五项启用且仅 Shell 默认审批。
     */
    @Test
    public void draftDefaultsShouldEnableFiveToolsAndApproveShellOnly() {
        AgentBuiltinToolsConfigResolver resolver = resolver(false);

        AgentBuiltinToolsConfig config = resolver.resolveDraftRuntime(Map.of());

        Assert.assertTrue(config.read().enabled());
        Assert.assertTrue(config.write().enabled());
        Assert.assertTrue(config.patch().enabled());
        Assert.assertTrue(config.shell().enabled());
        Assert.assertTrue(config.artifactPublish().enabled());
        Assert.assertFalse(config.read().approvalRequired());
        Assert.assertTrue(config.shell().approvalRequired());
    }

    /**
     * 验证旧发布快照缺失内置工具配置时保持全部禁用。
     */
    @Test
    public void legacyPublishedSnapshotShouldDisableAllBuiltinTools() {
        AgentBuiltinToolsConfig config = resolver(false).resolvePublishedRuntime(Map.of());

        Assert.assertFalse(config.read().enabled());
        Assert.assertFalse(config.write().enabled());
        Assert.assertFalse(config.patch().enabled());
        Assert.assertFalse(config.shell().enabled());
        Assert.assertFalse(config.artifactPublish().enabled());
    }

    /**
     * 验证普通账号伪造确认字段仍无法关闭 Shell 审批。
     */
    @Test(expected = BusinessException.class)
    public void ordinaryUserShouldNotDisableShellApprovalWithForgedConfirmation() {
        resolver(false).normalizeForDraftSave(shellApprovalDisabled(true), Map.of(), new LoginAccount());
    }

    /**
     * 验证平台超管确认后可关闭审批，且一次性确认字段不会持久化。
     */
    @Test
    public void superAdminShouldDisableShellApprovalAfterConfirmationAndStripSignal() {
        Map<String, Object> normalized = resolver(true).normalizeForDraftSave(
                shellApprovalDisabled(true), Map.of(), new LoginAccount());

        @SuppressWarnings("unchecked")
        Map<String, Object> builtin = (Map<String, Object>) normalized.get("builtinTools");
        Assert.assertFalse(builtin.containsKey("shellApprovalRiskConfirmed"));
        @SuppressWarnings("unchecked")
        Map<String, Object> shell = (Map<String, Object>) builtin.get("shell");
        Assert.assertEquals(Boolean.FALSE, shell.get("approvalRequired"));
    }

    /**
     * 验证显式未知结构版本不会按 v1 静默解释。
     */
    @Test(expected = BusinessException.class)
    public void futureSchemaVersionShouldBeRejected() {
        resolver(false).resolveDraftRuntime(Map.of(
                "builtinTools", Map.of("schemaVersion", 2)));
    }

    /**
     * 验证缺失版本号继续按 v1 兼容，并能识别真实关闭审批变更。
     */
    @Test
    public void missingSchemaVersionShouldUseV1AndDetectApprovalTransition() {
        AgentBuiltinToolsConfigResolver resolver = resolver(true);
        Map<String, Object> source = shellApprovalDisabled(true);

        Assert.assertTrue(resolver.isShellApprovalDisableTransition(source, Map.of()));
        Assert.assertEquals(AgentBuiltinToolsConfig.SCHEMA_VERSION,
                resolver.resolveDraftRuntime(source).toMap().get("schemaVersion"));
    }

    /** 验证 builtinTools 标量不会静默回退默认值。 */
    @Test(expected = BusinessException.class)
    public void scalarBuiltinToolsShouldBeRejected() {
        resolver(false).resolveDraftRuntime(Map.of("builtinTools", true));
    }

    /** 验证工具项标量不会静默回退默认值。 */
    @Test(expected = BusinessException.class)
    public void scalarToolConfigShouldBeRejected() {
        resolver(false).resolveDraftRuntime(Map.of("builtinTools", Map.of("read", true)));
    }

    /** 验证布尔字段的字符串形式不会被宽松接受。 */
    @Test(expected = BusinessException.class)
    public void stringBooleanShouldBeRejected() {
        resolver(false).resolveDraftRuntime(Map.of(
                "builtinTools", Map.of("read", Map.of("enabled", "true"))));
    }

    /** 验证 schemaVersion 必须使用数值类型。 */
    @Test(expected = BusinessException.class)
    public void stringSchemaVersionShouldBeRejected() {
        resolver(false).resolveDraftRuntime(Map.of(
                "builtinTools", Map.of("schemaVersion", "1")));
    }

    private AgentBuiltinToolsConfigResolver resolver(boolean superAdmin) {
        CategoryPermissionService permissions = Mockito.mock(CategoryPermissionService.class);
        Mockito.when(permissions.isSuperAdmin(Mockito.any())).thenReturn(superAdmin);
        return new AgentBuiltinToolsConfigResolver(permissions);
    }

    private Map<String, Object> shellApprovalDisabled(boolean confirmed) {
        Map<String, Object> builtin = new LinkedHashMap<>();
        builtin.put("shell", Map.of("enabled", true, "approvalRequired", false));
        builtin.put("shellApprovalRiskConfirmed", confirmed);
        return Map.of("builtinTools", builtin);
    }
}
