package claude

import (
	"context"
	"fmt"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	agentContext "github.com/yaoapp/yao/agent/context"
	"github.com/yaoapp/yao/agent/output/message"
	"github.com/yaoapp/yao/config"
	infraSandbox "github.com/yaoapp/yao/sandbox"
	"github.com/yaoapp/yao/test"
)

// TestRealClaudeCLIExecution tests real Claude CLI execution with streaming
// This test requires:
// 1. Docker running with yaoapp/sandbox-claude:latest image
// 2. Environment variables: DEEPSEEK_API_KEY, DEEPSEEK_API_PROXY, DEEPSEEK_MODELS_V3
func TestRealClaudeCLIExecution(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping real E2E test in short mode")
	}

	// Check for required environment variables
	apiKey := os.Getenv("DEEPSEEK_API_KEY")
	apiProxy := os.Getenv("DEEPSEEK_API_PROXY")
	model := os.Getenv("DEEPSEEK_MODELS_V3")

	if apiKey == "" || apiProxy == "" || model == "" {
		t.Skip("Skipping test: DEEPSEEK_API_KEY, DEEPSEEK_API_PROXY, or DEEPSEEK_MODELS_V3 not set")
	}

	test.Prepare(t, config.Conf)
	defer test.Clean()

	// Get data root from environment
	dataRoot := os.Getenv("YAO_ROOT")
	if dataRoot == "" {
		t.Skip("Skipping test: YAO_ROOT not set")
	}

	// Create config with proper paths
	cfg := infraSandbox.DefaultConfig()
	cfg.Init(dataRoot)

	manager, err := infraSandbox.NewManager(cfg)
	if err != nil {
		t.Skipf("Skipping test: Docker not available: %v", err)
	}
	defer manager.Close()

	// Create options WITH SystemPrompt (triggers Claude CLI execution)
	opts := &Options{
		Command:       "claude",
		Image:         "yaoapp/sandbox-claude:latest",
		UserID:        "test-user",
		ChatID:        fmt.Sprintf("test-real-e2e-%d", time.Now().UnixNano()),
		ConnectorHost: apiProxy,
		ConnectorKey:  apiKey,
		Model:         model,
		SystemPrompt:  "You are a helpful assistant. Reply concisely.",
		Timeout:       3 * time.Minute,
		ConnectorOptions: map[string]interface{}{
			"max_tokens": 4096, // Limit max_tokens to avoid backend API limits
		},
	}

	t.Logf("Creating executor with options:")
	t.Logf("  ConnectorHost: %s", opts.ConnectorHost)
	t.Logf("  Model: %s", opts.Model)
	t.Logf("  SystemPrompt: %s", opts.SystemPrompt)

	exec, err := NewExecutor(manager, opts)
	if err != nil {
		t.Skipf("Skipping test: Failed to create executor: %v", err)
	}
	defer exec.Close()

	// Verify shouldSkipClaudeCLI returns false
	if exec.shouldSkipClaudeCLI() {
		t.Fatal("shouldSkipClaudeCLI should return false when SystemPrompt is set")
	}

	// Test 1: First, manually test claude-proxy
	t.Log("=== Test 1: Verify claude-proxy is working ===")
	stdCtx := context.Background()

	// Prepare environment (this starts claude-proxy)
	err = exec.prepareEnvironment(stdCtx)
	require.NoError(t, err, "prepareEnvironment should succeed")

	// Check proxy is running
	result, err := exec.manager.Exec(stdCtx, exec.containerName, []string{"pgrep", "-f", "claude-proxy"}, nil)
	if err != nil || result.ExitCode != 0 {
		t.Log("claude-proxy not running, checking why...")
		// Check proxy log
		logContent, _ := exec.ReadFile(stdCtx, "proxy.log")
		t.Logf("Proxy log: %s", string(logContent))

		// Check config
		configContent, _ := exec.ReadFile(stdCtx, ".claude-proxy.json")
		t.Logf("Proxy config: %s", string(configContent))
	} else {
		t.Logf("claude-proxy is running with PID: %s", strings.TrimSpace(result.Stdout))
	}

	// Test 2: Test simple command execution
	t.Log("=== Test 2: Simple command execution ===")
	ctx := agentContext.New(stdCtx, nil, opts.ChatID)
	messages := []agentContext.Message{
		{Role: "user", Content: "Reply with exactly: HELLO_TEST_SUCCESS"},
	}

	// Collect streaming output
	var streamedChunks []string
	var streamedContent strings.Builder
	streamHandler := func(chunkType message.StreamChunkType, data []byte) int {
		chunk := string(data)
		streamedChunks = append(streamedChunks, chunk)
		streamedContent.Write(data)
		t.Logf("Stream chunk [%s]: %q", chunkType, chunk)
		return 0 // continue streaming
	}

	t.Log("Executing Claude CLI...")
	startTime := time.Now()
	response, err := exec.Stream(ctx, messages, streamHandler)
	duration := time.Since(startTime)
	t.Logf("Execution took: %v", duration)

	if err != nil {
		t.Logf("Stream error: %v", err)

		// Debug: check what's in the container
		t.Log("=== Debug info ===")

		// Check proxy log
		logContent, _ := exec.ReadFile(stdCtx, "proxy.log")
		t.Logf("Proxy log:\n%s", string(logContent))

		// List workspace
		output, _ := exec.Exec(stdCtx, []string{"ls", "-la", "/workspace"})
		t.Logf("Workspace contents:\n%s", output)

		// Check environment
		output, _ = exec.Exec(stdCtx, []string{"env"})
		t.Logf("Environment:\n%s", output)

		t.Fatalf("Stream failed: %v", err)
	}

	require.NotNil(t, response, "Response should not be nil")

	// Log results
	t.Logf("=== Results ===")
	t.Logf("Response ID: %s", response.ID)
	t.Logf("Response Model: %s", response.Model)
	t.Logf("Response Content: %v", response.Content)
	t.Logf("Streamed chunks count: %d", len(streamedChunks))
	t.Logf("Total streamed content: %s", streamedContent.String())

	// Verify we got some response
	var fullResponse string
	if content, ok := response.Content.(string); ok {
		fullResponse = content
	}
	if fullResponse == "" {
		fullResponse = streamedContent.String()
	}

	if fullResponse == "" {
		// Check proxy log for errors
		logContent, _ := exec.ReadFile(stdCtx, "proxy.log")
		t.Logf("Proxy log (for debugging):\n%s", string(logContent))
		t.Fatal("Got empty response from Claude CLI")
	}

	t.Logf("✓ Successfully got response: %s", fullResponse)

	// Check if streaming worked
	if len(streamedChunks) > 0 {
		t.Logf("✓ Streaming worked with %d chunks", len(streamedChunks))
	} else {
		t.Log("⚠ No streaming chunks received (might be buffered)")
	}
}

// TestClaudeCLIDirectExecution tests running claude directly in the container
func TestClaudeCLIDirectExecution(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping real E2E test in short mode")
	}

	// Check for required environment variables
	apiKey := os.Getenv("DEEPSEEK_API_KEY")
	apiProxy := os.Getenv("DEEPSEEK_API_PROXY")
	model := os.Getenv("DEEPSEEK_MODELS_V3")

	if apiKey == "" || apiProxy == "" || model == "" {
		t.Skip("Skipping test: DEEPSEEK_API_KEY, DEEPSEEK_API_PROXY, or DEEPSEEK_MODELS_V3 not set")
	}

	test.Prepare(t, config.Conf)
	defer test.Clean()

	dataRoot := os.Getenv("YAO_ROOT")
	if dataRoot == "" {
		t.Skip("Skipping test: YAO_ROOT not set")
	}

	cfg := infraSandbox.DefaultConfig()
	cfg.Init(dataRoot)

	manager, err := infraSandbox.NewManager(cfg)
	if err != nil {
		t.Skipf("Skipping test: Docker not available: %v", err)
	}
	defer manager.Close()

	opts := &Options{
		Command:       "claude",
		Image:         "yaoapp/sandbox-claude:latest",
		UserID:        "test-user",
		ChatID:        fmt.Sprintf("test-direct-%d", time.Now().UnixNano()),
		ConnectorHost: apiProxy,
		ConnectorKey:  apiKey,
		Model:         model,
		Timeout:       3 * time.Minute,
		ConnectorOptions: map[string]interface{}{
			"max_tokens": 4096, // Limit max_tokens to avoid backend API limits
		},
	}

	exec, err := NewExecutor(manager, opts)
	if err != nil {
		t.Skipf("Skipping test: Failed to create executor: %v", err)
	}
	defer exec.Close()

	stdCtx := context.Background()

	// Step 1: Write proxy config and start proxy
	t.Log("=== Step 1: Start claude-proxy ===")
	err = exec.prepareEnvironment(stdCtx)
	require.NoError(t, err)

	// Wait for proxy to start
	time.Sleep(2 * time.Second)

	// Check proxy status
	result, err := exec.manager.Exec(stdCtx, exec.containerName, []string{"pgrep", "-f", "claude-proxy"}, nil)
	if err == nil && result.ExitCode == 0 {
		t.Logf("✓ claude-proxy running, PID: %s", strings.TrimSpace(result.Stdout))
	} else {
		t.Log("⚠ claude-proxy might not be running")
	}

	// Step 2: Run claude CLI directly with simple prompt
	t.Log("=== Step 2: Run claude CLI directly ===")

	// Build a simple command - pass env vars explicitly
	directCmd := []string{
		"bash", "-c",
		`echo '{"type":"user","message":{"role":"user","content":"say hello"}}' | claude -p --dangerously-skip-permissions --permission-mode bypassPermissions --input-format stream-json --output-format stream-json --verbose 2>&1`,
	}

	reader, err := exec.manager.Stream(stdCtx, exec.containerName, directCmd, &infraSandbox.ExecOptions{
		WorkDir: exec.workDir,
		Timeout: 2 * time.Minute,
		Env: map[string]string{
			"ANTHROPIC_BASE_URL": "http://127.0.0.1:3456",
			"ANTHROPIC_API_KEY":  "dummy",
		},
	})
	if err != nil {
		t.Fatalf("Failed to execute: %v", err)
	}
	defer reader.Close()

	// Read output
	buf := make([]byte, 64*1024)
	var output strings.Builder
	for {
		n, err := reader.Read(buf)
		if n > 0 {
			chunk := string(buf[:n])
			output.WriteString(chunk)
			t.Logf("Output chunk: %q", chunk)
		}
		if err != nil {
			break
		}
	}

	t.Logf("=== Full output ===\n%s", output.String())

	if output.Len() == 0 {
		// Check logs
		logContent, _ := exec.ReadFile(stdCtx, "proxy.log")
		t.Logf("Proxy log:\n%s", string(logContent))
		t.Fatal("Got no output from claude CLI")
	}

	// Check for success indicators
	outputStr := output.String()
	if strings.Contains(outputStr, "error") || strings.Contains(outputStr, "Error") {
		t.Logf("⚠ Output contains error")
	}
	if strings.Contains(outputStr, "content_block") || strings.Contains(outputStr, "message_start") {
		t.Log("✓ Got streaming JSON output from Claude CLI")
	}
}
