package signoz

import (
	"context"
	"testing"
	"time"

	"github.com/CalmoAI/mcp-server-signoz/internal/domain"
	"github.com/CalmoAI/mcp-server-signoz/internal/infrastructure/config"
)

func TestValidateInputs(t *testing.T) {
	// Create a test client
	cfg := &config.SignozConfig{
		URL:    "http://localhost:3301",
		APIKey: "test-key",
	}
	client, err := NewClient(cfg)
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}

	tests := []struct {
		name          string
		dataType      string
		serviceName   string
		limit         int
		expectError   bool
		errorContains string
	}{
		{
			name:        "Valid traces input",
			dataType:    "traces",
			serviceName: "my-service",
			limit:       100,
			expectError: false,
		},
		{
			name:        "Valid logs input",
			dataType:    "logs",
			serviceName: "another-service",
			limit:       500,
			expectError: false,
		},
		{
			name:          "Invalid dataType",
			dataType:      "invalid",
			serviceName:   "my-service",
			limit:         100,
			expectError:   true,
			errorContains: "invalid dataType",
		},
		{
			name:          "Empty serviceName",
			dataType:      "traces",
			serviceName:   "",
			limit:         100,
			expectError:   true,
			errorContains: "serviceName cannot be empty",
		},
		{
			name:          "SQL injection with single quote",
			dataType:      "traces",
			serviceName:   "my-service'; DROP TABLE users; --",
			limit:         100,
			expectError:   true,
			errorContains: "potentially dangerous characters",
		},
		{
			name:          "SQL injection with double quote",
			dataType:      "traces",
			serviceName:   "my-service\" OR 1=1",
			limit:         100,
			expectError:   true,
			errorContains: "potentially dangerous characters",
		},
		{
			name:          "SQL injection with semicolon",
			dataType:      "traces",
			serviceName:   "my-service; DELETE FROM logs",
			limit:         100,
			expectError:   true,
			errorContains: "potentially dangerous characters",
		},
		{
			name:          "SQL injection with comment",
			dataType:      "traces",
			serviceName:   "my-service -- comment",
			limit:         100,
			expectError:   true,
			errorContains: "potentially dangerous characters",
		},
		{
			name:        "Service name with SQL keywords (allowed)",
			dataType:    "traces",
			serviceName: "data-from-api-service",
			limit:       100,
			expectError: false,
		},
		{
			name:          "SQL injection with quotes and SELECT",
			dataType:      "traces",
			serviceName:   "my-service' OR 1=1; SELECT * FROM users",
			limit:         100,
			expectError:   true,
			errorContains: "potentially dangerous characters",
		},
		{
			name:          "SQL injection with quotes and DROP",
			dataType:      "traces",
			serviceName:   "my-service'); DROP DATABASE signoz",
			limit:         100,
			expectError:   true,
			errorContains: "potentially dangerous characters",
		},
		{
			name:          "Limit too small",
			dataType:      "traces",
			serviceName:   "my-service",
			limit:         0,
			expectError:   true,
			errorContains: "limit must be between 1 and 10000",
		},
		{
			name:          "Limit too large",
			dataType:      "traces",
			serviceName:   "my-service",
			limit:         15000,
			expectError:   true,
			errorContains: "limit must be between 1 and 10000",
		},
		{
			name:        "Service name with hyphen (valid)",
			dataType:    "traces",
			serviceName: "my-service-name",
			limit:       100,
			expectError: false,
		},
		{
			name:        "Service name with underscore (valid)",
			dataType:    "traces",
			serviceName: "my_service_name",
			limit:       100,
			expectError: false,
		},
		{
			name:        "Service name with numbers (valid)",
			dataType:    "traces",
			serviceName: "service123",
			limit:       100,
			expectError: false,
		},
		{
			name:        "Service name with SELECT keyword (valid)",
			dataType:    "traces",
			serviceName: "user-select-service",
			limit:       100,
			expectError: false,
		},
		{
			name:        "Service name with FROM keyword (valid)",
			dataType:    "traces",
			serviceName: "data-from-cache",
			limit:       100,
			expectError: false,
		},
		{
			name:        "Service name with WHERE keyword (valid)",
			dataType:    "traces",
			serviceName: "somewhere-api",
			limit:       100,
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := client.validateInputs(tt.dataType, tt.serviceName, tt.limit)

			if tt.expectError {
				if err == nil {
					t.Errorf("Expected error but got none")
					return
				}
				if tt.errorContains != "" && !contains(err.Error(), tt.errorContains) {
					t.Errorf("Expected error to contain '%s' but got: %v", tt.errorContains, err)
				}
			} else {
				if err != nil {
					t.Errorf("Expected no error but got: %v", err)
				}
			}
		})
	}
}

func TestGetTracesOrLogsInputValidation(t *testing.T) {
	// Create a test client
	cfg := &config.SignozConfig{
		URL:    "http://localhost:3301",
		APIKey: "test-key",
	}
	client, err := NewClient(cfg)
	if err != nil {
		t.Fatalf("Failed to create client: %v", err)
	}

	ctx := context.Background()
	timeRange := domain.TimeRange{
		Start: time.Now().Add(-1 * time.Hour),
		End:   time.Now(),
	}

	// Test that dangerous character injection attempts are blocked
	dangerousCharTests := []struct {
		name        string
		serviceName string
	}{
		{"Single quote injection", "test'; DROP TABLE users; --"},
		{"Double quote injection", "test\" OR 1=1"},
		{"Comment injection", "test -- malicious comment"},
		{"Semicolon injection", "test; DELETE FROM logs"},
		{"Block comment injection", "test /* comment */ service"},
	}

	for _, tt := range dangerousCharTests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := client.GetTracesOrLogs(ctx, "traces", timeRange, tt.serviceName, 100)
			if err == nil {
				t.Errorf("Expected error for SQL injection attempt but got none")
				return
			}
			if !contains(err.Error(), "potentially dangerous characters") {
				t.Errorf("Expected validation error but got: %v", err)
			}
		})
	}

	// Test that legitimate service names with SQL keywords are now allowed
	legitimateTests := []struct {
		name        string
		serviceName string
	}{
		{"Service with FROM keyword", "data-from-api"},
		{"Service with SELECT keyword", "user-select-service"},
		{"Service with WHERE keyword", "somewhere-service"},
		{"Service with UNION keyword", "union-service"},
	}

	for _, tt := range legitimateTests {
		t.Run(tt.name, func(t *testing.T) {
			// This should not return a validation error (though it may fail for other reasons like network)
			_, err := client.GetTracesOrLogs(ctx, "traces", timeRange, tt.serviceName, 100)
			if err != nil && contains(err.Error(), "potentially dangerous characters") {
				t.Errorf("Expected legitimate service name '%s' to pass validation but got: %v", tt.serviceName, err)
			}
		})
	}
}

// Helper function to check if a string contains a substring
func contains(s, substr string) bool {
	return len(s) >= len(substr) && (s == substr || len(substr) == 0 ||
		(len(s) > len(substr) && func() bool {
			for i := 0; i <= len(s)-len(substr); i++ {
				if s[i:i+len(substr)] == substr {
					return true
				}
			}
			return false
		}()))
}
