package assistant_test

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/yaoapp/yao/agent"
	"github.com/yaoapp/yao/agent/assistant"
	agentContext "github.com/yaoapp/yao/agent/context"
	agentsandbox "github.com/yaoapp/yao/agent/sandbox"
	"github.com/yaoapp/yao/agent/sandbox/claude"
	"github.com/yaoapp/yao/config"
	"github.com/yaoapp/yao/test"
)

// TestSandboxOptionsBuilding tests that sandbox options are correctly built from assistant config
func TestSandboxOptionsBuilding(t *testing.T) {
	test.Prepare(t, config.Conf)
	defer test.Clean()

	// Load agent to ensure connectors are available
	err := agent.Load(config.Conf)
	require.NoError(t, err, "agent.Load should succeed")

	// Load the full test assistant
	ast, err := assistant.LoadPath("/assistants/tests/sandbox/full")
	require.NoError(t, err)
	require.NotNil(t, ast)

	// Verify sandbox is configured
	require.NotNil(t, ast.Sandbox)
	assert.Equal(t, "claude", ast.Sandbox.Command)
	assert.Equal(t, "5m", ast.Sandbox.Timeout)

	// Verify arguments are set
	require.NotNil(t, ast.Sandbox.Arguments)
	assert.Equal(t, float64(10), ast.Sandbox.Arguments["max_turns"])
	assert.Equal(t, "acceptEdits", ast.Sandbox.Arguments["permission_mode"])

	// Verify MCP configuration
	require.NotNil(t, ast.MCP)
	assert.Len(t, ast.MCP.Servers, 1)
	assert.Equal(t, "echo", ast.MCP.Servers[0].ServerID)

	t.Logf("Sandbox config: command=%s, timeout=%s", ast.Sandbox.Command, ast.Sandbox.Timeout)
	t.Logf("Sandbox arguments: %v", ast.Sandbox.Arguments)
	t.Logf("MCP servers: %v", ast.MCP.Servers)
}

// TestClaudeCommandBuilding tests that Claude CLI commands are correctly built
func TestClaudeCommandBuilding(t *testing.T) {
	test.Prepare(t, config.Conf)
	defer test.Clean()

	// Create test messages
	messages := []agentContext.Message{
		{Role: "system", Content: "You are a helpful coding assistant."},
		{Role: "user", Content: "Hello, how are you?"},
	}

	// Create options similar to what buildSandboxOptions would produce
	opts := &claude.Options{
		Command:       "claude",
		UserID:        "test-user",
		ChatID:        "test-chat",
		ConnectorHost: "https://ark.cn-beijing.volces.com/api/v3",
		ConnectorKey:  "test-api-key",
		Model:         "ep-xxxxx",
		Arguments: map[string]interface{}{
			"max_turns":       10,
			"permission_mode": "acceptEdits",
		},
	}

	// Build the command
	cmd, env, err := claude.BuildCommand(messages, opts)
	require.NoError(t, err)

	// Verify command structure
	// Command is now: ["bash", "-c", "cat << 'INPUTEOF' | claude -p ... INPUTEOF"]
	assert.NotEmpty(t, cmd)
	assert.Equal(t, "bash", cmd[0], "Command should start with bash")
	assert.Equal(t, "-c", cmd[1], "Second arg should be -c")
	assert.Contains(t, cmd[2], "claude -p", "Bash command should contain claude -p")
	assert.Contains(t, cmd[2], "--permission-mode", "Should include permission mode")
	assert.Contains(t, cmd[2], "--input-format", "Should include input-format flag")
	assert.Contains(t, cmd[2], "--output-format", "Should include output-format flag")
	assert.Contains(t, cmd[2], "--verbose", "Should include verbose flag")
	assert.Contains(t, cmd[2], "stream-json", "Should use stream-json format")
	assert.Contains(t, cmd[2], "INPUTEOF", "Should use heredoc for input")
	t.Logf("Built command: %v", cmd)

	// Verify environment variables (claude-proxy mode)
	assert.NotEmpty(t, env)
	assert.Equal(t, "http://127.0.0.1:3456", env["ANTHROPIC_BASE_URL"], "Should set proxy base URL")
	assert.Equal(t, "dummy", env["ANTHROPIC_API_KEY"], "Should set dummy API key for proxy")

	// max_turns is passed via CLI flag
	// system prompt is written to file via heredoc, then referenced via --append-system-prompt-file
	assert.Contains(t, cmd[2], "--max-turns", "Should include max-turns flag")
	assert.Contains(t, cmd[2], "cat << 'PROMPTEOF' > /tmp/.system-prompt.txt", "Should use heredoc for system prompt")
	assert.Contains(t, cmd[2], "--append-system-prompt-file", "Should include append-system-prompt-file flag")
	assert.Contains(t, cmd[2], "You are a helpful coding assistant", "Command should contain system prompt")
	t.Logf("Built environment: %v", env)
}

// TestClaudeProxyConfigBuilding tests that claude-proxy config is correctly built
func TestClaudeProxyConfigBuilding(t *testing.T) {
	test.Prepare(t, config.Conf)
	defer test.Clean()

	opts := &claude.Options{
		ConnectorHost: "https://ark.cn-beijing.volces.com/api/v3",
		ConnectorKey:  "test-api-key",
		Model:         "ep-xxxxx",
	}

	configJSON, err := claude.BuildProxyConfig(opts)
	require.NoError(t, err)
	require.NotEmpty(t, configJSON)

	t.Logf("Proxy config: %s", string(configJSON))

	// Verify the JSON contains expected fields for claude-proxy
	assert.Contains(t, string(configJSON), "backend")
	assert.Contains(t, string(configJSON), "api_key")
	assert.Contains(t, string(configJSON), "model")
	assert.Contains(t, string(configJSON), "test-api-key")
	assert.Contains(t, string(configJSON), "ep-xxxxx")
	// Backend URL should end with /chat/completions
	assert.Contains(t, string(configJSON), "/chat/completions")
}

// TestDefaultImageSelection tests that default images are correctly selected
func TestDefaultImageSelection(t *testing.T) {
	tests := []struct {
		command       string
		expectedImage string
	}{
		{"claude", "yaoapp/sandbox-claude:latest"},
		{"cursor", "yaoapp/sandbox-cursor:latest"},
		{"unknown", ""},
	}

	for _, tt := range tests {
		t.Run(tt.command, func(t *testing.T) {
			image := agentsandbox.DefaultImage(tt.command)
			assert.Equal(t, tt.expectedImage, image)
		})
	}
}

// TestSandboxCommandValidation tests that command validation works correctly
func TestSandboxCommandValidation(t *testing.T) {
	tests := []struct {
		command string
		valid   bool
	}{
		{"claude", true},
		{"cursor", true},
		{"invalid", false},
		{"", false},
	}

	for _, tt := range tests {
		t.Run(tt.command, func(t *testing.T) {
			result := agentsandbox.IsValidCommand(tt.command)
			assert.Equal(t, tt.valid, result)
		})
	}
}

// TestHasSandboxMethod tests the HasSandbox method on Assistant
func TestHasSandboxMethod(t *testing.T) {
	test.Prepare(t, config.Conf)
	defer test.Clean()

	// Test assistant with sandbox
	astWithSandbox, err := assistant.LoadPath("/assistants/tests/sandbox/basic")
	require.NoError(t, err)
	assert.True(t, astWithSandbox.HasSandbox(), "Assistant with sandbox config should return true")

	// Test assistant without sandbox
	astWithoutSandbox, err := assistant.LoadPath("/assistants/tests/simple-greeting")
	require.NoError(t, err)
	assert.False(t, astWithoutSandbox.HasSandbox(), "Assistant without sandbox config should return false")
}
