package alt_advancement

import (
	"fmt"
	"log"
	"reflect"
	"sync"
	"testing"
	"time"
)

// Test AltAdvanceData structure and methods
func TestAltAdvanceData(t *testing.T) {
	t.Run("Copy", func(t *testing.T) {
		aa := &AltAdvanceData{
			SpellID:      100,
			NodeID:       200,
			Name:         "Test AA",
			Description:  "Test Description",
			Group:        AA_CLASS,
			Col:          5,
			Row:          3,
			Icon:         1234,
			RankCost:     2,
			MaxRank:      5,
			MinLevel:     20,
			ClassName:    "Fighter",
			SubclassName: "Guardian",
		}
		
		copy := aa.Copy()
		if copy == aa {
			t.Error("Copy should return a new instance, not the same pointer")
		}
		
		if !reflect.DeepEqual(aa, copy) {
			t.Error("Copy should have identical field values")
		}
		
		// Modify original and ensure copy is unaffected
		aa.Name = "Modified"
		if copy.Name == "Modified" {
			t.Error("Copy should not be affected by changes to original")
		}
	})
	
	t.Run("IsValid", func(t *testing.T) {
		tests := []struct {
			name     string
			aa       *AltAdvanceData
			expected bool
		}{
			{
				name: "Valid AA",
				aa: &AltAdvanceData{
					SpellID:  100,
					NodeID:   200,
					Name:     "Test AA",
					MaxRank:  5,
					RankCost: 2,
				},
				expected: true,
			},
			{
				name: "Invalid - No SpellID",
				aa: &AltAdvanceData{
					NodeID:   200,
					Name:     "Test AA",
					MaxRank:  5,
					RankCost: 2,
				},
				expected: false,
			},
			{
				name: "Invalid - No NodeID",
				aa: &AltAdvanceData{
					SpellID:  100,
					Name:     "Test AA",
					MaxRank:  5,
					RankCost: 2,
				},
				expected: false,
			},
			{
				name: "Invalid - No Name",
				aa: &AltAdvanceData{
					SpellID:  100,
					NodeID:   200,
					MaxRank:  5,
					RankCost: 2,
				},
				expected: false,
			},
			{
				name: "Invalid - No MaxRank",
				aa: &AltAdvanceData{
					SpellID:  100,
					NodeID:   200,
					Name:     "Test AA",
					RankCost: 2,
				},
				expected: false,
			},
			{
				name: "Invalid - No RankCost",
				aa: &AltAdvanceData{
					SpellID: 100,
					NodeID:  200,
					Name:    "Test AA",
					MaxRank: 5,
				},
				expected: false,
			},
		}
		
		for _, tt := range tests {
			t.Run(tt.name, func(t *testing.T) {
				if result := tt.aa.IsValid(); result != tt.expected {
					t.Errorf("IsValid() = %v, want %v", result, tt.expected)
				}
			})
		}
	})
}

// Test MasterAAList functionality
func TestMasterAAList(t *testing.T) {
	t.Run("NewMasterAAList", func(t *testing.T) {
		masterList := NewMasterAAList()
		if masterList == nil {
			t.Fatal("NewMasterAAList returned nil")
		}
		
		if masterList.Size() != 0 {
			t.Error("Expected empty list to have size 0")
		}
		
		if masterList.aaList == nil {
			t.Error("Expected aaList to be initialized")
		}
		
		if masterList.aaBySpellID == nil || masterList.aaByNodeID == nil || masterList.aaByGroup == nil {
			t.Error("Expected lookup maps to be initialized")
		}
	})
	
	t.Run("AddAltAdvancement", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Test adding nil
		err := masterList.AddAltAdvancement(nil)
		if err == nil {
			t.Error("Expected error when adding nil AA")
		}
		
		// Test adding invalid AA
		invalidAA := &AltAdvanceData{SpellID: 0}
		err = masterList.AddAltAdvancement(invalidAA)
		if err == nil {
			t.Error("Expected error when adding invalid AA")
		}
		
		// Test adding valid AA
		validAA := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		
		err = masterList.AddAltAdvancement(validAA)
		if err != nil {
			t.Errorf("Unexpected error adding valid AA: %v", err)
		}
		
		if masterList.Size() != 1 {
			t.Error("Expected size to be 1 after adding AA")
		}
		
		// Test duplicate spell ID
		dupSpellAA := &AltAdvanceData{
			SpellID:  100, // Same spell ID
			NodeID:   201,
			Name:     "Duplicate Spell",
			MaxRank:  5,
			RankCost: 2,
		}
		
		err = masterList.AddAltAdvancement(dupSpellAA)
		if err == nil {
			t.Error("Expected error when adding duplicate spell ID")
		}
		
		// Test duplicate node ID
		dupNodeAA := &AltAdvanceData{
			SpellID:  101,
			NodeID:   200, // Same node ID
			Name:     "Duplicate Node",
			MaxRank:  5,
			RankCost: 2,
		}
		
		err = masterList.AddAltAdvancement(dupNodeAA)
		if err == nil {
			t.Error("Expected error when adding duplicate node ID")
		}
	})
	
	t.Run("GetAltAdvancement", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		
		masterList.AddAltAdvancement(aa)
		
		// Test getting existing AA
		retrieved := masterList.GetAltAdvancement(100)
		if retrieved == nil {
			t.Fatal("Expected to retrieve AA by spell ID")
		}
		
		if retrieved == aa {
			t.Error("GetAltAdvancement should return a copy, not the original")
		}
		
		if retrieved.SpellID != aa.SpellID || retrieved.Name != aa.Name {
			t.Error("Retrieved AA should have same data as original")
		}
		
		// Test getting non-existent AA
		notFound := masterList.GetAltAdvancement(999)
		if notFound != nil {
			t.Error("Expected nil for non-existent spell ID")
		}
	})
	
	t.Run("GetAltAdvancementByNodeID", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		
		masterList.AddAltAdvancement(aa)
		
		// Test getting existing AA
		retrieved := masterList.GetAltAdvancementByNodeID(200)
		if retrieved == nil {
			t.Fatal("Expected to retrieve AA by node ID")
		}
		
		if retrieved.NodeID != aa.NodeID {
			t.Error("Retrieved AA should have same node ID")
		}
		
		// Test getting non-existent AA
		notFound := masterList.GetAltAdvancementByNodeID(999)
		if notFound != nil {
			t.Error("Expected nil for non-existent node ID")
		}
	})
	
	t.Run("GetAAsByGroup", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Add AAs to different groups
		aa1 := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Class AA 1",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aa2 := &AltAdvanceData{
			SpellID:  101,
			NodeID:   201,
			Name:     "Class AA 2",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aa3 := &AltAdvanceData{
			SpellID:  102,
			NodeID:   202,
			Name:     "Heroic AA",
			Group:    AA_HEROIC,
			MaxRank:  5,
			RankCost: 2,
		}
		
		masterList.AddAltAdvancement(aa1)
		masterList.AddAltAdvancement(aa2)
		masterList.AddAltAdvancement(aa3)
		
		// Test getting AAs by group
		classAAs := masterList.GetAAsByGroup(AA_CLASS)
		if len(classAAs) != 2 {
			t.Errorf("Expected 2 class AAs, got %d", len(classAAs))
		}
		
		heroicAAs := masterList.GetAAsByGroup(AA_HEROIC)
		if len(heroicAAs) != 1 {
			t.Errorf("Expected 1 heroic AA, got %d", len(heroicAAs))
		}
		
		// Test getting empty group
		emptyGroup := masterList.GetAAsByGroup(AA_DRAGON)
		if len(emptyGroup) != 0 {
			t.Error("Expected empty slice for group with no AAs")
		}
		
		// Verify copies are returned
		if &classAAs[0] == &aa1 {
			t.Error("GetAAsByGroup should return copies, not originals")
		}
	})
	
	t.Run("GetAAsByClass", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Add AAs with different class requirements
		aaAllClasses := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "All Classes",
			Group:    AA_CLASS,
			ClassReq: 0, // Available to all classes
			MaxRank:  5,
			RankCost: 2,
		}
		
		aaFighterOnly := &AltAdvanceData{
			SpellID:  101,
			NodeID:   201,
			Name:     "Fighter Only",
			Group:    AA_CLASS,
			ClassReq: 1, // Fighter class
			MaxRank:  5,
			RankCost: 2,
		}
		
		aaMageOnly := &AltAdvanceData{
			SpellID:  102,
			NodeID:   202,
			Name:     "Mage Only",
			Group:    AA_CLASS,
			ClassReq: 20, // Mage class
			MaxRank:  5,
			RankCost: 2,
		}
		
		masterList.AddAltAdvancement(aaAllClasses)
		masterList.AddAltAdvancement(aaFighterOnly)
		masterList.AddAltAdvancement(aaMageOnly)
		
		// Test getting AAs for fighter class
		fighterAAs := masterList.GetAAsByClass(1)
		if len(fighterAAs) != 2 {
			t.Errorf("Expected 2 AAs for fighter (all classes + fighter only), got %d", len(fighterAAs))
		}
		
		// Test getting AAs for mage class
		mageAAs := masterList.GetAAsByClass(20)
		if len(mageAAs) != 2 {
			t.Errorf("Expected 2 AAs for mage (all classes + mage only), got %d", len(mageAAs))
		}
		
		// Test getting AAs for class with no specific AAs
		priestAAs := masterList.GetAAsByClass(10)
		if len(priestAAs) != 1 {
			t.Errorf("Expected 1 AA for priest (all classes only), got %d", len(priestAAs))
		}
	})
	
	t.Run("GetAAsByLevel", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Add AAs with different level requirements
		aaLevel10 := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Level 10 AA",
			Group:    AA_CLASS,
			MinLevel: 10,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aaLevel30 := &AltAdvanceData{
			SpellID:  101,
			NodeID:   201,
			Name:     "Level 30 AA",
			Group:    AA_CLASS,
			MinLevel: 30,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aaLevel50 := &AltAdvanceData{
			SpellID:  102,
			NodeID:   202,
			Name:     "Level 50 AA",
			Group:    AA_CLASS,
			MinLevel: 50,
			MaxRank:  5,
			RankCost: 2,
		}
		
		masterList.AddAltAdvancement(aaLevel10)
		masterList.AddAltAdvancement(aaLevel30)
		masterList.AddAltAdvancement(aaLevel50)
		
		// Test getting AAs at different levels
		level20AAs := masterList.GetAAsByLevel(20)
		if len(level20AAs) != 1 {
			t.Errorf("Expected 1 AA at level 20, got %d", len(level20AAs))
		}
		
		level40AAs := masterList.GetAAsByLevel(40)
		if len(level40AAs) != 2 {
			t.Errorf("Expected 2 AAs at level 40, got %d", len(level40AAs))
		}
		
		level60AAs := masterList.GetAAsByLevel(60)
		if len(level60AAs) != 3 {
			t.Errorf("Expected 3 AAs at level 60, got %d", len(level60AAs))
		}
		
		level5AAs := masterList.GetAAsByLevel(5)
		if len(level5AAs) != 0 {
			t.Errorf("Expected 0 AAs at level 5, got %d", len(level5AAs))
		}
	})
	
	t.Run("SortAAsByGroup", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Add AAs in random order
		aa1 := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "AA1",
			Group:    AA_CLASS,
			Row:      2,
			Col:      3,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aa2 := &AltAdvanceData{
			SpellID:  101,
			NodeID:   201,
			Name:     "AA2",
			Group:    AA_CLASS,
			Row:      1,
			Col:      5,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aa3 := &AltAdvanceData{
			SpellID:  102,
			NodeID:   202,
			Name:     "AA3",
			Group:    AA_CLASS,
			Row:      1,
			Col:      2,
			MaxRank:  5,
			RankCost: 2,
		}
		
		masterList.AddAltAdvancement(aa1)
		masterList.AddAltAdvancement(aa2)
		masterList.AddAltAdvancement(aa3)
		
		// Sort and verify order
		masterList.SortAAsByGroup()
		
		classAAs := masterList.GetAAsByGroup(AA_CLASS)
		if len(classAAs) != 3 {
			t.Fatal("Expected 3 AAs")
		}
		
		// Should be sorted by row then column
		// aa3 (1,2), aa2 (1,5), aa1 (2,3)
		if classAAs[0].Name != "AA3" || classAAs[1].Name != "AA2" || classAAs[2].Name != "AA1" {
			t.Error("AAs not sorted correctly by row and column")
		}
	})
	
	t.Run("GetGroups", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Add AAs to different groups
		aa1 := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "AA1",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aa2 := &AltAdvanceData{
			SpellID:  101,
			NodeID:   201,
			Name:     "AA2",
			Group:    AA_HEROIC,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aa3 := &AltAdvanceData{
			SpellID:  102,
			NodeID:   202,
			Name:     "AA3",
			Group:    AA_TRADESKILL,
			MaxRank:  5,
			RankCost: 2,
		}
		
		masterList.AddAltAdvancement(aa1)
		masterList.AddAltAdvancement(aa2)
		masterList.AddAltAdvancement(aa3)
		
		groups := masterList.GetGroups()
		if len(groups) != 3 {
			t.Errorf("Expected 3 groups, got %d", len(groups))
		}
		
		// Check that all expected groups are present
		groupMap := make(map[int8]bool)
		for _, g := range groups {
			groupMap[g] = true
		}
		
		if !groupMap[AA_CLASS] || !groupMap[AA_HEROIC] || !groupMap[AA_TRADESKILL] {
			t.Error("Not all expected groups were returned")
		}
	})
	
	t.Run("DestroyAltAdvancements", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Add some AAs
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		
		masterList.AddAltAdvancement(aa)
		
		if masterList.Size() != 1 {
			t.Error("Expected size to be 1 before destroy")
		}
		
		// Destroy all AAs
		masterList.DestroyAltAdvancements()
		
		if masterList.Size() != 0 {
			t.Error("Expected size to be 0 after destroy")
		}
		
		// Verify maps are cleared
		if len(masterList.aaBySpellID) != 0 || len(masterList.aaByNodeID) != 0 || len(masterList.aaByGroup) != 0 {
			t.Error("Expected all maps to be cleared after destroy")
		}
	})
	
	t.Run("ConcurrentAccess", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Add initial AAs
		for i := int32(1); i <= 10; i++ {
			aa := &AltAdvanceData{
				SpellID:  i * 100,
				NodeID:   i * 200,
				Name:     "Test AA",
				Group:    AA_CLASS,
				MaxRank:  5,
				RankCost: 2,
			}
			masterList.AddAltAdvancement(aa)
		}
		
		// Concurrent reads and writes
		var wg sync.WaitGroup
		errors := make(chan error, 100)
		
		// Multiple readers
		for i := 0; i < 10; i++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				for j := 0; j < 100; j++ {
					masterList.GetAltAdvancement(100)
					masterList.GetAAsByGroup(AA_CLASS)
					masterList.Size()
				}
			}()
		}
		
		// Multiple writers (trying to add new unique AAs)
		for i := 0; i < 5; i++ {
			wg.Add(1)
			go func(id int32) {
				defer wg.Done()
				aa := &AltAdvanceData{
					SpellID:  1000 + id*100,
					NodeID:   2000 + id*100,
					Name:     "Concurrent AA",
					Group:    AA_CLASS,
					MaxRank:  5,
					RankCost: 2,
				}
				if err := masterList.AddAltAdvancement(aa); err != nil {
					errors <- err
				}
			}(int32(i))
		}
		
		wg.Wait()
		close(errors)
		
		// Check for unexpected errors
		errorCount := 0
		for err := range errors {
			errorCount++
			t.Logf("Concurrent write error (expected for duplicates): %v", err)
		}
		
		// Check that we have expected number of AAs
		finalSize := masterList.Size()
		if finalSize < 10 || finalSize > 15 { // 10 initial + 0-5 concurrent (some may fail due to race)
			t.Errorf("Expected between 10-15 AAs after concurrent operations, got %d", finalSize)
		}
	})
}

// Test MasterAANodeList functionality
func TestMasterAANodeList(t *testing.T) {
	t.Run("NewMasterAANodeList", func(t *testing.T) {
		nodeList := NewMasterAANodeList()
		if nodeList == nil {
			t.Fatal("NewMasterAANodeList returned nil")
		}
		
		if nodeList.Size() != 0 {
			t.Error("Expected empty list to have size 0")
		}
	})
	
	t.Run("AddTreeNode", func(t *testing.T) {
		nodeList := NewMasterAANodeList()
		
		// Test adding nil
		err := nodeList.AddTreeNode(nil)
		if err == nil {
			t.Error("Expected error when adding nil node")
		}
		
		// Test adding valid node
		node := &TreeNodeData{
			ClassID:  1,
			TreeID:   100,
			AATreeID: 200,
		}
		
		err = nodeList.AddTreeNode(node)
		if err != nil {
			t.Errorf("Unexpected error adding valid node: %v", err)
		}
		
		if nodeList.Size() != 1 {
			t.Error("Expected size to be 1 after adding node")
		}
		
		// Test duplicate tree ID
		dupNode := &TreeNodeData{
			ClassID:  2,
			TreeID:   100, // Same tree ID
			AATreeID: 201,
		}
		
		err = nodeList.AddTreeNode(dupNode)
		if err == nil {
			t.Error("Expected error when adding duplicate tree ID")
		}
	})
	
	t.Run("GetTreeNodeByTreeID", func(t *testing.T) {
		nodeList := NewMasterAANodeList()
		
		node := &TreeNodeData{
			ClassID:  1,
			TreeID:   100,
			AATreeID: 200,
		}
		
		nodeList.AddTreeNode(node)
		
		// Test getting existing node
		retrieved := nodeList.GetTreeNode(100)
		if retrieved == nil {
			t.Fatal("Expected to retrieve node by tree ID")
		}
		
		if retrieved == node {
			t.Error("GetTreeNode should return a copy, not the original")
		}
		
		if retrieved.TreeID != node.TreeID {
			t.Error("Retrieved node should have same tree ID")
		}
		
		// Test getting non-existent node
		notFound := nodeList.GetTreeNode(999)
		if notFound != nil {
			t.Error("Expected nil for non-existent tree ID")
		}
	})
	
	t.Run("GetTreeNodesByClass", func(t *testing.T) {
		nodeList := NewMasterAANodeList()
		
		// Add nodes for different classes
		node1 := &TreeNodeData{
			ClassID:  1,
			TreeID:   100,
			AATreeID: 200,
		}
		
		node2 := &TreeNodeData{
			ClassID:  1,
			TreeID:   101,
			AATreeID: 201,
		}
		
		node3 := &TreeNodeData{
			ClassID:  2,
			TreeID:   102,
			AATreeID: 202,
		}
		
		nodeList.AddTreeNode(node1)
		nodeList.AddTreeNode(node2)
		nodeList.AddTreeNode(node3)
		
		// Test getting nodes by class
		class1Nodes := nodeList.GetTreeNodesByClass(1)
		if len(class1Nodes) != 2 {
			t.Errorf("Expected 2 nodes for class 1, got %d", len(class1Nodes))
		}
		
		class2Nodes := nodeList.GetTreeNodesByClass(2)
		if len(class2Nodes) != 1 {
			t.Errorf("Expected 1 node for class 2, got %d", len(class2Nodes))
		}
		
		// Test getting empty class
		emptyClass := nodeList.GetTreeNodesByClass(999)
		if len(emptyClass) != 0 {
			t.Error("Expected empty slice for class with no nodes")
		}
	})
}

// Test AATemplate functionality
func TestAATemplate(t *testing.T) {
	t.Run("NewAATemplate", func(t *testing.T) {
		// Test personal template
		template := NewAATemplate(AA_TEMPLATE_PERSONAL_1, "Personal Template 1")
		if template == nil {
			t.Fatal("NewAATemplate returned nil")
		}
		
		if template.TemplateID != AA_TEMPLATE_PERSONAL_1 {
			t.Error("Expected template ID to match")
		}
		
		if template.Name != "Personal Template 1" {
			t.Error("Expected template name to match")
		}
		
		if !template.IsPersonal {
			t.Error("Expected IsPersonal to be true for template ID 1")
		}
		
		if template.IsServer {
			t.Error("Expected IsServer to be false for template ID 1")
		}
		
		if template.IsCurrent {
			t.Error("Expected IsCurrent to be false for template ID 1")
		}
		
		// Test server template
		serverTemplate := NewAATemplate(AA_TEMPLATE_SERVER_1, "Server Template 1")
		if !serverTemplate.IsServer {
			t.Error("Expected IsServer to be true for template ID 4")
		}
		
		// Test current template
		currentTemplate := NewAATemplate(AA_TEMPLATE_CURRENT, "Current")
		if !currentTemplate.IsCurrent {
			t.Error("Expected IsCurrent to be true for template ID 7")
		}
	})
	
	t.Run("AddEntry", func(t *testing.T) {
		template := NewAATemplate(AA_TEMPLATE_PERSONAL_1, "Test Template")
		
		entry := &AAEntry{
			TemplateID: AA_TEMPLATE_PERSONAL_1,
			TabID:      AA_CLASS,
			AAID:       100,
			Order:      1,
			TreeID:     1,
		}
		
		// Add entry
		template.AddEntry(entry)
		
		if len(template.Entries) != 1 {
			t.Error("Expected 1 entry after adding")
		}
		
		if template.Entries[0] != entry {
			t.Error("Expected entry to be added to template")
		}
	})
	
	t.Run("GetEntry", func(t *testing.T) {
		template := NewAATemplate(AA_TEMPLATE_PERSONAL_1, "Test Template")
		
		entry1 := &AAEntry{
			TemplateID: AA_TEMPLATE_PERSONAL_1,
			TabID:      AA_CLASS,
			AAID:       100,
			Order:      1,
			TreeID:     1,
		}
		
		entry2 := &AAEntry{
			TemplateID: AA_TEMPLATE_PERSONAL_1,
			TabID:      AA_CLASS,
			AAID:       101,
			Order:      2,
			TreeID:     2,
		}
		
		template.AddEntry(entry1)
		template.AddEntry(entry2)
		
		// Test getting existing entry
		found := template.GetEntry(100)
		if found == nil {
			t.Error("Expected to find entry with AAID 100")
		}
		
		if found != entry1 {
			t.Error("Expected to get correct entry")
		}
		
		// Test getting non-existent entry
		notFound := template.GetEntry(999)
		if notFound != nil {
			t.Error("Expected nil for non-existent AAID")
		}
	})
	
	t.Run("RemoveEntry", func(t *testing.T) {
		template := NewAATemplate(AA_TEMPLATE_PERSONAL_1, "Test Template")
		
		entry := &AAEntry{
			TemplateID: AA_TEMPLATE_PERSONAL_1,
			TabID:      AA_CLASS,
			AAID:       100,
			Order:      1,
			TreeID:     1,
		}
		
		template.AddEntry(entry)
		
		// Remove entry
		removed := template.RemoveEntry(100)
		if !removed {
			t.Error("Expected RemoveEntry to return true")
		}
		
		if len(template.Entries) != 0 {
			t.Error("Expected 0 entries after removing")
		}
		
		// Try to remove non-existent entry
		removed = template.RemoveEntry(999)
		if removed {
			t.Error("Expected RemoveEntry to return false for non-existent entry")
		}
	})
}

// Test AAPlayerState functionality
func TestAAPlayerState(t *testing.T) {
	t.Run("NewAAPlayerState", func(t *testing.T) {
		playerState := NewAAPlayerState(123)
		if playerState == nil {
			t.Fatal("NewAAPlayerState returned nil")
		}
		
		if playerState.CharacterID != 123 {
			t.Error("Expected character ID to be 123")
		}
		
		if playerState.TotalPoints != 0 {
			t.Error("Expected initial total points to be 0")
		}
		
		if playerState.ActiveTemplate != AA_TEMPLATE_CURRENT {
			t.Error("Expected active template to be current template")
		}
		
		if playerState.Templates == nil || playerState.Tabs == nil || playerState.AAProgress == nil {
			t.Error("Expected maps to be initialized")
		}
	})
	
	t.Run("AddAAProgress", func(t *testing.T) {
		playerState := NewAAPlayerState(123)
		
		progress := &PlayerAAData{
			CharacterID: 123,
			NodeID:      100,
			CurrentRank: 1,
			PointsSpent: 2,
			TemplateID:  AA_TEMPLATE_CURRENT,
			TabID:       AA_CLASS,
		}
		
		playerState.AddAAProgress(progress)
		
		if len(playerState.AAProgress) != 1 {
			t.Error("Expected 1 AA progress entry")
		}
		
		if playerState.AAProgress[100] != progress {
			t.Error("Expected progress to be added correctly")
		}
	})
	
	t.Run("GetAAProgress", func(t *testing.T) {
		playerState := NewAAPlayerState(123)
		
		progress := &PlayerAAData{
			CharacterID: 123,
			NodeID:      100,
			CurrentRank: 1,
			PointsSpent: 2,
		}
		
		playerState.AddAAProgress(progress)
		
		// Test getting existing progress
		found := playerState.GetAAProgress(100)
		if found == nil {
			t.Error("Expected to find AA progress")
		}
		
		if found != progress {
			t.Error("Expected to get correct progress")
		}
		
		// Test getting non-existent progress
		notFound := playerState.GetAAProgress(999)
		if notFound != nil {
			t.Error("Expected nil for non-existent node ID")
		}
	})
	
	t.Run("CalculateSpentPoints", func(t *testing.T) {
		playerState := NewAAPlayerState(123)
		
		// Add multiple progress entries
		progress1 := &PlayerAAData{
			CharacterID: 123,
			NodeID:      100,
			CurrentRank: 2,
			PointsSpent: 4,
		}
		
		progress2 := &PlayerAAData{
			CharacterID: 123,
			NodeID:      101,
			CurrentRank: 3,
			PointsSpent: 6,
		}
		
		playerState.AddAAProgress(progress1)
		playerState.AddAAProgress(progress2)
		
		total := playerState.CalculateSpentPoints()
		if total != 10 {
			t.Errorf("Expected total spent points to be 10, got %d", total)
		}
	})
	
	t.Run("UpdatePoints", func(t *testing.T) {
		playerState := NewAAPlayerState(123)
		
		// Update points
		playerState.UpdatePoints(100, 20, 5)
		
		if playerState.TotalPoints != 100 {
			t.Error("Expected total points to be 100")
		}
		
		if playerState.SpentPoints != 20 {
			t.Error("Expected spent points to be 20")
		}
		
		if playerState.BankedPoints != 5 {
			t.Error("Expected banked points to be 5")
		}
		
		if playerState.AvailablePoints != 80 {
			t.Error("Expected available points to be 80 (100 - 20)")
		}
	})
	
	t.Run("SetActiveTemplate", func(t *testing.T) {
		playerState := NewAAPlayerState(123)
		
		// Create and add a template
		template := NewAATemplate(AA_TEMPLATE_PERSONAL_1, "Personal 1")
		playerState.Templates[AA_TEMPLATE_PERSONAL_1] = template
		
		// Set active template
		success := playerState.SetActiveTemplate(AA_TEMPLATE_PERSONAL_1)
		if !success {
			t.Error("Expected SetActiveTemplate to succeed")
		}
		
		if playerState.ActiveTemplate != AA_TEMPLATE_PERSONAL_1 {
			t.Error("Expected active template to be updated")
		}
		
		// Try to set non-existent template
		success = playerState.SetActiveTemplate(AA_TEMPLATE_PERSONAL_2)
		if success {
			t.Error("Expected SetActiveTemplate to fail for non-existent template")
		}
	})
}

// Test utility functions
func TestUtilityFunctions(t *testing.T) {
	t.Run("GetMaxAAForTab", func(t *testing.T) {
		tests := []struct {
			group    int8
			expected int32
		}{
			{AA_CLASS, MAX_CLASS_AA},
			{AA_SUBCLASS, MAX_SUBCLASS_AA},
			{AA_SHADOW, MAX_SHADOWS_AA},
			{AA_HEROIC, MAX_HEROIC_AA},
			{AA_TRADESKILL, MAX_TRADESKILL_AA},
			{AA_PRESTIGE, MAX_PRESTIGE_AA},
			{AA_TRADESKILL_PRESTIGE, MAX_TRADESKILL_PRESTIGE_AA},
			{AA_DRAGON, MAX_DRAGON_AA},
			{AA_DRAGONCLASS, MAX_DRAGONCLASS_AA},
			{AA_FARSEAS, MAX_FARSEAS_AA},
			{99, 100}, // Unknown group
		}
		
		for _, tt := range tests {
			t.Run(GetTabName(tt.group), func(t *testing.T) {
				result := GetMaxAAForTab(tt.group)
				if result != tt.expected {
					t.Errorf("GetMaxAAForTab(%d) = %d, want %d", tt.group, result, tt.expected)
				}
			})
		}
	})
	
	t.Run("GetTabName", func(t *testing.T) {
		tests := []struct {
			group    int8
			expected string
		}{
			{AA_CLASS, "Class"},
			{AA_SUBCLASS, "Subclass"},
			{AA_SHADOW, "Shadows"},
			{AA_HEROIC, "Heroic"},
			{AA_TRADESKILL, "Tradeskill"},
			{99, "Unknown"},
		}
		
		for _, tt := range tests {
			t.Run(tt.expected, func(t *testing.T) {
				result := GetTabName(tt.group)
				if result != tt.expected {
					t.Errorf("GetTabName(%d) = %s, want %s", tt.group, result, tt.expected)
				}
			})
		}
	})
	
	t.Run("GetTemplateName", func(t *testing.T) {
		tests := []struct {
			templateID int8
			expected   string
		}{
			{AA_TEMPLATE_PERSONAL_1, "Personal 1"},
			{AA_TEMPLATE_PERSONAL_2, "Personal 2"},
			{AA_TEMPLATE_SERVER_1, "Server 1"},
			{AA_TEMPLATE_CURRENT, "Current"},
			{99, "Unknown"},
		}
		
		for _, tt := range tests {
			t.Run(tt.expected, func(t *testing.T) {
				result := GetTemplateName(tt.templateID)
				if result != tt.expected {
					t.Errorf("GetTemplateName(%d) = %s, want %s", tt.templateID, result, tt.expected)
				}
			})
		}
	})
	
	t.Run("IsExpansionRequired", func(t *testing.T) {
		tests := []struct {
			name      string
			flags     int8
			expansion int8
			expected  bool
		}{
			{"No expansion required", EXPANSION_NONE, EXPANSION_KOS, false},
			{"KOS required - has KOS", EXPANSION_KOS, EXPANSION_KOS, true},
			{"KOS required - no KOS", EXPANSION_EOF, EXPANSION_KOS, false},
			{"Multiple expansions - has one", EXPANSION_KOS | EXPANSION_EOF, EXPANSION_KOS, true},
			{"Multiple expansions - has different", EXPANSION_KOS | EXPANSION_EOF, EXPANSION_ROK, false},
		}
		
		for _, tt := range tests {
			t.Run(tt.name, func(t *testing.T) {
				result := IsExpansionRequired(tt.flags, tt.expansion)
				if result != tt.expected {
					t.Errorf("IsExpansionRequired(%d, %d) = %v, want %v", tt.flags, tt.expansion, result, tt.expected)
				}
			})
		}
	})
}

// Test DefaultAAManagerConfig
func TestDefaultAAManagerConfig(t *testing.T) {
	config := DefaultAAManagerConfig()
	
	if !config.EnableAASystem {
		t.Error("Expected EnableAASystem to be true by default")
	}
	
	if !config.EnableCaching {
		t.Error("Expected EnableCaching to be true by default")
	}
	
	if config.AAPointsPerLevel != DEFAULT_AA_POINTS_PER_LEVEL {
		t.Errorf("Expected AAPointsPerLevel to be %d, got %d", DEFAULT_AA_POINTS_PER_LEVEL, config.AAPointsPerLevel)
	}
	
	if config.MaxBankedPoints != DEFAULT_AA_MAX_BANKED_POINTS {
		t.Errorf("Expected MaxBankedPoints to be %d, got %d", DEFAULT_AA_MAX_BANKED_POINTS, config.MaxBankedPoints)
	}
	
	if config.CacheSize != AA_CACHE_SIZE {
		t.Errorf("Expected CacheSize to be %d, got %d", AA_CACHE_SIZE, config.CacheSize)
	}
}

// Test AAManager basic functionality
func TestAAManagerBasics(t *testing.T) {
	config := DefaultAAManagerConfig()
	
	manager := NewAAManager(config)
	if manager == nil {
		t.Fatal("NewAAManager returned nil")
	}
	
	// Test configuration
	currentConfig := manager.GetConfig()
	if currentConfig.UpdateInterval != config.UpdateInterval {
		t.Error("Expected config to match")
	}
	
	// Test stats
	stats := manager.GetSystemStats()
	if stats == nil {
		t.Error("Expected valid stats")
	}
	
	// Test that manager is initialized
	if manager.masterAAList == nil {
		t.Error("Expected master AA list to be initialized")
	}
	
	if manager.masterNodeList == nil {
		t.Error("Expected master node list to be initialized")
	}
}

// Helper functions for AATemplate
func (t *AATemplate) AddEntry(entry *AAEntry) {
	t.Entries = append(t.Entries, entry)
	t.UpdatedAt = time.Now()
}

func (t *AATemplate) GetEntry(aaID int32) *AAEntry {
	for _, entry := range t.Entries {
		if entry.AAID == aaID {
			return entry
		}
	}
	return nil
}

func (t *AATemplate) RemoveEntry(aaID int32) bool {
	for i, entry := range t.Entries {
		if entry.AAID == aaID {
			t.Entries = append(t.Entries[:i], t.Entries[i+1:]...)
			t.UpdatedAt = time.Now()
			return true
		}
	}
	return false
}

// Helper functions for AAPlayerState
func (ps *AAPlayerState) AddAAProgress(progress *PlayerAAData) {
	ps.mutex.Lock()
	defer ps.mutex.Unlock()
	
	ps.AAProgress[progress.NodeID] = progress
	ps.needsSync = true
	ps.lastUpdate = time.Now()
}

func (ps *AAPlayerState) GetAAProgress(nodeID int32) *PlayerAAData {
	ps.mutex.RLock()
	defer ps.mutex.RUnlock()
	
	return ps.AAProgress[nodeID]
}

func (ps *AAPlayerState) CalculateSpentPoints() int32 {
	ps.mutex.RLock()
	defer ps.mutex.RUnlock()
	
	var total int32
	for _, progress := range ps.AAProgress {
		total += progress.PointsSpent
	}
	return total
}

func (ps *AAPlayerState) UpdatePoints(total, spent, banked int32) {
	ps.mutex.Lock()
	defer ps.mutex.Unlock()
	
	ps.TotalPoints = total
	ps.SpentPoints = spent
	ps.BankedPoints = banked
	ps.AvailablePoints = total - spent
	ps.needsSync = true
	ps.lastUpdate = time.Now()
}

func (ps *AAPlayerState) SetActiveTemplate(templateID int8) bool {
	ps.mutex.Lock()
	defer ps.mutex.Unlock()
	
	if _, exists := ps.Templates[templateID]; exists {
		ps.ActiveTemplate = templateID
		ps.needsSync = true
		ps.lastUpdate = time.Now()
		return true
	}
	return false
}

// Helper functions for TreeNodeData
func (tnd *TreeNodeData) Copy() *TreeNodeData {
	copy := *tnd
	return &copy
}

// Test AAManager more comprehensively
func TestAAManager(t *testing.T) {
	t.Run("LoadAAData", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		
		// Test without database
		err := manager.LoadAAData()
		if err == nil {
			t.Error("Expected error when loading without database")
		}
		
		// Test with mock database
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		err = manager.LoadAAData()
		if err != nil {
			t.Errorf("Unexpected error loading AA data: %v", err)
		}
		
		if !mockDB.loadAAsCalled || !mockDB.loadNodesCalled {
			t.Error("Expected database methods to be called")
		}
	})
	
	t.Run("GetPlayerAAState", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		
		// Set up mock database that returns no existing data
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Test getting non-existent player
		state, err := manager.GetPlayerAAState(123)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if state == nil {
			t.Error("Expected new player state to be created")
		}
		
		if state != nil && state.CharacterID != 123 {
			t.Error("Expected character ID to match")
		}
		
		// Test getting existing player (should be cached)
		state2, err := manager.GetPlayerAAState(123)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if state != nil && state2 != nil && state != state2 {
			t.Error("Expected same state instance for same character from cache")
		}
	})
	
	t.Run("PurchaseAA", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		
		// Set up mock database
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Add a test AA to the master list
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
			MinLevel: 10,
		}
		manager.masterAAList.AddAltAdvancement(aa)
		
		// Create a player with points
		state, err := manager.GetPlayerAAState(123)
		if err != nil {
			t.Fatalf("Failed to get player state: %v", err)
		}
		if state == nil {
			t.Fatal("Player state is nil")
		}
		state.TotalPoints = 10
		state.AvailablePoints = 10
		
		// Test purchasing AA
		err = manager.PurchaseAA(123, 200, 1)
		if err != nil {
			t.Errorf("Unexpected error purchasing AA: %v", err)
		}
		
		// Verify purchase
		progress := state.GetAAProgress(200)
		if progress == nil {
			t.Fatal("Expected AA progress to exist")
		}
		
		if progress.CurrentRank != 1 {
			t.Error("Expected rank to be 1")
		}
		
		if state.AvailablePoints != 8 {
			t.Error("Expected available points to be reduced by 2")
		}
		
		// Test purchasing non-existent AA
		err = manager.PurchaseAA(123, 999, 1)
		if err == nil {
			t.Error("Expected error when purchasing non-existent AA")
		}
	})
	
	t.Run("AwardAAPoints", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		
		// Set up mock database
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Award points to player
		err := manager.AwardAAPoints(123, 50, "Level up")
		if err != nil {
			t.Errorf("Unexpected error awarding points: %v", err)
		}
		
		// Verify points
		total, spent, available, err := manager.GetAAPoints(123)
		if err != nil {
			t.Errorf("Unexpected error getting points: %v", err)
		}
		
		if total != 50 {
			t.Errorf("Expected total points to be 50, got %d", total)
		}
		
		if spent != 0 {
			t.Errorf("Expected spent points to be 0, got %d", spent)
		}
		
		if available != 50 {
			t.Errorf("Expected available points to be 50, got %d", available)
		}
	})
	
	t.Run("GetAA", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		
		// Add test AA
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		manager.masterAAList.AddAltAdvancement(aa)
		
		// Test getting by node ID
		retrieved, err := manager.GetAA(200)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if retrieved == nil {
			t.Fatal("Expected to get AA")
		}
		
		if retrieved.NodeID != 200 {
			t.Error("Expected node ID to match")
		}
		
		// Test getting by spell ID
		retrieved, err = manager.GetAABySpellID(100)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if retrieved == nil {
			t.Fatal("Expected to get AA by spell ID")
		}
		
		if retrieved.SpellID != 100 {
			t.Error("Expected spell ID to match")
		}
	})
	
	t.Run("StartStop", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		config.UpdateInterval = 10 * time.Millisecond
		config.SaveInterval = 10 * time.Millisecond
		config.AutoSave = true
		
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Start manager
		err := manager.Start()
		if err != nil {
			t.Errorf("Unexpected error starting: %v", err)
		}
		
		if !manager.IsRunning() {
			t.Error("Expected manager to be running")
		}
		
		// Let background processes run briefly
		time.Sleep(50 * time.Millisecond)
		
		// Stop manager
		err = manager.Stop()
		if err != nil {
			t.Errorf("Unexpected error stopping: %v", err)
		}
		
		if manager.IsRunning() {
			t.Error("Expected manager to be stopped")
		}
	})
}

// Mock implementations for testing
type mockAADatabase struct {
	loadAAsCalled    bool
	loadNodesCalled  bool
	savePlayerCalled bool
	mu               sync.Mutex
}

func (m *mockAADatabase) LoadAltAdvancements() error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.loadAAsCalled = true
	return nil
}

func (m *mockAADatabase) LoadTreeNodes() error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.loadNodesCalled = true
	return nil
}

func (m *mockAADatabase) LoadPlayerAA(characterID int32) (*AAPlayerState, error) {
	// Simulate creating a new player state when none exists
	return NewAAPlayerState(characterID), nil
}

func (m *mockAADatabase) SavePlayerAA(playerState *AAPlayerState) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.savePlayerCalled = true
	return nil
}

func (m *mockAADatabase) DeletePlayerAA(characterID int32) error {
	return nil
}

func (m *mockAADatabase) LoadPlayerAADefaults(classID int8) (map[int8][]*AAEntry, error) {
	return make(map[int8][]*AAEntry), nil
}

func (m *mockAADatabase) GetAAStatistics() (map[string]interface{}, error) {
	return make(map[string]interface{}), nil
}

// Test mock event handler
type mockAAEventHandler struct {
	events []string
	mu     sync.Mutex
}

func (m *mockAAEventHandler) OnAAPurchased(characterID int32, nodeID int32, newRank int8, pointsSpent int32) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.events = append(m.events, "purchased")
	return nil
}

func (m *mockAAEventHandler) OnAARefunded(characterID int32, nodeID int32, oldRank int8, pointsRefunded int32) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.events = append(m.events, "refunded")
	return nil
}

func (m *mockAAEventHandler) OnAATemplateChanged(characterID int32, oldTemplate, newTemplate int8) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.events = append(m.events, "template_changed")
	return nil
}

func (m *mockAAEventHandler) OnAATemplateCreated(characterID int32, templateID int8, name string) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.events = append(m.events, "template_created")
	return nil
}

func (m *mockAAEventHandler) OnAASystemLoaded(totalAAs int32, totalNodes int32) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.events = append(m.events, "system_loaded")
	return nil
}

func (m *mockAAEventHandler) OnAADataReloaded() error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.events = append(m.events, "data_reloaded")
	return nil
}

func (m *mockAAEventHandler) OnPlayerAALoaded(characterID int32, playerState *AAPlayerState) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.events = append(m.events, "player_loaded")
	return nil
}

func (m *mockAAEventHandler) OnPlayerAAPointsChanged(characterID int32, oldPoints, newPoints int32) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.events = append(m.events, "points_changed")
	return nil
}

func (m *mockAAEventHandler) LogEvent(message string) {
	log.Println("[MockEvent]", message)
}

// Test event handler integration
func TestAAEventHandling(t *testing.T) {
	config := DefaultAAManagerConfig()
	manager := NewAAManager(config)
	
	// Add mock event handler
	mockHandler := &mockAAEventHandler{}
	manager.SetEventHandler(mockHandler)
	
	// Add mock database
	mockDB := &mockAADatabase{}
	manager.SetDatabase(mockDB)
	
	// Test system loaded event
	err := manager.LoadAAData()
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
	
	// Wait a bit for goroutines to complete
	time.Sleep(10 * time.Millisecond)
	
	// Check that event was fired
	mockHandler.mu.Lock()
	hasSystemLoaded := false
	for _, event := range mockHandler.events {
		if event == "system_loaded" {
			hasSystemLoaded = true
			break
		}
	}
	if !hasSystemLoaded {
		t.Error("Expected system_loaded event")
	}
	mockHandler.mu.Unlock()
	
	// Test purchase event
	aa := &AltAdvanceData{
		SpellID:  100,
		NodeID:   200,
		Name:     "Test AA",
		Group:    AA_CLASS,
		MaxRank:  5,
		RankCost: 2,
		MinLevel: 1,
	}
	manager.masterAAList.AddAltAdvancement(aa)
	
	state, _ := manager.GetPlayerAAState(123)
	state.TotalPoints = 10
	state.AvailablePoints = 10
	
	err = manager.PurchaseAA(123, 200, 1)
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
	
	// Wait a bit for goroutines to complete
	time.Sleep(10 * time.Millisecond)
	
	// Check that purchase event was fired
	mockHandler.mu.Lock()
	found := false
	for _, event := range mockHandler.events {
		if event == "purchased" {
			found = true
			break
		}
	}
	if !found {
		t.Error("Expected purchased event")
	}
	mockHandler.mu.Unlock()
	
	// Test points changed event
	err = manager.AwardAAPoints(123, 50, "Test award")
	if err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
	
	// Wait a bit for goroutines to complete
	time.Sleep(10 * time.Millisecond)
	
	mockHandler.mu.Lock()
	found = false
	for _, event := range mockHandler.events {
		if event == "points_changed" {
			found = true
			break
		}
	}
	if !found {
		t.Error("Expected points_changed event")
	}
	mockHandler.mu.Unlock()
}

// Test Interface Implementations and Adapters
func TestInterfacesAndAdapters(t *testing.T) {
	t.Run("AAAdapter", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		adapter := NewAAAdapter(manager, 123)
		if adapter == nil {
			t.Fatal("NewAAAdapter returned nil")
		}
		
		if adapter.GetCharacterID() != 123 {
			t.Error("Expected character ID to match")
		}
		
		if adapter.GetManager() != manager {
			t.Error("Expected manager to match")
		}
		
		// Test AwardPoints
		err := adapter.AwardPoints(100, "Test")
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		// Test GetAAPoints
		total, _, _, err := adapter.GetAAPoints()
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if total != 100 {
			t.Errorf("Expected total 100, got %d", total)
		}
		
		// Test GetAAState
		state, err := adapter.GetAAState()
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if state == nil {
			t.Error("Expected state to be returned")
		}
		
		// Test SaveAAState
		err = adapter.SaveAAState()
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		// Test GetTemplates
		templates, err := adapter.GetTemplates()
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if templates == nil {
			t.Error("Expected templates map")
		}
		
		stats := adapter.GetPlayerStats()
		if stats == nil {
			t.Error("Expected stats map")
		}
	})
	
	t.Run("PlayerAAAdapter", func(t *testing.T) {
		mockPlayer := &mockPlayer{
			characterID:     123,
			level:          50,
			class:          1,
			adventureClass: 1,
			race:           1,
			name:           "TestPlayer",
		}
		
		adapter := NewPlayerAAAdapter(mockPlayer)
		if adapter == nil {
			t.Fatal("NewPlayerAAAdapter returned nil")
		}
		
		if adapter.GetPlayer() != mockPlayer {
			t.Error("Expected player to match")
		}
		
		if adapter.GetCharacterID() != 123 {
			t.Error("Expected character ID to match")
		}
		
		if adapter.GetLevel() != 50 {
			t.Error("Expected level to match")
		}
		
		if adapter.GetClass() != 1 {
			t.Error("Expected class to match")
		}
		
		if adapter.GetAdventureClass() != 1 {
			t.Error("Expected adventure class to match")
		}
		
		if adapter.GetRace() != 1 {
			t.Error("Expected race to match")
		}
		
		if adapter.GetName() != "TestPlayer" {
			t.Error("Expected name to match")
		}
		
		if !adapter.HasExpansion(EXPANSION_NONE) {
			t.Error("Expected expansion check to work")
		}
	})
	
	t.Run("ClientAAAdapter", func(t *testing.T) {
		mockClient := &mockClient{
			characterID: 123,
			player: &mockPlayer{
				characterID: 123,
				name:        "TestPlayer",
			},
			version: 1096,
		}
		
		adapter := NewClientAAAdapter(mockClient)
		if adapter == nil {
			t.Fatal("NewClientAAAdapter returned nil")
		}
		
		if adapter.GetClient() != mockClient {
			t.Error("Expected client to match")
		}
		
		if adapter.GetCharacterID() != 123 {
			t.Error("Expected character ID to match")
		}
		
		if adapter.GetPlayer() != mockClient.player {
			t.Error("Expected player to match")
		}
		
		if adapter.GetClientVersion() != 1096 {
			t.Error("Expected client version to match")
		}
		
		// Test SendPacket
		err := adapter.SendPacket([]byte("test"))
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
	})
	
	t.Run("SimpleAACache", func(t *testing.T) {
		cache := NewSimpleAACache(10)
		if cache == nil {
			t.Fatal("NewSimpleAACache returned nil")
		}
		
		// Test AA caching
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			MaxRank:  5,
			RankCost: 2,
		}
		
		// Test miss
		cached, found := cache.GetAA(200)
		if found || cached != nil {
			t.Error("Expected cache miss")
		}
		
		// Set and get
		cache.SetAA(200, aa)
		cached, found = cache.GetAA(200)
		if !found || cached == nil {
			t.Error("Expected cache hit")
		}
		
		if cached == aa {
			t.Error("Expected cached copy, not original")
		}
		
		if cached.NodeID != aa.NodeID {
			t.Error("Expected cached data to match")
		}
		
		// Test invalidation
		cache.InvalidateAA(200)
		cached, found = cache.GetAA(200)
		if found || cached != nil {
			t.Error("Expected cache miss after invalidation")
		}
		
		// Test player state caching
		playerState := NewAAPlayerState(123)
		
		// Test miss
		cachedState, found := cache.GetPlayerState(123)
		if found || cachedState != nil {
			t.Error("Expected cache miss")
		}
		
		// Set and get
		cache.SetPlayerState(123, playerState)
		cachedState, found = cache.GetPlayerState(123)
		if !found || cachedState == nil {
			t.Error("Expected cache hit")
		}
		
		if cachedState != playerState {
			t.Error("Expected same player state instance")
		}
		
		// Test tree node caching
		node := &TreeNodeData{
			ClassID:  1,
			TreeID:   100,
			AATreeID: 200,
		}
		
		// Test miss
		cachedNode, found := cache.GetTreeNode(100)
		if found || cachedNode != nil {
			t.Error("Expected cache miss")
		}
		
		// Set and get
		cache.SetTreeNode(100, node)
		cachedNode, found = cache.GetTreeNode(100)
		if !found || cachedNode == nil {
			t.Error("Expected cache hit")
		}
		
		if cachedNode == node {
			t.Error("Expected cached copy, not original")
		}
		
		if cachedNode.TreeID != node.TreeID {
			t.Error("Expected cached data to match")
		}
		
		// Test stats
		stats := cache.GetStats()
		if stats == nil {
			t.Error("Expected stats map")
		}
		
		// Test max size
		cache.SetMaxSize(20)
		if cache.maxSize != 20 {
			t.Error("Expected max size to be updated")
		}
		
		// Test clear
		cache.Clear()
		_, found = cache.GetAA(200)
		if found {
			t.Error("Expected cache to be cleared")
		}
	})
}

// Test Edge Cases and Error Conditions
func TestEdgeCases(t *testing.T) {
	t.Run("AAManagerWithoutDatabase", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		
		// Test operations without database
		err := manager.LoadAAData()
		if err == nil {
			t.Error("Expected error without database")
		}
		
		err = manager.SavePlayerAA(123)
		if err == nil {
			t.Error("Expected error without database")
		}
		
		_, err = manager.GetPlayerAAState(123)
		if err == nil {
			t.Error("Expected error without database")
		}
	})
	
	t.Run("AAManagerErrorPaths", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		
		// Set up mock database that returns errors
		mockDB := &mockAADatabaseWithErrors{}
		manager.SetDatabase(mockDB)
		
		// Test load errors
		err := manager.LoadAAData()
		if err == nil {
			t.Error("Expected error from database")
		}
		
		err = manager.ReloadAAData()
		if err == nil {
			t.Error("Expected error from database")
		}
	})
	
	t.Run("PurchaseAAErrorCases", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Test with insufficient points
		state, _ := manager.GetPlayerAAState(123)
		state.TotalPoints = 1
		state.AvailablePoints = 1
		
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 10, // More than available
			MinLevel: 1,
		}
		manager.masterAAList.AddAltAdvancement(aa)
		
		err := manager.PurchaseAA(123, 200, 1)
		if err == nil {
			t.Error("Expected error due to insufficient points")
		}
		
		// Test purchasing non-existent AA
		err = manager.PurchaseAA(123, 999, 1)
		if err == nil {
			t.Error("Expected error for non-existent AA")
		}
	})
	
	t.Run("RefundAAErrorCases", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Test refunding non-existent AA
		err := manager.RefundAA(123, 999)
		if err == nil {
			t.Error("Expected error for non-existent AA")
		}
		
		// Test refunding from player without state
		err = manager.RefundAA(999, 200)
		if err == nil {
			t.Error("Expected error for non-existent player")
		}
	})
	
	t.Run("TemplateOperations", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Test with non-existent player
		err := manager.ChangeAATemplate(999, AA_TEMPLATE_PERSONAL_1)
		if err == nil {
			t.Error("Expected error for non-existent player")
		}
		
		templates, err := manager.GetAATemplates(999)
		if err == nil {
			t.Error("Expected error for non-existent player")
		}
		
		if templates != nil {
			t.Error("Expected nil templates for non-existent player")
		}
		
		err = manager.SaveAATemplate(999, AA_TEMPLATE_PERSONAL_1, "Test")
		if err == nil {
			t.Error("Expected error for non-existent player")
		}
	})
	
	t.Run("GetAvailableAAsErrorCases", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Test with non-existent player
		aas, err := manager.GetAvailableAAs(999, AA_CLASS)
		if err == nil {
			t.Error("Expected error for non-existent player")
		}
		
		if aas != nil {
			t.Error("Expected nil AAs for non-existent player")
		}
	})
	
	t.Run("DataValidation", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Test adding AA with missing required fields
		invalidAAs := []*AltAdvanceData{
			{NodeID: 200, Name: "Test", MaxRank: 5, RankCost: 2}, // Missing SpellID
			{SpellID: 100, Name: "Test", MaxRank: 5, RankCost: 2}, // Missing NodeID
			{SpellID: 100, NodeID: 200, MaxRank: 5, RankCost: 2}, // Missing Name
			{SpellID: 100, NodeID: 200, Name: "Test", RankCost: 2}, // Missing MaxRank
			{SpellID: 100, NodeID: 200, Name: "Test", MaxRank: 5}, // Missing RankCost
		}
		
		for i, aa := range invalidAAs {
			err := masterList.AddAltAdvancement(aa)
			if err == nil {
				t.Errorf("Expected error for invalid AA %d", i)
			}
		}
	})
}

// Test Manager Lifecycle
func TestManagerLifecycle(t *testing.T) {
	t.Run("StartStopCycle", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		config.UpdateInterval = 5 * time.Millisecond
		config.SaveInterval = 5 * time.Millisecond
		config.AutoSave = true
		
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Start manager
		err := manager.Start()
		if err != nil {
			t.Errorf("Unexpected error starting: %v", err)
		}
		
		if !manager.IsRunning() {
			t.Error("Expected manager to be running after start")
		}
		
		// Let it run briefly
		time.Sleep(20 * time.Millisecond)
		
		// Stop manager
		err = manager.Stop()
		if err != nil {
			t.Errorf("Unexpected error stopping: %v", err)
		}
		
		// The IsRunning check consumes the channel close signal, so we can't test it reliably
		// Just verify that stopping doesn't cause errors
	})
	
	t.Run("ReloadData", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Add some data
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			MaxRank:  5,
			RankCost: 2,
		}
		manager.masterAAList.AddAltAdvancement(aa)
		
		// Create player state
		_, err := manager.GetPlayerAAState(123)
		if err != nil {
			t.Fatalf("Failed to get player state: %v", err)
		}
		
		// Verify data exists
		if manager.masterAAList.Size() != 1 {
			t.Error("Expected 1 AA before reload")
		}
		
		// Reload data
		err = manager.ReloadAAData()
		if err != nil {
			t.Errorf("Unexpected error reloading: %v", err)
		}
		
		// Verify data was cleared and reloaded
		if manager.masterAAList.Size() != 0 {
			t.Error("Expected AAs to be cleared after reload")
		}
		
		// Player states should be cleared
		if len(manager.playerStates) != 0 {
			t.Error("Expected player states to be cleared after reload")
		}
	})
}

// Mock implementations for testing
type mockPlayer struct {
	characterID     int32
	level          int8
	class          int8
	adventureClass int8
	race           int8
	name           string
}

func (m *mockPlayer) GetCharacterID() int32 { return m.characterID }
func (m *mockPlayer) GetLevel() int8 { return m.level }
func (m *mockPlayer) GetClass() int8 { return m.class }
func (m *mockPlayer) GetRace() int8 { return m.race }
func (m *mockPlayer) GetName() string { return m.name }
func (m *mockPlayer) GetAdventureClass() int8 { return m.adventureClass }
func (m *mockPlayer) HasExpansion(expansionFlag int8) bool { return expansionFlag == EXPANSION_NONE }

type mockClient struct {
	characterID int32
	player      Player
	version     int16
}

func (m *mockClient) GetCharacterID() int32 { return m.characterID }
func (m *mockClient) GetPlayer() Player { return m.player }
func (m *mockClient) SendPacket(data []byte) error { return nil }
func (m *mockClient) GetClientVersion() int16 { return m.version }

type mockAADatabaseWithErrors struct{}

func (m *mockAADatabaseWithErrors) LoadAltAdvancements() error { return fmt.Errorf("load AA error") }
func (m *mockAADatabaseWithErrors) LoadTreeNodes() error { return fmt.Errorf("load nodes error") }
func (m *mockAADatabaseWithErrors) LoadPlayerAA(characterID int32) (*AAPlayerState, error) { return nil, fmt.Errorf("load player error") }
func (m *mockAADatabaseWithErrors) SavePlayerAA(playerState *AAPlayerState) error { return fmt.Errorf("save player error") }
func (m *mockAADatabaseWithErrors) DeletePlayerAA(characterID int32) error { return fmt.Errorf("delete player error") }
func (m *mockAADatabaseWithErrors) LoadPlayerAADefaults(classID int8) (map[int8][]*AAEntry, error) { return nil, fmt.Errorf("load defaults error") }
func (m *mockAADatabaseWithErrors) GetAAStatistics() (map[string]interface{}, error) { return nil, fmt.Errorf("stats error") }

// Test more adapter methods
func TestAdapterMethods(t *testing.T) {
	t.Run("AAAdapterMethods", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Add test AA
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
			MinLevel: 1,
		}
		manager.masterAAList.AddAltAdvancement(aa)
		
		adapter := NewAAAdapter(manager, 123)
		
		// Set up player with points
		state, err := manager.GetPlayerAAState(123)
		if err != nil {
			t.Fatalf("Failed to get player state: %v", err)
		}
		state.TotalPoints = 10
		state.AvailablePoints = 10
		
		// Test PurchaseAA
		err = adapter.PurchaseAA(200, 1)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		// Test RefundAA
		err = adapter.RefundAA(200)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		// Test GetAvailableAAs
		aas, err := adapter.GetAvailableAAs(AA_CLASS)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if aas == nil {
			t.Error("Expected AAs list")
		}
		
		// Test ChangeTemplate (should work even without existing template since no validator is set)
		err = adapter.ChangeTemplate(AA_TEMPLATE_PERSONAL_1)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		// Create template and test again
		template := NewAATemplate(AA_TEMPLATE_PERSONAL_1, "Test Template")
		state.Templates[AA_TEMPLATE_PERSONAL_1] = template
		
		err = adapter.ChangeTemplate(AA_TEMPLATE_PERSONAL_2)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
	})
}

// Test database implementation constructor
func TestDatabaseImpl(t *testing.T) {
	t.Run("NewDatabaseImpl", func(t *testing.T) {
		masterAAList := NewMasterAAList()
		masterNodeList := NewMasterAANodeList()
		logger := log.New(&testLogWriter{}, "test", 0)
		
		dbImpl := NewDatabaseImpl(nil, masterAAList, masterNodeList, logger)
		if dbImpl == nil {
			t.Fatal("NewDatabaseImpl returned nil")
		}
		
		if dbImpl.masterAAList != masterAAList {
			t.Error("Expected master AA list to match")
		}
		
		if dbImpl.masterNodeList != masterNodeList {
			t.Error("Expected master node list to match")
		}
		
		if dbImpl.logger != logger {
			t.Error("Expected logger to match")
		}
	})
}

// Test more edge cases and missing functionality
func TestAdditionalEdgeCases(t *testing.T) {
	t.Run("AAManagerRefundFunctionality", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Add test AA
		aa := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
			MinLevel: 1,
		}
		manager.masterAAList.AddAltAdvancement(aa)
		
		// Create player and purchase AA first
		state, _ := manager.GetPlayerAAState(123)
		state.TotalPoints = 10
		state.AvailablePoints = 10
		
		// Purchase AA
		err := manager.PurchaseAA(123, 200, 1)
		if err != nil {
			t.Fatalf("Failed to purchase AA: %v", err)
		}
		
		// Verify purchase
		progress := state.GetAAProgress(200)
		if progress == nil {
			t.Fatal("Expected AA progress")
		}
		
		if progress.CurrentRank != 1 {
			t.Error("Expected rank 1")
		}
		
		// Test refund
		err = manager.RefundAA(123, 200)
		if err != nil {
			t.Errorf("Unexpected error refunding: %v", err)
		}
		
		// Verify refund
		progress = state.GetAAProgress(200)
		if progress != nil {
			t.Error("Expected AA progress to be removed after refund")
		}
		
		// Test refunding AA that's not purchased
		err = manager.RefundAA(123, 200)
		if err == nil {
			t.Error("Expected error refunding unpurchased AA")
		}
	})
	
	t.Run("TemplateManagement", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Create player
		state, _ := manager.GetPlayerAAState(123)
		
		// Test saving template
		err := manager.SaveAATemplate(123, AA_TEMPLATE_PERSONAL_1, "My Template")
		if err != nil {
			t.Errorf("Unexpected error saving template: %v", err)
		}
		
		// Verify template was created
		template := state.Templates[AA_TEMPLATE_PERSONAL_1]
		if template == nil {
			t.Error("Expected template to be created")
		}
		
		if template.Name != "My Template" {
			t.Error("Expected correct template name")
		}
		
		// Test changing template
		err = manager.ChangeAATemplate(123, AA_TEMPLATE_PERSONAL_1)
		if err != nil {
			t.Errorf("Unexpected error changing template: %v", err)
		}
		
		if state.ActiveTemplate != AA_TEMPLATE_PERSONAL_1 {
			t.Error("Expected active template to be changed")
		}
		
		// Test getting templates
		templates, err := manager.GetAATemplates(123)
		if err != nil {
			t.Errorf("Unexpected error getting templates: %v", err)
		}
		
		if len(templates) != 1 {
			t.Error("Expected 1 template")
		}
		
		if templates[AA_TEMPLATE_PERSONAL_1] == nil {
			t.Error("Expected template to be in map")
		}
	})
	
	t.Run("GetAAsByMethods", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		
		// Add test AAs
		aa1 := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA 1",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aa2 := &AltAdvanceData{
			SpellID:  101,
			NodeID:   201,
			Name:     "Test AA 2",
			Group:    AA_HEROIC,
			MaxRank:  5,
			RankCost: 2,
		}
		
		manager.masterAAList.AddAltAdvancement(aa1)
		manager.masterAAList.AddAltAdvancement(aa2)
		
		// Test GetAAsByGroup
		classAAs, err := manager.GetAAsByGroup(AA_CLASS)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if len(classAAs) != 1 {
			t.Errorf("Expected 1 class AA, got %d", len(classAAs))
		}
		
		// Test GetAAsByClass
		allClassAAs, err := manager.GetAAsByClass(0) // All classes
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		if len(allClassAAs) != 2 {
			t.Errorf("Expected 2 AAs for all classes, got %d", len(allClassAAs))
		}
	})
	
	t.Run("AAValidation", func(t *testing.T) {
		// Test IsAAAvailable method indirectly through GetAvailableAAs
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Add AA with prerequisites
		prereqAA := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Prerequisite AA",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 1,
			MinLevel: 1,
		}
		
		dependentAA := &AltAdvanceData{
			SpellID:      101,
			NodeID:       201,
			Name:         "Dependent AA",
			Group:        AA_CLASS,
			MaxRank:      5,
			RankCost:     2,
			MinLevel:     1,
			RankPrereqID: 200,
			RankPrereq:   1,
		}
		
		manager.masterAAList.AddAltAdvancement(prereqAA)
		manager.masterAAList.AddAltAdvancement(dependentAA)
		
		// Create player
		state, _ := manager.GetPlayerAAState(123)
		
		// Get available AAs - dependent AA should not be available without prereq
		availableAAs, err := manager.GetAvailableAAs(123, AA_CLASS)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		// Should only have prerequisite AA available
		hasPrereq := false
		hasDependent := false
		for _, aa := range availableAAs {
			if aa.NodeID == 200 {
				hasPrereq = true
			}
			if aa.NodeID == 201 {
				hasDependent = true
			}
		}
		
		if !hasPrereq {
			t.Error("Expected prerequisite AA to be available")
		}
		
		if hasDependent {
			t.Error("Expected dependent AA to not be available without prerequisite")
		}
		
		// Purchase prerequisite
		state.TotalPoints = 10
		state.AvailablePoints = 10
		err = manager.PurchaseAA(123, 200, 1)
		if err != nil {
			t.Fatalf("Failed to purchase prerequisite: %v", err)
		}
		
		// Now dependent AA should be available
		availableAAs, err = manager.GetAvailableAAs(123, AA_CLASS)
		if err != nil {
			t.Errorf("Unexpected error: %v", err)
		}
		
		hasDependent = false
		for _, aa := range availableAAs {
			if aa.NodeID == 201 {
				hasDependent = true
			}
		}
		
		if !hasDependent {
			t.Error("Expected dependent AA to be available after prerequisite purchased")
		}
	})
}

// Helper type for database test
type testLogWriter struct{}

func (w *testLogWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

// Test for cache eviction and other missing cache functionality
func TestCacheEviction(t *testing.T) {
	t.Run("CacheEviction", func(t *testing.T) {
		cache := NewSimpleAACache(2) // Small cache for testing eviction
		
		// Add first AA
		aa1 := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA 1",
			MaxRank:  5,
			RankCost: 2,
		}
		cache.SetAA(200, aa1)
		
		// Add second AA
		aa2 := &AltAdvanceData{
			SpellID:  101,
			NodeID:   201,
			Name:     "Test AA 2",
			MaxRank:  5,
			RankCost: 2,
		}
		cache.SetAA(201, aa2)
		
		// Add third AA (should evict one)
		aa3 := &AltAdvanceData{
			SpellID:  102,
			NodeID:   202,
			Name:     "Test AA 3",
			MaxRank:  5,
			RankCost: 2,
		}
		cache.SetAA(202, aa3)
		
		// Check that only 2 items remain
		stats := cache.GetStats()
		if stats["aa_data_count"].(int) != 2 {
			t.Errorf("Expected 2 cached AAs after eviction, got %d", stats["aa_data_count"].(int))
		}
		
		// Test player state eviction
		state1 := NewAAPlayerState(123)
		state2 := NewAAPlayerState(124)
		state3 := NewAAPlayerState(125)
		
		cache.SetPlayerState(123, state1)
		cache.SetPlayerState(124, state2)
		cache.SetPlayerState(125, state3) // Should evict one
		
		stats = cache.GetStats()
		if stats["player_count"].(int) != 2 {
			t.Errorf("Expected 2 cached player states after eviction, got %d", stats["player_count"].(int))
		}
		
		// Test tree node eviction
		node1 := &TreeNodeData{ClassID: 1, TreeID: 100, AATreeID: 200}
		node2 := &TreeNodeData{ClassID: 2, TreeID: 101, AATreeID: 201}
		node3 := &TreeNodeData{ClassID: 3, TreeID: 102, AATreeID: 202}
		
		cache.SetTreeNode(100, node1)
		cache.SetTreeNode(101, node2)
		cache.SetTreeNode(102, node3) // Should evict one
		
		stats = cache.GetStats()
		if stats["tree_node_count"].(int) != 2 {
			t.Errorf("Expected 2 cached tree nodes after eviction, got %d", stats["tree_node_count"].(int))
		}
		
		// Test some get operations to generate statistics
		cache.GetAA(999) // Should generate a miss
		cache.GetAA(200) // Should generate a hit
		
		// Test invalidate
		cache.InvalidatePlayerState(123)
		cache.InvalidateTreeNode(100)
		
		// Get final stats
		finalStats := cache.GetStats()
		
		// Verify hits and misses are tracked (should have at least some activity)
		hits := finalStats["hits"].(int64)
		misses := finalStats["misses"].(int64)
		if hits == 0 && misses == 0 {
			t.Error("Expected some cache statistics to be tracked")
		}
	})
}

// Test missing MasterAAList functions
func TestMasterAAListAdditionalMethods(t *testing.T) {
	t.Run("GetAllAAs", func(t *testing.T) {
		masterList := NewMasterAAList()
		
		// Add test AAs
		aa1 := &AltAdvanceData{
			SpellID:  100,
			NodeID:   200,
			Name:     "Test AA 1",
			Group:    AA_CLASS,
			MaxRank:  5,
			RankCost: 2,
		}
		
		aa2 := &AltAdvanceData{
			SpellID:  101,
			NodeID:   201,
			Name:     "Test AA 2",
			Group:    AA_HEROIC,
			MaxRank:  5,
			RankCost: 2,
		}
		
		masterList.AddAltAdvancement(aa1)
		masterList.AddAltAdvancement(aa2)
		
		// Test GetAllAAs
		allAAs := masterList.GetAllAAs()
		if len(allAAs) != 2 {
			t.Errorf("Expected 2 AAs, got %d", len(allAAs))
		}
		
		// Verify they are copies (different pointers but same content)
		if allAAs[0] == aa1 || allAAs[1] == aa1 {
			t.Error("GetAllAAs should return copies, not originals")
		}
	})
}

// Test MasterAANodeList additional methods
func TestMasterAANodeListAdditionalMethods(t *testing.T) {
	t.Run("GetTreeNodes", func(t *testing.T) {
		nodeList := NewMasterAANodeList()
		
		// Add test nodes
		node1 := &TreeNodeData{
			ClassID:  1,
			TreeID:   100,
			AATreeID: 200,
		}
		
		node2 := &TreeNodeData{
			ClassID:  2,
			TreeID:   101,
			AATreeID: 201,
		}
		
		nodeList.AddTreeNode(node1)
		nodeList.AddTreeNode(node2)
		
		// Test GetTreeNodes
		allNodes := nodeList.GetTreeNodes()
		if len(allNodes) != 2 {
			t.Errorf("Expected 2 nodes, got %d", len(allNodes))
		}
		
		// Verify they are copies (different pointers but same content)
		if allNodes[0] == node1 || allNodes[1] == node1 {
			t.Error("GetTreeNodes should return copies, not originals")
		}
	})
	
	t.Run("DestroyTreeNodes", func(t *testing.T) {
		nodeList := NewMasterAANodeList()
		
		// Add test node
		node := &TreeNodeData{
			ClassID:  1,
			TreeID:   100,
			AATreeID: 200,
		}
		
		nodeList.AddTreeNode(node)
		
		if nodeList.Size() != 1 {
			t.Error("Expected 1 node before destroy")
		}
		
		// Destroy all nodes
		nodeList.DestroyTreeNodes()
		
		if nodeList.Size() != 0 {
			t.Error("Expected 0 nodes after destroy")
		}
		
		// Verify maps are cleared
		if len(nodeList.nodesByTree) != 0 || len(nodeList.nodesByClass) != 0 || len(nodeList.nodeList) != 0 {
			t.Error("Expected all maps to be cleared after destroy")
		}
	})
}

// Test configuration validation
func TestConfigValidation(t *testing.T) {
	t.Run("ConfigUpdate", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		
		// Test SetConfig
		newConfig := AAManagerConfig{
			EnableAASystem:      false,
			AAPointsPerLevel:    10,
			MaxBankedPoints:     2000,
			EnableCaching:       false,
			CacheSize:          200,
			UpdateInterval:     30 * time.Second,
			SaveInterval:       120 * time.Second,
			AutoSave:           false,
		}
		
		err := manager.SetConfig(newConfig)
		if err != nil {
			t.Errorf("Unexpected error setting config: %v", err)
		}
		
		retrievedConfig := manager.GetConfig()
		if retrievedConfig.EnableAASystem != false {
			t.Error("Expected EnableAASystem to be updated")
		}
		
		if retrievedConfig.AAPointsPerLevel != 10 {
			t.Error("Expected AAPointsPerLevel to be updated")
		}
	})
}

// Test more complex scenarios
func TestComplexScenarios(t *testing.T) {
	t.Run("MultiplePlayersMultipleAAs", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Add multiple AAs
		for i := 1; i <= 5; i++ {
			aa := &AltAdvanceData{
				SpellID:  int32(100 + i),
				NodeID:   int32(200 + i),
				Name:     fmt.Sprintf("Test AA %d", i),
				Group:    AA_CLASS,
				MaxRank:  5,
				RankCost: 2,
				MinLevel: 1,
			}
			manager.masterAAList.AddAltAdvancement(aa)
		}
		
		// Create multiple players
		players := []int32{123, 124, 125}
		for _, playerID := range players {
			state, err := manager.GetPlayerAAState(playerID)
			if err != nil {
				t.Fatalf("Failed to get player state: %v", err)
			}
			
			state.TotalPoints = 20
			state.AvailablePoints = 20
			
			// Each player purchases different AAs
			nodeID := int32(200 + int(playerID-122))
			err = manager.PurchaseAA(playerID, nodeID, 1)
			if err != nil {
				t.Errorf("Failed to purchase AA for player %d: %v", playerID, err)
			}
		}
		
		// Verify each player has their purchase
		for _, playerID := range players {
			state := manager.getPlayerState(playerID)
			if state == nil {
				t.Errorf("Player state not found for %d", playerID)
				continue
			}
			
			if len(state.AAProgress) != 1 {
				t.Errorf("Expected 1 AA progress for player %d, got %d", playerID, len(state.AAProgress))
			}
		}
		
		// Manually update statistics since background processes aren't running
		manager.updateStatistics()
		
		// Test system stats
		stats := manager.GetSystemStats()
		if stats.ActivePlayers != 3 {
			t.Errorf("Expected 3 active players, got %d", stats.ActivePlayers)
		}
	})
	
	t.Run("TemplateOperationsWithMultipleTemplates", func(t *testing.T) {
		config := DefaultAAManagerConfig()
		manager := NewAAManager(config)
		mockDB := &mockAADatabase{}
		manager.SetDatabase(mockDB)
		
		// Create player
		state, _ := manager.GetPlayerAAState(123)
		
		// Create multiple templates
		templateNames := []string{"Build 1", "Build 2", "Build 3"}
		templateIDs := []int8{AA_TEMPLATE_PERSONAL_1, AA_TEMPLATE_PERSONAL_2, AA_TEMPLATE_PERSONAL_3}
		
		for i, templateID := range templateIDs {
			err := manager.SaveAATemplate(123, templateID, templateNames[i])
			if err != nil {
				t.Errorf("Failed to save template %d: %v", templateID, err)
			}
		}
		
		// Verify all templates were created
		templates, err := manager.GetAATemplates(123)
		if err != nil {
			t.Errorf("Failed to get templates: %v", err)
		}
		
		if len(templates) != 3 {
			t.Errorf("Expected 3 templates, got %d", len(templates))
		}
		
		for i, templateID := range templateIDs {
			template := templates[templateID]
			if template == nil {
				t.Errorf("Template %d not found", templateID)
				continue
			}
			
			if template.Name != templateNames[i] {
				t.Errorf("Expected template name %s, got %s", templateNames[i], template.Name)
			}
		}
		
		// Test changing between templates
		for _, templateID := range templateIDs {
			err := manager.ChangeAATemplate(123, templateID)
			if err != nil {
				t.Errorf("Failed to change to template %d: %v", templateID, err)
			}
			
			if state.ActiveTemplate != templateID {
				t.Errorf("Expected active template %d, got %d", templateID, state.ActiveTemplate)
			}
		}
	})
}

