package jdenticon

import (
	"context"
	"errors"
	"strings"
	"testing"
	"time"
)

func TestComplexityLimitProtection(t *testing.T) {
	// Test that complexity limits prevent resource exhaustion
	config, err := Configure(WithMaxComplexity(1)) // Very low limit
	if err != nil {
		t.Fatalf("Failed to create config: %v", err)
	}

	ctx := context.Background()
	_, err = ToSVGWithConfig(ctx, "test-complexity", 64, config)

	if err == nil {
		t.Fatal("Expected complexity limit to be exceeded, but got no error")
	}

	// Check that we get the right error type (may be wrapped in ErrGenerationFailed)
	var complexityErr *ErrComplexityLimitExceeded
	if !errors.As(err, &complexityErr) {
		// Check if it's an engine complexity error that got translated
		if !strings.Contains(err.Error(), "complexity limit exceeded") {
			t.Errorf("Expected complexity limit error, got: %v", err)
		}
	}
}

func TestComplexityLimitDisabled(t *testing.T) {
	// Test that complexity limits can be disabled
	config, err := Configure(WithMaxComplexity(-1)) // Disabled
	if err != nil {
		t.Fatalf("Failed to create config: %v", err)
	}

	ctx := context.Background()
	_, err = ToSVGWithConfig(ctx, "test-disabled", 64, config)

	if err != nil {
		t.Errorf("Expected no error with disabled complexity limit, got: %v", err)
	}
}

func TestContextTimeoutProtection(t *testing.T) {
	// Create a context that will timeout very quickly
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
	defer cancel()

	// Wait a bit to ensure the context expires
	time.Sleep(1 * time.Millisecond)

	_, err := Generate(ctx, "test-timeout", 64)

	if err == nil {
		t.Fatal("Expected context timeout error, but got no error")
	}

	if !errors.Is(err, context.DeadlineExceeded) {
		t.Errorf("Expected context.DeadlineExceeded, got: %v", err)
	}
}

func TestContextCancellationProtection(t *testing.T) {
	// Create a context that we'll cancel
	ctx, cancel := context.WithCancel(context.Background())
	cancel() // Cancel immediately

	_, err := Generate(ctx, "test-cancellation", 64)

	if err == nil {
		t.Fatal("Expected context cancellation error, but got no error")
	}

	if !errors.Is(err, context.Canceled) {
		t.Errorf("Expected context.Canceled, got: %v", err)
	}
}

func TestNormalOperationWithLimits(t *testing.T) {
	// Test that normal operation works with reasonable limits
	config, err := Configure(WithMaxComplexity(200)) // Reasonable limit
	if err != nil {
		t.Fatalf("Failed to create config: %v", err)
	}

	ctx := context.Background()
	svg, err := ToSVGWithConfig(ctx, "test-normal", 64, config)

	if err != nil {
		t.Errorf("Expected normal operation to work, got error: %v", err)
	}

	if len(svg) == 0 {
		t.Error("Expected non-empty SVG output")
	}
}

func TestDefaultComplexityLimit(t *testing.T) {
	// Test that default complexity limit allows normal operation
	config := DefaultConfig()

	ctx := context.Background()
	svg, err := ToSVGWithConfig(ctx, "test-default", 64, config)

	if err != nil {
		t.Errorf("Expected default limits to allow normal operation, got error: %v", err)
	}

	if len(svg) == 0 {
		t.Error("Expected non-empty SVG output")
	}
}

func TestComplexityCalculationConsistency(t *testing.T) {
	// Test that complexity calculation is deterministic
	config, err := Configure(WithMaxComplexity(50))
	if err != nil {
		t.Fatalf("Failed to create config: %v", err)
	}

	ctx := context.Background()
	input := "consistency-test"

	// Try the same input multiple times - should get consistent results
	for i := 0; i < 5; i++ {
		_, err := ToSVGWithConfig(ctx, input, 64, config)
		// The error should be consistent (either always fail or always succeed)
		if i == 0 {
			// Store first result for comparison
			if err != nil {
				// If first attempt failed, all should fail
				continue
			}
		}
		// All subsequent attempts should have the same result
	}
}

func BenchmarkComplexityCalculation(b *testing.B) {
	// Benchmark the overhead of complexity calculation
	config, err := Configure(WithMaxComplexity(100))
	if err != nil {
		b.Fatalf("Failed to create config: %v", err)
	}

	ctx := context.Background()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = ToSVGWithConfig(ctx, "benchmark-test", 64, config)
	}
}

func BenchmarkWithoutComplexityLimit(b *testing.B) {
	// Benchmark without complexity limits for comparison
	config, err := Configure(WithMaxComplexity(-1)) // Disabled
	if err != nil {
		b.Fatalf("Failed to create config: %v", err)
	}

	ctx := context.Background()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, _ = ToSVGWithConfig(ctx, "benchmark-test", 64, config)
	}
}
