package routing

import (
	"encoding/json"
	"testing"
)

func TestParseIntentResponse(t *testing.T) {
	tests := []struct {
		name     string
		response string
		wantErr  bool
		validate func(*testing.T, *Intent)
	}{
		{
			name: "basic intent",
			response: `{
				"activities": ["CloneRepoActivity", "AnalyzeCodeActivity"],
				"parameters": {"repo": "https://github.com/test/repo"},
				"isCron": false,
				"workflowName": "analyze-repo"
			}`,
			wantErr: false,
			validate: func(t *testing.T, intent *Intent) {
				if len(intent.Activities) != 2 {
					t.Errorf("expected 2 activities, got %d", len(intent.Activities))
				}
				if intent.Activities[0] != "CloneRepoActivity" {
					t.Errorf("expected CloneRepoActivity first, got %s", intent.Activities[0])
				}
				if intent.IsCron {
					t.Error("expected isCron=false")
				}
			},
		},
		{
			name: "cron intent",
			response: `{
				"activities": ["CloneRepoActivity", "SecurityScanActivity"],
				"parameters": {"repo": "https://github.com/test/repo"},
				"isCron": true,
				"cronSchedule": "0 2 * * *",
				"cronTimezone": "America/New_York",
				"workflowName": "daily-security-scan"
			}`,
			wantErr: false,
			validate: func(t *testing.T, intent *Intent) {
				if !intent.IsCron {
					t.Error("expected isCron=true")
				}
				if intent.CronSchedule != "0 2 * * *" {
					t.Errorf("expected cron schedule '0 2 * * *', got %s", intent.CronSchedule)
				}
				if intent.CronTimezone != "America/New_York" {
					t.Errorf("expected timezone 'America/New_York', got %s", intent.CronTimezone)
				}
			},
		},
		{
			name: "with markdown code block",
			response: "```json\n{\"activities\": [\"CloneRepoActivity\"], \"parameters\": {}, \"isCron\": false}\n```",
			wantErr: false,
			validate: func(t *testing.T, intent *Intent) {
				if len(intent.Activities) != 1 {
					t.Errorf("expected 1 activity, got %d", len(intent.Activities))
				}
			},
		},
		{
			name: "defaults applied",
			response: `{"activities": ["CloneRepoActivity"], "parameters": {}}`,
			wantErr: false,
			validate: func(t *testing.T, intent *Intent) {
				if intent.CronTimezone != "UTC" {
					t.Errorf("expected default timezone UTC, got %s", intent.CronTimezone)
				}
				if intent.ErrorHandling != "retry" {
					t.Errorf("expected default errorHandling 'retry', got %s", intent.ErrorHandling)
				}
				if intent.WorkflowName != "generated-workflow" {
					t.Errorf("expected default workflowName, got %s", intent.WorkflowName)
				}
			},
		},
		{
			name:    "invalid json",
			response: "this is not json",
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			intent, err := parseIntentResponse(tt.response)
			if tt.wantErr {
				if err == nil {
					t.Error("expected error, got nil")
				}
				return
			}
			if err != nil {
				t.Errorf("unexpected error: %v", err)
				return
			}
			if tt.validate != nil {
				tt.validate(t, intent)
			}
		})
	}
}

func TestBuildSpec(t *testing.T) {
	// Load knowledge base
	kb, err := LoadKnowledgeBaseFromDefaultPath()
	if err != nil {
		t.Fatalf("failed to load knowledge base: %v", err)
	}

	router := &LLMRouter{
		knowledgeBase: kb,
	}

	intent := &Intent{
		Activities:    []string{"CloneRepoActivity", "AnalyzeCodeActivity", "SecurityScanActivity"},
		Parameters:    map[string]interface{}{"repo": "https://github.com/test/repo", "branch": "main"},
		WorkflowName:  "test-workflow",
		ErrorHandling: "retry",
	}

	input := LLMRouterInput{
		Message: "Analyze repo for security",
		Context: map[string]interface{}{},
	}

	spec, err := router.buildSpec(intent, input)
	if err != nil {
		t.Fatalf("buildSpec failed: %v", err)
	}

	// Validate spec
	if spec.Name != "test-workflow" {
		t.Errorf("expected name 'test-workflow', got %s", spec.Name)
	}

	if len(spec.States) < 3 {
		t.Errorf("expected at least 3 states, got %d", len(spec.States))
	}

	// First state should be CloneRepoActivity
	if spec.States[0].Resource != "CloneRepoActivity" {
		t.Errorf("expected first state to be CloneRepoActivity, got %s", spec.States[0].Resource)
	}

	// Last activity state should have End=true
	lastActivityIdx := len(spec.States) - 1
	if spec.States[lastActivityIdx].Type == StateTypeFail {
		lastActivityIdx--
	}
	if !spec.States[lastActivityIdx].End {
		t.Error("expected last activity state to have End=true")
	}

	// Check retry policy on flaky activity (AnalyzeCodeActivity)
	for _, state := range spec.States {
		if state.Resource == "AnalyzeCodeActivity" {
			if state.Retry == nil {
				t.Error("expected retry policy on flaky activity")
			} else if state.Retry.MaxAttempts != 3 {
				t.Errorf("expected 3 max attempts for flaky activity, got %d", state.Retry.MaxAttempts)
			}
			if len(state.Catch) == 0 {
				t.Error("expected catch clause on flaky activity")
			}
		}
	}
}

func TestBuildCronSpec(t *testing.T) {
	kb, err := LoadKnowledgeBaseFromDefaultPath()
	if err != nil {
		t.Fatalf("failed to load knowledge base: %v", err)
	}

	router := &LLMRouter{
		knowledgeBase: kb,
	}

	intent := &Intent{
		Activities:   []string{"CloneRepoActivity", "SecurityScanActivity"},
		Parameters:   map[string]interface{}{"repo": "https://github.com/test/repo"},
		IsCron:       true,
		CronSchedule: "0 2 * * *",
		CronTimezone: "UTC",
		WorkflowName: "daily-scan",
	}

	input := LLMRouterInput{
		Message: "Run security scan daily at 2 AM",
	}

	cronSpec, err := router.buildCronSpec(intent, input)
	if err != nil {
		t.Fatalf("buildCronSpec failed: %v", err)
	}

	if cronSpec.Type != "CronWorkflow" {
		t.Errorf("expected type 'CronWorkflow', got %s", cronSpec.Type)
	}
	if cronSpec.Schedule != "0 2 * * *" {
		t.Errorf("expected schedule '0 2 * * *', got %s", cronSpec.Schedule)
	}
	if cronSpec.Timezone != "UTC" {
		t.Errorf("expected timezone 'UTC', got %s", cronSpec.Timezone)
	}
	if !cronSpec.EnableHistory {
		t.Error("expected EnableHistory=true")
	}
}

func TestBuildParameters(t *testing.T) {
	kb, err := LoadKnowledgeBaseFromDefaultPath()
	if err != nil {
		t.Fatalf("failed to load knowledge base: %v", err)
	}

	router := &LLMRouter{
		knowledgeBase: kb,
	}

	// Test first activity (CloneRepoActivity) - should use input references
	cloneAct := kb.GetActivity("CloneRepoActivity")
	intent := &Intent{
		Activities: []string{"CloneRepoActivity", "AnalyzeCodeActivity"},
		Parameters: map[string]interface{}{"repo": "https://github.com/test/repo"},
	}

	params := router.buildParameters(cloneAct, intent, 0)
	if params["repo"] != "https://github.com/test/repo" {
		t.Errorf("expected repo from parameters, got %v", params["repo"])
	}

	// Test second activity (AnalyzeCodeActivity) - should reference previous output
	analyzeAct := kb.GetActivity("AnalyzeCodeActivity")
	params = router.buildParameters(analyzeAct, intent, 1)
	if params["path"] != "${CloneRepoActivity.output.path}" {
		t.Errorf("expected JSONPath reference to CloneRepoActivity.output.path, got %v", params["path"])
	}
}

func TestBuildRetryPolicy(t *testing.T) {
	kb, err := LoadKnowledgeBaseFromDefaultPath()
	if err != nil {
		t.Fatalf("failed to load knowledge base: %v", err)
	}

	router := &LLMRouter{
		knowledgeBase: kb,
	}

	// Flaky activity with retry error handling
	analyzeAct := kb.GetActivity("AnalyzeCodeActivity")
	intent := &Intent{ErrorHandling: "retry"}
	policy := router.buildRetryPolicy(analyzeAct, intent)

	if policy.MaxAttempts != 3 {
		t.Errorf("expected 3 max attempts for flaky activity, got %d", policy.MaxAttempts)
	}
	if policy.BackoffRate != 2.0 {
		t.Errorf("expected backoff rate 2.0, got %f", policy.BackoffRate)
	}

	// Fail-fast error handling
	intent = &Intent{ErrorHandling: "fail-fast"}
	policy = router.buildRetryPolicy(analyzeAct, intent)

	if policy.MaxAttempts != 1 {
		t.Errorf("expected 1 max attempt for fail-fast, got %d", policy.MaxAttempts)
	}
}

func TestIntentJSONMarshal(t *testing.T) {
	intent := &Intent{
		Activities:    []string{"CloneRepoActivity"},
		Parameters:    map[string]interface{}{"repo": "https://test"},
		IsCron:        true,
		CronSchedule:  "0 * * * *",
		CronTimezone:  "UTC",
		WorkflowName:  "test",
		ErrorHandling: "retry",
	}

	data, err := json.Marshal(intent)
	if err != nil {
		t.Fatalf("marshal failed: %v", err)
	}

	var decoded Intent
	if err := json.Unmarshal(data, &decoded); err != nil {
		t.Fatalf("unmarshal failed: %v", err)
	}

	if decoded.CronSchedule != intent.CronSchedule {
		t.Errorf("expected schedule %s, got %s", intent.CronSchedule, decoded.CronSchedule)
	}
}
