package rules

import (
	"testing"
)

// Test Rule creation and basic functionality
func TestRule(t *testing.T) {
	// Test NewRule with default values
	rule := NewRule()
	if rule == nil {
		t.Fatal("NewRule() returned nil")
	}

	if rule.GetCategory() != 0 {
		t.Errorf("Expected category 0, got %d", rule.GetCategory())
	}

	if rule.GetType() != 0 {
		t.Errorf("Expected type 0, got %d", rule.GetType())
	}

	if rule.GetValue() != "" {
		t.Errorf("Expected empty value, got %s", rule.GetValue())
	}

	if rule.GetCombined() != "NONE" {
		t.Errorf("Expected combined 'NONE', got %s", rule.GetCombined())
	}

	// Test NewRuleWithValues
	rule2 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
	if rule2 == nil {
		t.Fatal("NewRuleWithValues() returned nil")
	}

	if rule2.GetCategory() != CategoryPlayer {
		t.Errorf("Expected category %d, got %d", CategoryPlayer, rule2.GetCategory())
	}

	if rule2.GetType() != PlayerMaxLevel {
		t.Errorf("Expected type %d, got %d", PlayerMaxLevel, rule2.GetType())
	}

	if rule2.GetValue() != "50" {
		t.Errorf("Expected value '50', got %s", rule2.GetValue())
	}

	if rule2.GetCombined() != "Player:MaxLevel" {
		t.Errorf("Expected combined 'Player:MaxLevel', got %s", rule2.GetCombined())
	}

	// Test type conversion methods
	if rule2.GetInt32() != 50 {
		t.Errorf("Expected int32 50, got %d", rule2.GetInt32())
	}

	if rule2.GetBool() != true {
		t.Errorf("Expected bool true, got %t", rule2.GetBool())
	}

	// Test SetValue
	rule2.SetValue("100")
	if rule2.GetValue() != "100" {
		t.Errorf("Expected value '100' after SetValue, got %s", rule2.GetValue())
	}

	if rule2.GetInt32() != 100 {
		t.Errorf("Expected int32 100 after SetValue, got %d", rule2.GetInt32())
	}
}

// Test Rule copy constructor
func TestRuleCopy(t *testing.T) {
	original := NewRuleWithValues(CategoryCombat, CombatMaxRange, "4.0", "Combat:MaxCombatRange")
	copy := NewRuleFromRule(original)

	if copy == nil {
		t.Fatal("NewRuleFromRule() returned nil")
	}

	if copy.GetCategory() != original.GetCategory() {
		t.Errorf("Copy category mismatch: expected %d, got %d", original.GetCategory(), copy.GetCategory())
	}

	if copy.GetType() != original.GetType() {
		t.Errorf("Copy type mismatch: expected %d, got %d", original.GetType(), copy.GetType())
	}

	if copy.GetValue() != original.GetValue() {
		t.Errorf("Copy value mismatch: expected %s, got %s", original.GetValue(), copy.GetValue())
	}

	if copy.GetCombined() != original.GetCombined() {
		t.Errorf("Copy combined mismatch: expected %s, got %s", original.GetCombined(), copy.GetCombined())
	}

	// Test that they are independent copies
	copy.SetValue("8.0")
	if original.GetValue() == copy.GetValue() {
		t.Error("Original and copy are not independent after modification")
	}
}

// Test RuleSet creation and basic operations
func TestRuleSet(t *testing.T) {
	ruleSet := NewRuleSet()
	if ruleSet == nil {
		t.Fatal("NewRuleSet() returned nil")
	}

	if ruleSet.GetID() != 0 {
		t.Errorf("Expected ID 0, got %d", ruleSet.GetID())
	}

	if ruleSet.GetName() != "" {
		t.Errorf("Expected empty name, got %s", ruleSet.GetName())
	}

	// Test SetID and SetName
	ruleSet.SetID(1)
	ruleSet.SetName("Test Rule Set")

	if ruleSet.GetID() != 1 {
		t.Errorf("Expected ID 1, got %d", ruleSet.GetID())
	}

	if ruleSet.GetName() != "Test Rule Set" {
		t.Errorf("Expected name 'Test Rule Set', got %s", ruleSet.GetName())
	}

	// Test adding rules
	rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
	rule2 := NewRuleWithValues(CategoryCombat, CombatMaxRange, "4.0", "Combat:MaxCombatRange")

	ruleSet.AddRule(rule1)
	ruleSet.AddRule(rule2)

	// Test rule retrieval
	retrievedRule1 := ruleSet.GetRule(CategoryPlayer, PlayerMaxLevel)
	if retrievedRule1 == nil {
		t.Fatal("GetRule() returned nil for added rule")
	}

	if retrievedRule1.GetValue() != "50" {
		t.Errorf("Retrieved rule value mismatch: expected '50', got %s", retrievedRule1.GetValue())
	}

	// Test rule retrieval by name
	retrievedRule2 := ruleSet.GetRuleByName("Combat", "MaxCombatRange")
	if retrievedRule2 == nil {
		t.Fatal("GetRuleByName() returned nil for added rule")
	}

	if retrievedRule2.GetValue() != "4.0" {
		t.Errorf("Retrieved rule value mismatch: expected '4.0', got %s", retrievedRule2.GetValue())
	}

	// Test Size
	if ruleSet.Size() != 2 {
		t.Errorf("Expected size 2, got %d", ruleSet.Size())
	}

	// Test HasRule
	if !ruleSet.HasRule(CategoryPlayer, PlayerMaxLevel) {
		t.Error("HasRule() returned false for existing rule")
	}

	if ruleSet.HasRule(CategorySpawn, SpawnSpeedMultiplier) {
		t.Error("HasRule() returned true for non-existing rule")
	}
}

// Test RuleSet copy functionality
func TestRuleSetCopy(t *testing.T) {
	original := NewRuleSet()
	original.SetID(1)
	original.SetName("Original")

	rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
	rule2 := NewRuleWithValues(CategoryCombat, CombatMaxRange, "4.0", "Combat:MaxCombatRange")
	original.AddRule(rule1)
	original.AddRule(rule2)

	// Test copy constructor
	copy := NewRuleSetFromRuleSet(original)
	if copy == nil {
		t.Fatal("NewRuleSetFromRuleSet() returned nil")
	}

	if copy.GetID() != original.GetID() {
		t.Errorf("Copy ID mismatch: expected %d, got %d", original.GetID(), copy.GetID())
	}

	if copy.GetName() != original.GetName() {
		t.Errorf("Copy name mismatch: expected %s, got %s", original.GetName(), copy.GetName())
	}

	if copy.Size() != original.Size() {
		t.Errorf("Copy size mismatch: expected %d, got %d", original.Size(), copy.Size())
	}

	// Test that rules were copied correctly
	copyRule := copy.GetRule(CategoryPlayer, PlayerMaxLevel)
	if copyRule == nil {
		t.Fatal("Copied rule set is missing expected rule")
	}

	if copyRule.GetValue() != "50" {
		t.Errorf("Copied rule value mismatch: expected '50', got %s", copyRule.GetValue())
	}
}

// Test RuleManager initialization
func TestRuleManager(t *testing.T) {
	ruleManager := NewRuleManager()
	if ruleManager == nil {
		t.Fatal("NewRuleManager() returned nil")
	}

	if !ruleManager.IsInitialized() {
		t.Error("RuleManager should be initialized after creation")
	}

	// Test getting a default rule
	rule := ruleManager.GetGlobalRule(CategoryPlayer, PlayerMaxLevel)
	if rule == nil {
		t.Fatal("GetGlobalRule() returned nil for default rule")
	}

	// Should return the default value from Init()
	if rule.GetValue() != "50" {
		t.Errorf("Expected default value '50', got %s", rule.GetValue())
	}

	// Test getting rule by name
	rule2 := ruleManager.GetGlobalRuleByName("Player", "MaxLevel")
	if rule2 == nil {
		t.Fatal("GetGlobalRuleByName() returned nil for default rule")
	}

	if rule2.GetValue() != "50" {
		t.Errorf("Expected default value '50', got %s", rule2.GetValue())
	}

	// Test blank rule for non-existent rule
	blankRule := ruleManager.GetGlobalRule(9999, 9999)
	if blankRule == nil {
		t.Fatal("GetGlobalRule() should return blank rule for non-existent rule")
	}

	if blankRule.IsValid() {
		t.Error("Blank rule should not be valid")
	}
}

// Test RuleManager rule set operations
func TestRuleManagerRuleSets(t *testing.T) {
	ruleManager := NewRuleManager()

	// Create a test rule set
	ruleSet := NewRuleSet()
	ruleSet.SetID(1)
	ruleSet.SetName("Test Set")

	// Add some rules to it
	rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "60", "Player:MaxLevel")
	ruleSet.AddRule(rule1)

	// Add the rule set to the manager
	if !ruleManager.AddRuleSet(ruleSet) {
		t.Fatal("AddRuleSet() returned false")
	}

	// Test getting the rule set
	retrievedRuleSet := ruleManager.GetRuleSet(1)
	if retrievedRuleSet == nil {
		t.Fatal("GetRuleSet() returned nil")
	}

	if retrievedRuleSet.GetName() != "Test Set" {
		t.Errorf("Retrieved rule set name mismatch: expected 'Test Set', got %s", retrievedRuleSet.GetName())
	}

	// Test duplicate rule set
	duplicateRuleSet := NewRuleSet()
	duplicateRuleSet.SetID(1)
	duplicateRuleSet.SetName("Duplicate")

	if ruleManager.AddRuleSet(duplicateRuleSet) {
		t.Error("AddRuleSet() should return false for duplicate ID")
	}

	// Test GetNumRuleSets
	if ruleManager.GetNumRuleSets() != 1 {
		t.Errorf("Expected 1 rule set, got %d", ruleManager.GetNumRuleSets())
	}

	// Test setting global rule set
	if !ruleManager.SetGlobalRuleSet(1) {
		t.Fatal("SetGlobalRuleSet() returned false")
	}

	// Test that global rule now returns the overridden value
	globalRule := ruleManager.GetGlobalRule(CategoryPlayer, PlayerMaxLevel)
	if globalRule == nil {
		t.Fatal("GetGlobalRule() returned nil after setting global rule set")
	}

	if globalRule.GetValue() != "60" {
		t.Errorf("Expected overridden value '60', got %s", globalRule.GetValue())
	}
}

// Test category and type name functions
func TestCategoryNames(t *testing.T) {
	// Test GetCategoryName
	name := GetCategoryName(CategoryPlayer)
	if name != "Player" {
		t.Errorf("Expected category name 'Player', got %s", name)
	}

	// Test unknown category
	unknownName := GetCategoryName(9999)
	if unknownName != "Unknown" {
		t.Errorf("Expected 'Unknown' for invalid category, got %s", unknownName)
	}

	// Test GetCategoryByName
	category, exists := GetCategoryByName("Player")
	if !exists {
		t.Error("GetCategoryByName() should find 'Player' category")
	}

	if category != CategoryPlayer {
		t.Errorf("Expected category %d, got %d", CategoryPlayer, category)
	}

	// Test unknown category name
	_, exists = GetCategoryByName("NonExistent")
	if exists {
		t.Error("GetCategoryByName() should not find non-existent category")
	}
}

// Test rule validation
func TestRuleValidation(t *testing.T) {
	ruleManager := NewRuleManager()

	// Test valid rule
	err := ruleManager.ValidateRule(CategoryPlayer, PlayerMaxLevel, "50")
	if err != nil {
		t.Errorf("ValidateRule() returned error for valid rule: %v", err)
	}

	// Test rule value too long
	longValue := make([]byte, MaxRuleValueLength+1)
	for i := range longValue {
		longValue[i] = 'a'
	}

	err = ruleManager.ValidateRule(CategoryPlayer, PlayerMaxLevel, string(longValue))
	if err != ErrRuleValueTooLong {
		t.Errorf("ValidateRule() should return ErrRuleValueTooLong for long value")
	}
}

// Test RuleManagerAdapter
func TestRuleManagerAdapter(t *testing.T) {
	ruleManager := NewRuleManager()
	adapter := NewRuleManagerAdapter(ruleManager, 0)

	if adapter == nil {
		t.Fatal("NewRuleManagerAdapter() returned nil")
	}

	// Test basic rule access
	rule := adapter.GetRule(CategoryPlayer, PlayerMaxLevel)
	if rule == nil {
		t.Fatal("Adapter GetRule() returned nil")
	}

	if rule.GetValue() != "50" {
		t.Errorf("Expected value '50', got %s", rule.GetValue())
	}

	// Test convenience methods
	intValue := adapter.GetInt32(CategoryPlayer, PlayerMaxLevel)
	if intValue != 50 {
		t.Errorf("Expected int32 50, got %d", intValue)
	}

	boolValue := adapter.GetBool(CategoryPlayer, PlayerMaxLevel)
	if !boolValue {
		t.Error("Expected bool true for value '50'")
	}

	stringValue := adapter.GetString(CategoryPlayer, PlayerMaxLevel)
	if stringValue != "50" {
		t.Errorf("Expected string '50', got %s", stringValue)
	}

	// Test zone ID
	if adapter.GetZoneID() != 0 {
		t.Errorf("Expected zone ID 0, got %d", adapter.GetZoneID())
	}

	adapter.SetZoneID(100)
	if adapter.GetZoneID() != 100 {
		t.Errorf("Expected zone ID 100 after SetZoneID, got %d", adapter.GetZoneID())
	}
}

// Benchmark rule access performance
func BenchmarkRuleAccess(b *testing.B) {
	ruleManager := NewRuleManager()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = ruleManager.GetGlobalRule(CategoryPlayer, PlayerMaxLevel)
	}
}

// Benchmark rule manager creation
func BenchmarkRuleManagerCreation(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = NewRuleManager()
	}
}
