package main

import (
	"context"
	"encoding/json"
	"os"
	"strings"
	"testing"
	"time"

	"gogs.dmsc.dev/arp/arp_cli/cmd"
	"gogs.dmsc.dev/arp/arp_cli/config"

	"github.com/urfave/cli/v3"
)

// Integration tests for arp_cli workflow commands
// These tests require a running ARP server and test the full CLI stack

var (
	testServerURL string
	testToken     string
	testUserID    string
)

// TestMain sets up the integration test environment
func TestMain(m *testing.M) {
	// Check for test server URL
	testServerURL = os.Getenv("ARP_TEST_URL")
	if testServerURL == "" {
		testServerURL = "http://localhost:8080/query"
	}

	// Run tests
	code := m.Run()
	os.Exit(code)
}

// setupTestAuth creates a test configuration with authentication
func setupTestAuth(t *testing.T) {
	t.Helper()

	// Create test config
	cfg := &config.Config{
		ServerURL: testServerURL,
		Token:     testToken,
	}

	if err := config.Save(cfg); err != nil {
		t.Fatalf("Failed to save test config: %v", err)
	}
}

// TestIntegration_WorkflowTemplateLifecycle tests the full lifecycle of workflow templates
func TestIntegration_WorkflowTemplateLifecycle(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	// This test would require a running server with authentication
	// For now, we'll test the command structure and flag parsing

	app := buildTestApp()

	// Test 1: List templates (should work even with no auth for structure test)
	t.Run("TemplateList", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "template", "list", "--json"}
		err := app.Run(context.Background(), args)
		// We expect an error about missing auth, but the command structure should be valid
		if err != nil && !strings.Contains(err.Error(), "not authenticated") {
			t.Logf("Expected auth error or success, got: %v", err)
		}
	})

	// Test 2: Get template with missing ID
	t.Run("TemplateGetMissingID", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "template", "get"}
		err := app.Run(context.Background(), args)
		if err == nil {
			t.Error("Expected error for missing required flag")
		}
	})

	// Test 3: Create template with flags
	t.Run("TemplateCreateFlags", func(t *testing.T) {
		args := []string{
			"arp_cli", "workflow", "template", "create",
			"--name", "Test Template",
			"--description", "A test workflow template",
			"--definition", `{"nodes": [], "edges": []}`,
		}
		err := app.Run(context.Background(), args)
		// We expect an error about missing auth
		if err != nil && !strings.Contains(err.Error(), "not authenticated") {
			t.Logf("Expected auth error or success, got: %v", err)
		}
	})
}

// TestIntegration_WorkflowInstanceLifecycle tests the full lifecycle of workflow instances
func TestIntegration_WorkflowInstanceLifecycle(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	app := buildTestApp()

	// Test 1: List instances
	t.Run("InstanceList", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "instance", "list", "--json"}
		err := app.Run(context.Background(), args)
		if err != nil && !strings.Contains(err.Error(), "not authenticated") {
			t.Logf("Expected auth error or success, got: %v", err)
		}
	})

	// Test 2: Start instance with missing template
	t.Run("InstanceStartMissingTemplate", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "instance", "start"}
		err := app.Run(context.Background(), args)
		if err == nil {
			t.Error("Expected error for missing required flag")
		}
	})

	// Test 3: Cancel instance with missing ID
	t.Run("InstanceCancelMissingID", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "instance", "cancel"}
		err := app.Run(context.Background(), args)
		if err == nil {
			t.Error("Expected error for missing required flag")
		}
	})
}

// TestIntegration_WorkflowNodeLifecycle tests the full lifecycle of workflow nodes
func TestIntegration_WorkflowNodeLifecycle(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	app := buildTestApp()

	// Test 1: List nodes with missing instance
	t.Run("NodeListMissingInstance", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "node", "list"}
		err := app.Run(context.Background(), args)
		if err == nil {
			t.Error("Expected error for missing required flag")
		}
	})

	// Test 2: Get node with missing ID
	t.Run("NodeGetMissingID", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "node", "get"}
		err := app.Run(context.Background(), args)
		if err == nil {
			t.Error("Expected error for missing required flag")
		}
	})

	// Test 3: Retry node with missing ID
	t.Run("NodeRetryMissingID", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "node", "retry"}
		err := app.Run(context.Background(), args)
		if err == nil {
			t.Error("Expected error for missing required flag")
		}
	})
}

// TestIntegration_CommandHelp tests that help is available for all workflow commands
func TestIntegration_CommandHelp(t *testing.T) {
	app := buildTestApp()

	tests := []struct {
		name string
		args []string
	}{
		{"workflow help", []string{"arp_cli", "workflow", "--help"}},
		{"template help", []string{"arp_cli", "workflow", "template", "--help"}},
		{"instance help", []string{"arp_cli", "workflow", "instance", "--help"}},
		{"node help", []string{"arp_cli", "workflow", "node", "--help"}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := app.Run(context.Background(), tt.args)
			if err != nil {
				t.Errorf("Help command failed: %v", err)
			}
		})
	}
}

// TestIntegration_JSONOutput tests JSON output format
func TestIntegration_JSONOutput(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	app := buildTestApp()

	// Test that JSON flag is accepted
	t.Run("TemplateListJSON", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "template", "list", "--json"}
		err := app.Run(context.Background(), args)
		// We expect an error about missing auth
		if err != nil && !strings.Contains(err.Error(), "not authenticated") {
			t.Logf("Expected auth error or success, got: %v", err)
		}
	})

	t.Run("InstanceListJSON", func(t *testing.T) {
		args := []string{"arp_cli", "workflow", "instance", "list", "--json"}
		err := app.Run(context.Background(), args)
		if err != nil && !strings.Contains(err.Error(), "not authenticated") {
			t.Logf("Expected auth error or success, got: %v", err)
		}
	})
}

// TestIntegration_FileInput tests file input for workflow definitions
func TestIntegration_FileInput(t *testing.T) {
	// Create a temporary file with workflow definition
	tmpFile, err := os.CreateTemp("", "workflow_*.json")
	if err != nil {
		t.Fatalf("Failed to create temp file: %v", err)
	}
	defer os.Remove(tmpFile.Name())

	definition := `{
		"nodes": [
			{"id": "start", "type": "start"},
			{"id": "task1", "type": "task"},
			{"id": "end", "type": "end"}
		],
		"edges": [
			{"from": "start", "to": "task1"},
			{"from": "task1", "to": "end"}
		]
	}`

	if _, err := tmpFile.WriteString(definition); err != nil {
		t.Fatalf("Failed to write to temp file: %v", err)
	}
	tmpFile.Close()

	// Test that readFileOrString works correctly
	result := cmd.ReadFileOrString("@" + tmpFile.Name())
	if result != definition {
		t.Errorf("File content mismatch.\nExpected: %s\nGot: %s", definition, result)
	}
}

// TestIntegration_CommandAliases tests command aliases
func TestIntegration_CommandAliases(t *testing.T) {
	app := buildTestApp()

	tests := []struct {
		name string
		args []string
	}{
		{"template list alias", []string{"arp_cli", "workflow", "template", "ls"}},
		{"instance list alias", []string{"arp_cli", "workflow", "instance", "ls"}},
		{"node list alias", []string{"arp_cli", "workflow", "node", "ls"}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := app.Run(context.Background(), tt.args)
			// We expect an error about missing auth
			if err != nil && !strings.Contains(err.Error(), "not authenticated") {
				t.Logf("Expected auth error or success, got: %v", err)
			}
		})
	}
}

// TestIntegration_MultipleFlags tests commands with multiple flags
func TestIntegration_MultipleFlags(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	app := buildTestApp()

	t.Run("StartWithAllFlags", func(t *testing.T) {
		args := []string{
			"arp_cli", "workflow", "instance", "start",
			"--template", "template-123",
			"--service", "service-456",
			"--context", `{"key": "value"}`,
		}
		err := app.Run(context.Background(), args)
		// We expect an error about missing auth
		if err != nil && !strings.Contains(err.Error(), "not authenticated") {
			t.Logf("Expected auth error or success, got: %v", err)
		}
	})

	t.Run("CreateWithAllFlags", func(t *testing.T) {
		args := []string{
			"arp_cli", "workflow", "template", "create",
			"--name", "Test Workflow",
			"--description", "Test Description",
			"--definition", `{"nodes": []}`,
			"--active",
		}
		err := app.Run(context.Background(), args)
		if err != nil && !strings.Contains(err.Error(), "not authenticated") {
			t.Logf("Expected auth error or success, got: %v", err)
		}
	})
}

// TestIntegration_ErrorHandling tests error handling for required flags
func TestIntegration_ErrorHandling(t *testing.T) {
	app := buildTestApp()

	tests := []struct {
		name        string
		args        []string
		expectError bool
	}{
		{"missing template id", []string{"arp_cli", "workflow", "template", "get"}, true},
		{"missing instance id", []string{"arp_cli", "workflow", "instance", "get"}, true},
		{"missing node id", []string{"arp_cli", "workflow", "node", "get"}, true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := app.Run(context.Background(), tt.args)
			hasError := err != nil
			if hasError != tt.expectError {
				t.Errorf("Expected error=%v, got error=%v (%v)", tt.expectError, hasError, err)
			}
		})
	}
}

// TestIntegration_WorkflowDefinitionValidation tests workflow definition handling
func TestIntegration_WorkflowDefinitionValidation(t *testing.T) {
	// Test various workflow definition formats
	tests := []struct {
		name       string
		definition string
		valid      bool
	}{
		{
			name:       "empty definition",
			definition: `{}`,
			valid:      true,
		},
		{
			name:       "simple DAG",
			definition: `{"nodes": [{"id": "a"}], "edges": []}`,
			valid:      true,
		},
		{
			name:       "complex DAG",
			definition: `{"nodes": [{"id": "start", "type": "start"}, {"id": "end", "type": "end"}], "edges": [{"from": "start", "to": "end"}]}`,
			valid:      true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Verify the definition is valid JSON
			var data interface{}
			err := json.Unmarshal([]byte(tt.definition), &data)
			if tt.valid && err != nil {
				t.Errorf("Expected valid JSON, got error: %v", err)
			}
			if !tt.valid && err == nil {
				t.Error("Expected invalid JSON, but parsing succeeded")
			}
		})
	}
}

// buildTestApp creates a CLI app for testing
func buildTestApp() *cli.Command {
	return &cli.Command{
		Name:     "arp_cli",
		Usage:    "Test CLI",
		Commands: []*cli.Command{cmd.WorkflowCommand()},
	}
}

// Helper function to check if a string contains a substring
func containsString(s, substr string) bool {
	return strings.Contains(s, substr)
}

// BenchmarkWorkflowCommand benchmarks the workflow command execution
func BenchmarkWorkflowCommand(b *testing.B) {
	app := buildTestApp()
	args := []string{"arp_cli", "workflow", "--help"}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = app.Run(context.Background(), args)
	}
}

// BenchmarkWorkflowTemplateList benchmarks the template list command
func BenchmarkWorkflowTemplateList(b *testing.B) {
	app := buildTestApp()
	args := []string{"arp_cli", "workflow", "template", "list", "--json"}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = app.Run(context.Background(), args)
	}
}

// Mock test for context handling
func TestContextHandling(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	app := buildTestApp()
	args := []string{"arp_cli", "workflow", "template", "list"}

	// The command should respect context cancellation
	done := make(chan error, 1)
	go func() {
		done <- app.Run(ctx, args)
	}()

	select {
	case <-ctx.Done():
		t.Log("Context cancelled")
	case err := <-done:
		if err != nil {
			t.Logf("Command completed with error: %v", err)
		}
	case <-time.After(10 * time.Second):
		t.Error("Command took too long")
	}
}
