package claudecode

import (
	"context"
	"strings"
	"testing"

	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/port"
)

// Ensure Adapter implements port.CodeAgent at compile time.
var _ port.CodeAgent = (*Adapter)(nil)

func TestAdapter_Name(t *testing.T) {
	adapter := NewAdapter("test-ns")
	if name := adapter.Name(); name != "Claude Code" {
		t.Errorf("expected name 'Claude Code', got %q", name)
	}
}

func TestAdapter_Provider(t *testing.T) {
	adapter := NewAdapter("test-ns")
	if p := adapter.Provider(); p != domain.AgentProviderClaudeCode {
		t.Errorf("expected provider 'claudecode', got %q", p)
	}
}

func TestAdapter_Capabilities(t *testing.T) {
	adapter := NewAdapter("test-ns")
	caps := adapter.Capabilities()

	if caps.Provider != domain.AgentProviderClaudeCode {
		t.Errorf("expected provider claudecode")
	}
	if !caps.SupportsSessionContinuation {
		t.Error("expected session continuation support")
	}
	if caps.SupportsModelSelection {
		t.Error("expected no model selection support")
	}
	if !caps.SupportsToolControl {
		t.Error("expected tool control support")
	}
	if !caps.SupportsStreaming {
		t.Error("expected streaming support")
	}
	if len(caps.SupportedModels) == 0 {
		t.Error("expected at least one supported model")
	}
}

func TestAdapter_buildCommandArgs_Basic(t *testing.T) {
	adapter := NewAdapter("rdev")
	req := &domain.AgentRequest{
		Prompt: "Hello, Claude",
	}

	args := adapter.buildCommandArgs("rdev", "pod-123", req)

	// Verify essential args are present
	argsStr := strings.Join(args, " ")

	if !strings.Contains(argsStr, "exec -n rdev pod-123") {
		t.Error("expected kubectl exec command")
	}
	if !strings.Contains(argsStr, "claude") {
		t.Error("expected claude command")
	}
	if !strings.Contains(argsStr, "-p") {
		t.Error("expected -p flag")
	}
	if !strings.Contains(argsStr, "--output-format stream-json") {
		t.Error("expected stream-json output format")
	}
	if !strings.Contains(argsStr, "--verbose") {
		t.Error("expected --verbose flag for stream-json output")
	}
	// Should include default allowed tools instead of --dangerously-skip-permissions
	expectedTools := []string{"Bash", "Edit", "Write", "Read", "Glob", "Grep", "Task", "WebFetch", "WebSearch"}
	for _, tool := range expectedTools {
		if !strings.Contains(argsStr, "--allowedTools "+tool) {
			t.Errorf("expected default allowed tool: %s", tool)
		}
	}
	if !strings.Contains(argsStr, "Hello, Claude") {
		t.Error("expected prompt in args")
	}
}

func TestAdapter_buildCommandArgs_WithSessionResume(t *testing.T) {
	adapter := NewAdapter("rdev")
	req := &domain.AgentRequest{
		Prompt:    "Continue working",
		SessionID: "session-abc",
	}

	args := adapter.buildCommandArgs("rdev", "pod-123", req)
	argsStr := strings.Join(args, " ")

	if !strings.Contains(argsStr, "--resume session-abc") {
		t.Error("expected --resume flag with session ID")
	}
}

func TestAdapter_buildCommandArgs_WithAllowedTools(t *testing.T) {
	adapter := NewAdapter("rdev")
	req := &domain.AgentRequest{
		Prompt:       "Run tests",
		AllowedTools: []string{"Bash", "Read", "Edit"},
	}

	args := adapter.buildCommandArgs("rdev", "pod-123", req)
	argsStr := strings.Join(args, " ")

	if !strings.Contains(argsStr, "--allowedTools Bash") {
		t.Error("expected --allowedTools Bash")
	}
	if !strings.Contains(argsStr, "--allowedTools Read") {
		t.Error("expected --allowedTools Read")
	}
	if !strings.Contains(argsStr, "--allowedTools Edit") {
		t.Error("expected --allowedTools Edit")
	}
}

func TestAdapter_buildCommandArgs_WithWorkingDir(t *testing.T) {
	adapter := NewAdapter("rdev")
	req := &domain.AgentRequest{
		Prompt:     "List files",
		WorkingDir: "/workspace/subdir",
	}

	args := adapter.buildCommandArgs("rdev", "pod-123", req)
	argsStr := strings.Join(args, " ")

	if !strings.Contains(argsStr, "--add-dir /workspace/subdir") {
		t.Error("expected --add-dir for non-default working directory")
	}
}

func TestAdapter_buildCommandArgs_DefaultWorkingDir(t *testing.T) {
	adapter := NewAdapter("rdev")
	req := &domain.AgentRequest{
		Prompt:     "List files",
		WorkingDir: "/workspace", // default
	}

	args := adapter.buildCommandArgs("rdev", "pod-123", req)
	argsStr := strings.Join(args, " ")

	// Should NOT have --add-dir for default workspace
	if strings.Contains(argsStr, "--add-dir") {
		t.Error("expected no --add-dir for default workspace")
	}
}

func TestAdapter_Execute_MissingPrompt(t *testing.T) {
	adapter := NewAdapter("rdev")
	req := &domain.AgentRequest{
		Prompt: "", // missing
		Metadata: map[string]string{
			"pod_name": "test-pod",
		},
	}

	_, err := adapter.Execute(context.Background(), req, func(e domain.AgentEvent) {})
	if err == nil {
		t.Error("expected error for missing prompt")
	}
	if !strings.Contains(err.Error(), "prompt is required") {
		t.Errorf("expected 'prompt is required' error, got: %v", err)
	}
}

func TestAdapter_Execute_MissingPodName(t *testing.T) {
	adapter := NewAdapter("rdev")
	req := &domain.AgentRequest{
		Prompt:   "Hello",
		Metadata: map[string]string{}, // no pod_name
	}

	result, _ := adapter.Execute(context.Background(), req, func(e domain.AgentEvent) {})
	if result.Error == nil {
		t.Error("expected error for missing pod_name")
	}
	if !strings.Contains(result.Error.Error(), "pod_name") {
		t.Errorf("expected pod_name error, got: %v", result.Error)
	}
}

func TestAdapter_Cancel(t *testing.T) {
	adapter := NewAdapter("rdev")

	// Cancel non-existent session should not error
	err := adapter.Cancel(context.Background(), "nonexistent")
	if err != nil {
		t.Errorf("expected no error for non-existent session, got: %v", err)
	}
}

func TestAdapter_parseStreamOutput(t *testing.T) {
	adapter := NewAdapter("rdev")

	input := strings.NewReader(`{"type":"init","session_id":"test-123"}
{"type":"message","role":"assistant","content":[{"type":"text","text":"Hello!"}]}
{"type":"result","subtype":"success","is_error":false,"duration_ms":100}
`)

	var events []domain.AgentEvent
	handler := func(e domain.AgentEvent) {
		events = append(events, e)
	}

	var output strings.Builder
	resultMsg, err := adapter.parseStreamOutput(input, handler, &output)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if len(events) != 3 {
		t.Errorf("expected 3 events, got %d", len(events))
	}

	// Verify event types
	if events[0].Type != domain.AgentEventOutput {
		t.Errorf("expected first event to be output (init)")
	}
	if events[1].Type != domain.AgentEventOutput {
		t.Errorf("expected second event to be output (message)")
	}
	if events[2].Type != domain.AgentEventComplete {
		t.Errorf("expected third event to be complete (result)")
	}

	// Verify result message
	if resultMsg == nil {
		t.Fatal("expected result message")
	}
	if !resultMsg.IsSuccess() {
		t.Error("expected success result")
	}

	// Verify output was collected
	if !strings.Contains(output.String(), "Hello!") {
		t.Error("expected output to contain assistant message")
	}
}

func TestAdapter_parseStreamOutput_PlainText(t *testing.T) {
	adapter := NewAdapter("rdev")

	// Non-JSON lines should be treated as plain output
	input := strings.NewReader(`Not JSON output
Another plain line
{"type":"result","status":"success"}
`)

	var events []domain.AgentEvent
	handler := func(e domain.AgentEvent) {
		events = append(events, e)
	}

	var output strings.Builder
	_, err := adapter.parseStreamOutput(input, handler, &output)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if len(events) != 3 {
		t.Errorf("expected 3 events, got %d", len(events))
	}

	// First two should be plain output events
	if events[0].Content != "Not JSON output" {
		t.Errorf("expected plain text content")
	}
	if events[0].Stream != "stdout" {
		t.Errorf("expected stdout stream for plain text")
	}
}

func TestGenerateSessionID(t *testing.T) {
	id1 := generateSessionID()
	id2 := generateSessionID()

	if id1 == id2 {
		t.Error("expected unique session IDs")
	}

	if !strings.HasPrefix(id1, "claude-") {
		t.Errorf("expected session ID to start with 'claude-', got %q", id1)
	}
}
