package com.easyagents.agent.runtime.tool.operate;

import com.easyagents.agent.runtime.AgentRuntimeException;
import com.easyagents.agent.runtime.tool.AgentToolSpec;
import io.agentscope.core.tool.Toolkit;
import org.junit.Assert;
import org.junit.Test;

import java.util.List;
import java.util.Set;
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;

/**
 * 测试 Agent 操作类工具适配器。
 */
public class AgentOperateToolAdapterTest {

    private final AgentOperateToolAdapter adapter = new AgentOperateToolAdapter();

    @Test
    public void shouldRegisterReadFileToolsWithDefaultHitlDisabled() {
        Toolkit toolkit = new Toolkit();
        AgentOperateToolSpec spec = spec(AgentOperateToolType.READ_FILE);

        List<AgentToolSpec> toolSpecs = adapter.register(List.of(spec), toolkit);

        Assert.assertNotNull(toolkit.getTool(AgentOperateToolAdapter.VIEW_TEXT_FILE_TOOL));
        Assert.assertNotNull(toolkit.getTool(AgentOperateToolAdapter.LIST_DIRECTORY_TOOL));
        Assert.assertEquals(2, toolSpecs.size());
        Assert.assertTrue(toolSpecs.stream().noneMatch(AgentToolSpec::isApprovalRequired));
    }

    @Test
    public void shouldRegisterWriteFileToolsWithDefaultHitlDisabled() {
        Toolkit toolkit = new Toolkit();
        AgentOperateToolSpec spec = spec(AgentOperateToolType.WRITE_FILE);

        List<AgentToolSpec> toolSpecs = adapter.register(List.of(spec), toolkit);

        Assert.assertNotNull(toolkit.getTool(AgentOperateToolAdapter.WRITE_TEXT_FILE_TOOL));
        Assert.assertNotNull(toolkit.getTool(AgentOperateToolAdapter.INSERT_TEXT_FILE_TOOL));
        Assert.assertEquals(2, toolSpecs.size());
        Assert.assertTrue(toolSpecs.stream().noneMatch(AgentToolSpec::isApprovalRequired));
    }

    @Test(expected = AgentRuntimeException.class)
    public void shouldRejectEmptyShellWhitelist() {
        AgentOperateToolSpec spec = spec(AgentOperateToolType.SHELL);
        spec.setShellAllowedCommands(Set.of());

        adapter.register(List.of(spec), new Toolkit());
    }

    @Test
    public void shouldRegisterShellWithForcedRmApprovalMetadata() {
        Toolkit toolkit = new Toolkit();
        AgentOperateToolSpec spec = spec(AgentOperateToolType.SHELL);

        List<AgentToolSpec> toolSpecs = adapter.register(List.of(spec), toolkit);

        Assert.assertNotNull(toolkit.getTool(AgentOperateToolAdapter.EXECUTE_SHELL_COMMAND_TOOL));
        Assert.assertEquals(1, toolSpecs.size());
        Assert.assertTrue(toolSpecs.get(0).isApprovalRequired());
        Assert.assertNotNull(toolSpecs.get(0).getApprovalPolicy());
        Assert.assertEquals(List.of("rm"), toolSpecs.get(0).getMetadata().get("forceApprovalCommands"));
        Assert.assertFalse(toolSpecs.get(0).getMetadata().containsKey("baseDir"));
    }

    @Test
    public void shouldDisableAllShellApprovalPoliciesWithAgentSwitch() {
        Toolkit toolkit = new Toolkit();
        AgentOperateToolSpec spec = spec(AgentOperateToolType.SHELL);
        spec.setApprovalRequired(false);

        List<AgentToolSpec> toolSpecs = adapter.register(List.of(spec), toolkit);

        Assert.assertEquals(1, toolSpecs.size());
        Assert.assertFalse(toolSpecs.get(0).isApprovalRequired());
        Assert.assertNull(toolSpecs.get(0).getApprovalPolicy());
        Assert.assertFalse(toolSpecs.get(0).getMetadata().containsKey("forceApprovalCommands"));
        Assert.assertFalse(toolSpecs.get(0).getMetadata().containsKey("forceApprovalCommandArgument"));
    }

    @Test
    public void shouldRegisterPatchWithDefaultHitlDisabled() {
        Toolkit toolkit = new Toolkit();
        AgentOperateToolSpec spec = spec(AgentOperateToolType.PATCH);

        List<AgentToolSpec> toolSpecs = adapter.register(List.of(spec), toolkit);

        Assert.assertNotNull(toolkit.getTool(AgentOperateToolAdapter.APPLY_PATCH_TOOL));
        Assert.assertEquals(1, toolSpecs.size());
        Assert.assertFalse(toolSpecs.get(0).isApprovalRequired());
    }

    @Test
    public void shouldSkipDisabledOperateTool() {
        Toolkit toolkit = new Toolkit();
        AgentOperateToolSpec spec = spec(AgentOperateToolType.SHELL);
        spec.setEnabled(false);

        List<AgentToolSpec> toolSpecs = adapter.register(List.of(spec), toolkit);

        Assert.assertNull(toolkit.getTool(AgentOperateToolAdapter.EXECUTE_SHELL_COMMAND_TOOL));
        Assert.assertTrue(toolSpecs.isEmpty());
    }

    @Test(expected = AgentRuntimeException.class)
    public void shouldRejectMissingBaseDir() {
        AgentOperateToolSpec spec = spec(AgentOperateToolType.READ_FILE);
        spec.setBaseDir(null);

        adapter.register(List.of(spec), new Toolkit());
    }

    @Test(expected = AgentRuntimeException.class)
    public void shouldRejectRelativeBaseDir() {
        AgentOperateToolSpec spec = spec(AgentOperateToolType.READ_FILE);
        spec.setBaseDir("relative/workspace");

        adapter.register(List.of(spec), new Toolkit());
    }

    @Test(expected = AgentRuntimeException.class)
    public void shouldRejectToolNameConflict() {
        Toolkit toolkit = new Toolkit();
        AgentOperateToolSpec first = spec(AgentOperateToolType.READ_FILE);
        AgentOperateToolSpec second = spec(AgentOperateToolType.READ_FILE);

        adapter.register(List.of(first, second), toolkit);
    }

    private AgentOperateToolSpec spec(AgentOperateToolType type) {
        AgentOperateToolSpec spec = new AgentOperateToolSpec();
        spec.setType(type);
        try {
            Path workspace = Files.createTempDirectory("operate-tool-adapter-");
            spec.setBaseDir(workspace.toAbsolutePath().toString());
        } catch (IOException error) {
            throw new AssertionError(error);
        }
        return spec;
    }
}
