package policy

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"next.orly.dev/pkg/encoders/event"
	"next.orly.dev/pkg/encoders/tag"
)

// Helper function to create test event
func createTestEventBench(id, pubkey, content string, kind uint16) *event.E {
	return &event.E{
		ID:        []byte(id),
		Kind:      kind,
		Pubkey:    []byte(pubkey),
		Content:   []byte(content),
		Tags:      &tag.S{},
		CreatedAt: time.Now().Unix(),
	}
}

func BenchmarkCheckKindsPolicy(b *testing.B) {
	policy := &P{
		Kind: Kinds{
			Whitelist: []int{1, 3, 5, 7, 9735},
		},
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		policy.checkKindsPolicy(1)
	}
}

func BenchmarkCheckRulePolicy(b *testing.B) {
	// Create test event
	testEvent := createTestEventBench("test-event-id", "test-pubkey", "test content", 1)

	rule := Rule{
		Description:  "test rule",
		WriteAllow:   []string{"test-pubkey"},
		SizeLimit:    int64Ptr(10000),
		ContentLimit: int64Ptr(1000),
		MustHaveTags: []string{"p"},
	}

	policy := &P{}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		policy.checkRulePolicy("write", testEvent, rule, []byte("test-pubkey"))
	}
}

func BenchmarkCheckPolicy(b *testing.B) {
	// Create test event
	testEvent := createTestEventBench("test-event-id", "test-pubkey", "test content", 1)

	policy := &P{
		Kind: Kinds{
			Whitelist: []int{1, 3, 5},
		},
		Rules: map[int]Rule{
			1: {
				Description: "test rule",
				WriteAllow:  []string{"test-pubkey"},
			},
		},
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		policy.CheckPolicy("write", testEvent, []byte("test-pubkey"), "127.0.0.1")
	}
}

func BenchmarkCheckPolicyWithScript(b *testing.B) {
	// Create temporary directory
	tempDir := b.TempDir()
	scriptPath := filepath.Join(tempDir, "policy.sh")

	// Create a simple test script
	scriptContent := `#!/bin/bash
while IFS= read -r line; do
    echo '{"id":"test","action":"accept","msg":""}'
done
`
	err := os.WriteFile(scriptPath, []byte(scriptContent), 0755)
	if err != nil {
		b.Fatalf("Failed to create test script: %v", err)
	}

	ctx := context.Background()
	manager := &PolicyManager{
		ctx:          ctx,
		configDir:    tempDir,
		scriptPath:   scriptPath,
		enabled:      true,
		responseChan: make(chan PolicyResponse, 100),
	}

	// Start the policy manager
	err = manager.StartPolicy()
	if err != nil {
		b.Fatalf("Failed to start policy: %v", err)
	}
	defer manager.StopPolicy()

	// Give the script time to start
	time.Sleep(100 * time.Millisecond)

	// Create test event
	testEvent := createTestEventBench("test-event-id", "test-pubkey", "test content", 1)

	policy := &P{
		Manager: manager,
		Kind:    Kinds{},
		Rules: map[int]Rule{
			1: {
				Description: "test rule with script",
				Script:      "policy.sh",
			},
		},
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		policy.CheckPolicy("write", testEvent, []byte("test-pubkey"), "127.0.0.1")
	}
}

func BenchmarkLoadFromFile(b *testing.B) {
	// Create temporary directory
	tempDir := b.TempDir()
	configPath := filepath.Join(tempDir, "policy.json")

	// Create test config
	configData := `{
		"kind": {
			"whitelist": [1, 3, 5, 7, 9735],
			"blacklist": []
		},
		"rules": {
			"1": {
				"description": "text notes",
				"write_allow": [],
				"size_limit": 32000
			},
			"3": {
				"description": "contacts",
				"write_allow": ["npub1example1"],
				"script": "policy.sh"
			}
		}
	}`

	err := os.WriteFile(configPath, []byte(configData), 0644)
	if err != nil {
		b.Fatalf("Failed to create test config: %v", err)
	}

	policy := &P{}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		err := policy.LoadFromFile(configPath)
		if err != nil {
			b.Fatalf("Failed to load config: %v", err)
		}
	}
}

func BenchmarkCheckPolicyMultipleKinds(b *testing.B) {
	// Create policy with many rules
	rules := make(map[int]Rule)
	for i := 1; i <= 100; i++ {
		rules[i] = Rule{
			Description: "test rule",
			WriteAllow:  []string{"test-pubkey"},
		}
	}

	policy := &P{
		Kind:  Kinds{},
		Rules: rules,
	}

	// Create test events with different kinds
	events := make([]*event.E, 100)
	for i := 0; i < 100; i++ {
		events[i] = createTestEvent("test-event-id", "test-pubkey", "test content", uint16(i+1))
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		event := events[i%100]
		policy.CheckPolicy("write", event, []byte("test-pubkey"), "127.0.0.1")
	}
}

func BenchmarkCheckPolicyLargeWhitelist(b *testing.B) {
	// Create large whitelist
	whitelist := make([]int, 1000)
	for i := 0; i < 1000; i++ {
		whitelist[i] = i + 1
	}

	policy := &P{
		Kind: Kinds{
			Whitelist: whitelist,
		},
		Rules: map[int]Rule{},
	}

	testEvent := createTestEvent("test-event-id", "test-pubkey", "test content", 500) // Kind in the middle of the whitelist

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		policy.CheckPolicy("write", testEvent, []byte("test-pubkey"), "127.0.0.1")
	}
}

func BenchmarkCheckPolicyLargeBlacklist(b *testing.B) {
	// Create large blacklist
	blacklist := make([]int, 1000)
	for i := 0; i < 1000; i++ {
		blacklist[i] = i + 1
	}

	policy := &P{
		Kind: Kinds{
			Blacklist: blacklist,
		},
		Rules: map[int]Rule{},
	}

	testEvent := createTestEvent("test-event-id", "test-pubkey", "test content", 1500) // Kind not in blacklist

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		policy.CheckPolicy("write", testEvent, []byte("test-pubkey"), "127.0.0.1")
	}
}

func BenchmarkCheckPolicyComplexRule(b *testing.B) {
	// Create test event with many tags
	testEvent := createTestEventBench("test-event-id", "test-pubkey", "test content", 1)

	// Add many tags
	for i := 0; i < 100; i++ {
		tagItem1 := tag.New()
		tagItem1.T = append(tagItem1.T, []byte("p"), []byte("test-pubkey"))
		*testEvent.Tags = append(*testEvent.Tags, tagItem1)

		tagItem2 := tag.New()
		tagItem2.T = append(tagItem2.T, []byte("e"), []byte("test-event"))
		*testEvent.Tags = append(*testEvent.Tags, tagItem2)
	}

	rule := Rule{
		Description:  "complex rule",
		WriteAllow:   []string{"test-pubkey"},
		SizeLimit:    int64Ptr(100000),
		ContentLimit: int64Ptr(10000),
		MustHaveTags: []string{"p", "e"},
		Privileged:   true,
	}

	policy := &P{}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		policy.checkRulePolicy("write", testEvent, rule, []byte("test-pubkey"))
	}
}

func BenchmarkCheckPolicyLargeEvent(b *testing.B) {
	// Create large content
	largeContent := strings.Repeat("a", 100000) // 100KB content

	policy := &P{
		Kind: Kinds{},
		Rules: map[int]Rule{
			1: {
				Description:  "size limit test",
				SizeLimit:    int64Ptr(200000), // 200KB limit
				ContentLimit: int64Ptr(200000), // 200KB content limit
			},
		},
	}

	// Create test event with large content
	testEvent := createTestEvent("test-event-id", "test-pubkey", largeContent, 1)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		policy.CheckPolicy("write", testEvent, []byte("test-pubkey"), "127.0.0.1")
	}
}
