package appearances

import (
	"fmt"
	"sync"
	"testing"
)

func TestNewAppearance(t *testing.T) {
	tests := []struct {
		name             string
		id               int32
		appearanceName   string
		minClientVersion int16
		wantNil          bool
	}{
		{
			name:             "valid appearance",
			id:               100,
			appearanceName:   "Test Appearance",
			minClientVersion: 1096,
			wantNil:          false,
		},
		{
			name:             "empty name returns nil",
			id:               200,
			appearanceName:   "",
			minClientVersion: 1096,
			wantNil:          true,
		},
		{
			name:             "negative id allowed",
			id:               -50,
			appearanceName:   "Negative ID",
			minClientVersion: 0,
			wantNil:          false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			app := NewAppearance(tt.id, tt.appearanceName, tt.minClientVersion)
			
			if tt.wantNil {
				if app != nil {
					t.Errorf("expected nil appearance, got %v", app)
				}
				return
			}
			
			if app == nil {
				t.Fatal("expected non-nil appearance, got nil")
			}
			
			if app.GetID() != tt.id {
				t.Errorf("ID = %v, want %v", app.GetID(), tt.id)
			}
			
			if app.GetName() != tt.appearanceName {
				t.Errorf("Name = %v, want %v", app.GetName(), tt.appearanceName)
			}
			
			if app.GetMinClientVersion() != tt.minClientVersion {
				t.Errorf("MinClientVersion = %v, want %v", app.GetMinClientVersion(), tt.minClientVersion)
			}
		})
	}
}

func TestAppearanceGetters(t *testing.T) {
	app := NewAppearance(123, "Test Appearance", 1096)
	
	if id := app.GetID(); id != 123 {
		t.Errorf("GetID() = %v, want 123", id)
	}
	
	if name := app.GetName(); name != "Test Appearance" {
		t.Errorf("GetName() = %v, want Test Appearance", name)
	}
	
	if nameStr := app.GetNameString(); nameStr != "Test Appearance" {
		t.Errorf("GetNameString() = %v, want Test Appearance", nameStr)
	}
	
	if minVer := app.GetMinClientVersion(); minVer != 1096 {
		t.Errorf("GetMinClientVersion() = %v, want 1096", minVer)
	}
}

func TestAppearanceSetters(t *testing.T) {
	app := NewAppearance(100, "Original", 1000)
	
	app.SetName("Modified Name")
	if app.GetName() != "Modified Name" {
		t.Errorf("SetName failed: got %v, want Modified Name", app.GetName())
	}
	
	app.SetMinClientVersion(2000)
	if app.GetMinClientVersion() != 2000 {
		t.Errorf("SetMinClientVersion failed: got %v, want 2000", app.GetMinClientVersion())
	}
}

func TestIsCompatibleWithClient(t *testing.T) {
	app := NewAppearance(100, "Test", 1096)
	
	tests := []struct {
		clientVersion int16
		want          bool
	}{
		{1095, false}, // Below minimum
		{1096, true},  // Exact minimum
		{1097, true},  // Above minimum
		{2000, true},  // Well above minimum
		{0, false},    // Zero version
	}
	
	for _, tt := range tests {
		t.Run("", func(t *testing.T) {
			if got := app.IsCompatibleWithClient(tt.clientVersion); got != tt.want {
				t.Errorf("IsCompatibleWithClient(%v) = %v, want %v", tt.clientVersion, got, tt.want)
			}
		})
	}
}

func TestAppearanceClone(t *testing.T) {
	original := NewAppearance(500, "Original Appearance", 1200)
	clone := original.Clone()
	
	if clone == nil {
		t.Fatal("Clone returned nil")
	}
	
	if clone == original {
		t.Error("Clone returned same pointer as original")
	}
	
	if clone.GetID() != original.GetID() {
		t.Errorf("Clone ID = %v, want %v", clone.GetID(), original.GetID())
	}
	
	if clone.GetName() != original.GetName() {
		t.Errorf("Clone Name = %v, want %v", clone.GetName(), original.GetName())
	}
	
	if clone.GetMinClientVersion() != original.GetMinClientVersion() {
		t.Errorf("Clone MinClientVersion = %v, want %v", clone.GetMinClientVersion(), original.GetMinClientVersion())
	}
	
	// Verify modification independence
	clone.SetName("Modified Clone")
	if original.GetName() == "Modified Clone" {
		t.Error("Modifying clone affected original")
	}
}

func TestNewAppearances(t *testing.T) {
	apps := NewAppearances()
	
	if apps == nil {
		t.Fatal("NewAppearances returned nil")
	}
	
	if count := apps.GetAppearanceCount(); count != 0 {
		t.Errorf("New appearances collection should be empty, got count %v", count)
	}
}

func TestAppearancesInsertAndFind(t *testing.T) {
	apps := NewAppearances()
	
	// Test nil insertion
	err := apps.InsertAppearance(nil)
	if err == nil {
		t.Error("InsertAppearance(nil) should return error")
	}
	
	// Insert valid appearances
	app1 := NewAppearance(100, "Appearance 1", 1000)
	app2 := NewAppearance(200, "Appearance 2", 1100)
	
	if err := apps.InsertAppearance(app1); err != nil {
		t.Errorf("InsertAppearance failed: %v", err)
	}
	
	if err := apps.InsertAppearance(app2); err != nil {
		t.Errorf("InsertAppearance failed: %v", err)
	}
	
	// Test finding by ID
	found := apps.FindAppearanceByID(100)
	if found == nil {
		t.Error("FindAppearanceByID(100) returned nil")
	} else if found.GetName() != "Appearance 1" {
		t.Errorf("FindAppearanceByID(100) returned wrong appearance: %v", found.GetName())
	}
	
	// Test finding non-existent ID
	notFound := apps.FindAppearanceByID(999)
	if notFound != nil {
		t.Errorf("FindAppearanceByID(999) should return nil, got %v", notFound)
	}
}

func TestAppearancesHasAppearance(t *testing.T) {
	apps := NewAppearances()
	app := NewAppearance(300, "Test", 1000)
	apps.InsertAppearance(app)
	
	if !apps.HasAppearance(300) {
		t.Error("HasAppearance(300) should return true")
	}
	
	if apps.HasAppearance(999) {
		t.Error("HasAppearance(999) should return false")
	}
}

func TestAppearancesGetAllAndCount(t *testing.T) {
	apps := NewAppearances()
	
	// Add multiple appearances
	for i := int32(1); i <= 5; i++ {
		app := NewAppearance(i*100, "Appearance", 1000)
		apps.InsertAppearance(app)
	}
	
	if count := apps.GetAppearanceCount(); count != 5 {
		t.Errorf("GetAppearanceCount() = %v, want 5", count)
	}
	
	all := apps.GetAllAppearances()
	if len(all) != 5 {
		t.Errorf("GetAllAppearances() returned %v items, want 5", len(all))
	}
	
	// Verify it's a copy by modifying returned map
	delete(all, 100)
	if apps.GetAppearanceCount() != 5 {
		t.Error("Modifying returned map affected internal state")
	}
}

func TestAppearancesGetIDs(t *testing.T) {
	apps := NewAppearances()
	expectedIDs := []int32{100, 200, 300}
	
	for _, id := range expectedIDs {
		app := NewAppearance(id, "Test", 1000)
		apps.InsertAppearance(app)
	}
	
	ids := apps.GetAppearanceIDs()
	if len(ids) != len(expectedIDs) {
		t.Errorf("GetAppearanceIDs() returned %v IDs, want %v", len(ids), len(expectedIDs))
	}
	
	// Check all expected IDs are present
	idMap := make(map[int32]bool)
	for _, id := range ids {
		idMap[id] = true
	}
	
	for _, expected := range expectedIDs {
		if !idMap[expected] {
			t.Errorf("Expected ID %v not found in returned IDs", expected)
		}
	}
}

func TestAppearancesFindByName(t *testing.T) {
	apps := NewAppearances()
	
	apps.InsertAppearance(NewAppearance(1, "Human Male", 1000))
	apps.InsertAppearance(NewAppearance(2, "Human Female", 1000))
	apps.InsertAppearance(NewAppearance(3, "Elf Male", 1000))
	apps.InsertAppearance(NewAppearance(4, "Dwarf Female", 1000))
	
	// Test partial matching
	humanApps := apps.FindAppearancesByName("Human")
	if len(humanApps) != 2 {
		t.Errorf("FindAppearancesByName('Human') returned %v results, want 2", len(humanApps))
	}
	
	// Test case sensitivity
	maleApps := apps.FindAppearancesByName("Male")
	if len(maleApps) != 2 {
		t.Errorf("FindAppearancesByName('Male') returned %v results, want 2", len(maleApps))
	}
	
	// Test empty substring
	allApps := apps.FindAppearancesByName("")
	if len(allApps) != 4 {
		t.Errorf("FindAppearancesByName('') returned %v results, want 4", len(allApps))
	}
	
	// Test no matches
	noMatches := apps.FindAppearancesByName("Orc")
	if len(noMatches) != 0 {
		t.Errorf("FindAppearancesByName('Orc') returned %v results, want 0", len(noMatches))
	}
}

func TestAppearancesFindByMinClient(t *testing.T) {
	apps := NewAppearances()
	
	apps.InsertAppearance(NewAppearance(1, "Old", 1000))
	apps.InsertAppearance(NewAppearance(2, "Medium1", 1096))
	apps.InsertAppearance(NewAppearance(3, "Medium2", 1096))
	apps.InsertAppearance(NewAppearance(4, "New", 1200))
	
	results := apps.FindAppearancesByMinClient(1096)
	if len(results) != 2 {
		t.Errorf("FindAppearancesByMinClient(1096) returned %v results, want 2", len(results))
	}
	
	results = apps.FindAppearancesByMinClient(999)
	if len(results) != 0 {
		t.Errorf("FindAppearancesByMinClient(999) returned %v results, want 0", len(results))
	}
}

func TestAppearancesGetCompatible(t *testing.T) {
	apps := NewAppearances()
	
	apps.InsertAppearance(NewAppearance(1, "Old", 1000))
	apps.InsertAppearance(NewAppearance(2, "Medium", 1096))
	apps.InsertAppearance(NewAppearance(3, "New", 1200))
	apps.InsertAppearance(NewAppearance(4, "Newer", 1300))
	
	// Client version 1100 should get Old and Medium
	compatible := apps.GetCompatibleAppearances(1100)
	if len(compatible) != 2 {
		t.Errorf("GetCompatibleAppearances(1100) returned %v results, want 2", len(compatible))
	}
	
	// Client version 1500 should get all
	compatible = apps.GetCompatibleAppearances(1500)
	if len(compatible) != 4 {
		t.Errorf("GetCompatibleAppearances(1500) returned %v results, want 4", len(compatible))
	}
	
	// Client version 500 should get none
	compatible = apps.GetCompatibleAppearances(500)
	if len(compatible) != 0 {
		t.Errorf("GetCompatibleAppearances(500) returned %v results, want 0", len(compatible))
	}
}

func TestAppearancesRemove(t *testing.T) {
	apps := NewAppearances()
	app := NewAppearance(100, "Test", 1000)
	apps.InsertAppearance(app)
	
	// Remove existing
	if !apps.RemoveAppearance(100) {
		t.Error("RemoveAppearance(100) should return true")
	}
	
	if apps.HasAppearance(100) {
		t.Error("Appearance 100 should have been removed")
	}
	
	// Remove non-existent
	if apps.RemoveAppearance(100) {
		t.Error("RemoveAppearance(100) should return false for non-existent ID")
	}
}

func TestAppearancesUpdate(t *testing.T) {
	apps := NewAppearances()
	
	// Test updating nil
	err := apps.UpdateAppearance(nil)
	if err == nil {
		t.Error("UpdateAppearance(nil) should return error")
	}
	
	// Insert and update
	original := NewAppearance(100, "Original", 1000)
	apps.InsertAppearance(original)
	
	updated := NewAppearance(100, "Updated", 1100)
	err = apps.UpdateAppearance(updated)
	if err != nil {
		t.Errorf("UpdateAppearance failed: %v", err)
	}
	
	found := apps.FindAppearanceByID(100)
	if found.GetName() != "Updated" {
		t.Errorf("Updated appearance name = %v, want Updated", found.GetName())
	}
	
	// Update non-existent (should insert)
	new := NewAppearance(200, "New", 1200)
	err = apps.UpdateAppearance(new)
	if err != nil {
		t.Errorf("UpdateAppearance failed: %v", err)
	}
	
	if !apps.HasAppearance(200) {
		t.Error("UpdateAppearance should insert non-existent appearance")
	}
}

func TestAppearancesGetByIDRange(t *testing.T) {
	apps := NewAppearances()
	
	// Insert appearances with various IDs
	for _, id := range []int32{5, 10, 15, 20, 25, 30} {
		apps.InsertAppearance(NewAppearance(id, "Test", 1000))
	}
	
	results := apps.GetAppearancesByIDRange(10, 20)
	if len(results) != 3 { // Should get 10, 15, 20
		t.Errorf("GetAppearancesByIDRange(10, 20) returned %v results, want 3", len(results))
	}
	
	// Verify correct IDs
	idMap := make(map[int32]bool)
	for _, app := range results {
		idMap[app.GetID()] = true
	}
	
	for _, expectedID := range []int32{10, 15, 20} {
		if !idMap[expectedID] {
			t.Errorf("Expected ID %v not found in range results", expectedID)
		}
	}
	
	// Test empty range
	results = apps.GetAppearancesByIDRange(100, 200)
	if len(results) != 0 {
		t.Errorf("GetAppearancesByIDRange(100, 200) returned %v results, want 0", len(results))
	}
}

func TestAppearancesValidate(t *testing.T) {
	apps := NewAppearances()
	
	// Valid appearances
	apps.InsertAppearance(NewAppearance(100, "Valid", 1000))
	
	issues := apps.ValidateAppearances()
	if len(issues) != 0 {
		t.Errorf("ValidateAppearances() returned issues for valid data: %v", issues)
	}
	
	if !apps.IsValid() {
		t.Error("IsValid() should return true for valid data")
	}
	
	// Force invalid state by directly modifying map
	apps.mutex.Lock()
	apps.appearanceMap[200] = nil
	apps.appearanceMap[300] = NewAppearance(301, "", 1000) // ID mismatch and empty name
	apps.appearanceMap[400] = NewAppearance(400, "Negative", -100)
	apps.mutex.Unlock()
	
	issues = apps.ValidateAppearances()
	if len(issues) < 3 {
		t.Errorf("ValidateAppearances() should return at least 3 issues, got %v", len(issues))
	}
	
	if apps.IsValid() {
		t.Error("IsValid() should return false for invalid data")
	}
}

func TestAppearancesStatistics(t *testing.T) {
	apps := NewAppearances()
	
	// Add appearances with different client versions
	apps.InsertAppearance(NewAppearance(10, "A", 1000))
	apps.InsertAppearance(NewAppearance(20, "B", 1000))
	apps.InsertAppearance(NewAppearance(30, "C", 1096))
	apps.InsertAppearance(NewAppearance(40, "D", 1096))
	apps.InsertAppearance(NewAppearance(50, "E", 1096))
	
	stats := apps.GetStatistics()
	
	if total, ok := stats["total_appearances"].(int); !ok || total != 5 {
		t.Errorf("total_appearances = %v, want 5", stats["total_appearances"])
	}
	
	if minID, ok := stats["min_id"].(int32); !ok || minID != 10 {
		t.Errorf("min_id = %v, want 10", stats["min_id"])
	}
	
	if maxID, ok := stats["max_id"].(int32); !ok || maxID != 50 {
		t.Errorf("max_id = %v, want 50", stats["max_id"])
	}
	
	if idRange, ok := stats["id_range"].(int32); !ok || idRange != 40 {
		t.Errorf("id_range = %v, want 40", stats["id_range"])
	}
	
	if versionCounts, ok := stats["appearances_by_min_client"].(map[int16]int); ok {
		if versionCounts[1000] != 2 {
			t.Errorf("appearances with min client 1000 = %v, want 2", versionCounts[1000])
		}
		if versionCounts[1096] != 3 {
			t.Errorf("appearances with min client 1096 = %v, want 3", versionCounts[1096])
		}
	} else {
		t.Error("appearances_by_min_client not found in statistics")
	}
}

func TestAppearancesClearAndReset(t *testing.T) {
	apps := NewAppearances()
	
	// Add some appearances
	for i := int32(1); i <= 3; i++ {
		apps.InsertAppearance(NewAppearance(i*100, "Test", 1000))
	}
	
	if apps.GetAppearanceCount() != 3 {
		t.Error("Setup failed: should have 3 appearances")
	}
	
	// Test ClearAppearances
	apps.ClearAppearances()
	if apps.GetAppearanceCount() != 0 {
		t.Errorf("ClearAppearances() failed: count = %v, want 0", apps.GetAppearanceCount())
	}
	
	// Add again and test Reset
	for i := int32(1); i <= 3; i++ {
		apps.InsertAppearance(NewAppearance(i*100, "Test", 1000))
	}
	
	apps.Reset()
	if apps.GetAppearanceCount() != 0 {
		t.Errorf("Reset() failed: count = %v, want 0", apps.GetAppearanceCount())
	}
}

func TestAppearancesConcurrency(t *testing.T) {
	apps := NewAppearances()
	var wg sync.WaitGroup
	
	// Concurrent insertions
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(id int32) {
			defer wg.Done()
			app := NewAppearance(id, "Concurrent", 1000)
			apps.InsertAppearance(app)
		}(int32(i))
	}
	
	// Concurrent reads
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			_ = apps.GetAppearanceCount()
			_ = apps.GetAllAppearances()
			_ = apps.FindAppearanceByID(25)
		}()
	}
	
	// Concurrent searches
	for i := 0; i < 20; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			_ = apps.FindAppearancesByName("Concurrent")
			_ = apps.GetCompatibleAppearances(1100)
		}()
	}
	
	wg.Wait()
	
	// Verify all insertions succeeded
	if count := apps.GetAppearanceCount(); count != 100 {
		t.Errorf("After concurrent operations, count = %v, want 100", count)
	}
}

func TestContainsFunction(t *testing.T) {
	tests := []struct {
		str    string
		substr string
		want   bool
	}{
		{"hello world", "world", true},
		{"hello world", "World", false}, // Case sensitive
		{"hello", "hello world", false},
		{"hello", "", true},
		{"", "hello", false},
		{"", "", true},
		{"abcdef", "cde", true},
		{"abcdef", "xyz", false},
	}
	
	for _, tt := range tests {
		t.Run("", func(t *testing.T) {
			if got := contains(tt.str, tt.substr); got != tt.want {
				t.Errorf("contains(%q, %q) = %v, want %v", tt.str, tt.substr, got, tt.want)
			}
		})
	}
}

// Benchmarks
func BenchmarkAppearanceInsert(b *testing.B) {
	apps := NewAppearances()
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		app := NewAppearance(int32(i), "Benchmark", 1000)
		apps.InsertAppearance(app)
	}
}

func BenchmarkAppearanceFindByID(b *testing.B) {
	apps := NewAppearances()
	
	// Pre-populate
	for i := 0; i < 10000; i++ {
		app := NewAppearance(int32(i), "Benchmark", 1000)
		apps.InsertAppearance(app)
	}
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		apps.FindAppearanceByID(int32(i % 10000))
	}
}

func BenchmarkAppearanceFindByName(b *testing.B) {
	apps := NewAppearances()
	
	// Pre-populate with varied names
	names := []string{"Human Male", "Human Female", "Elf Male", "Elf Female", "Dwarf Male"}
	for i := 0; i < 1000; i++ {
		app := NewAppearance(int32(i), names[i%len(names)], 1000)
		apps.InsertAppearance(app)
	}
	
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		apps.FindAppearancesByName("Male")
	}
}

// Mock implementations
type MockDatabase struct {
	appearances []*Appearance
	saveError   error
	loadError   error
	deleteError error
}

func (m *MockDatabase) LoadAllAppearances() ([]*Appearance, error) {
	if m.loadError != nil {
		return nil, m.loadError
	}
	return m.appearances, nil
}

func (m *MockDatabase) SaveAppearance(appearance *Appearance) error {
	return m.saveError
}

func (m *MockDatabase) DeleteAppearance(id int32) error {
	return m.deleteError
}

func (m *MockDatabase) LoadAppearancesByClientVersion(minClientVersion int16) ([]*Appearance, error) {
	var results []*Appearance
	for _, app := range m.appearances {
		if app.GetMinClientVersion() == minClientVersion {
			results = append(results, app)
		}
	}
	return results, nil
}

type MockLogger struct {
	logs []string
}

func (m *MockLogger) LogInfo(message string, args ...interface{}) {
	m.logs = append(m.logs, fmt.Sprintf("INFO: "+message, args...))
}

func (m *MockLogger) LogError(message string, args ...interface{}) {
	m.logs = append(m.logs, fmt.Sprintf("ERROR: "+message, args...))
}

func (m *MockLogger) LogDebug(message string, args ...interface{}) {
	m.logs = append(m.logs, fmt.Sprintf("DEBUG: "+message, args...))
}

func (m *MockLogger) LogWarning(message string, args ...interface{}) {
	m.logs = append(m.logs, fmt.Sprintf("WARNING: "+message, args...))
}

type MockEntity struct {
	id         int32
	name       string
	databaseID int32
}

func (m *MockEntity) GetID() int32         { return m.id }
func (m *MockEntity) GetName() string      { return m.name }
func (m *MockEntity) GetDatabaseID() int32 { return m.databaseID }

type MockClient struct {
	version   int16
	sendError error
}

func (m *MockClient) GetVersion() int16 { return m.version }
func (m *MockClient) SendAppearanceUpdate(appearanceID int32) error { return m.sendError }

// Manager tests
func TestNewManager(t *testing.T) {
	db := &MockDatabase{}
	logger := &MockLogger{}
	
	manager := NewManager(db, logger)
	
	if manager == nil {
		t.Fatal("NewManager returned nil")
	}
	
	if manager.database != db {
		t.Error("Manager database not set correctly")
	}
	
	if manager.logger != logger {
		t.Error("Manager logger not set correctly")
	}
	
	if manager.appearances == nil {
		t.Error("Manager appearances not initialized")
	}
}

func TestManagerInitialize(t *testing.T) {
	tests := []struct {
		name        string
		database    *MockDatabase
		wantError   bool
		wantCount   int
		wantLogInfo bool
	}{
		{
			name: "successful initialization",
			database: &MockDatabase{
				appearances: []*Appearance{
					NewAppearance(1, "Test1", 1000),
					NewAppearance(2, "Test2", 1096),
				},
			},
			wantError:   false,
			wantCount:   2,
			wantLogInfo: true,
		},
		{
			name:        "nil database",
			database:    nil,
			wantError:   false,
			wantCount:   0,
			wantLogInfo: true,
		},
		{
			name: "database load error",
			database: &MockDatabase{
				loadError: fmt.Errorf("database error"),
			},
			wantError:   true,
			wantCount:   0,
			wantLogInfo: true,
		},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			logger := &MockLogger{}
			var manager *Manager
			if tt.database != nil {
				manager = NewManager(tt.database, logger)
			} else {
				manager = &Manager{
					appearances: NewAppearances(),
					database:    nil,
					logger:      logger,
				}
			}
			
			err := manager.Initialize()
			
			if (err != nil) != tt.wantError {
				t.Errorf("Initialize() error = %v, wantError %v", err, tt.wantError)
			}
			
			if count := manager.GetAppearanceCount(); count != tt.wantCount {
				t.Errorf("GetAppearanceCount() = %v, want %v", count, tt.wantCount)
			}
			
			if tt.wantLogInfo && len(logger.logs) == 0 {
				t.Error("Expected log messages, got none")
			}
		})
	}
}

func TestManagerFindAppearanceByID(t *testing.T) {
	db := &MockDatabase{
		appearances: []*Appearance{
			NewAppearance(100, "Test", 1000),
		},
	}
	logger := &MockLogger{}
	manager := NewManager(db, logger)
	manager.Initialize()
	
	// Test successful lookup
	appearance := manager.FindAppearanceByID(100)
	if appearance == nil {
		t.Error("FindAppearanceByID(100) returned nil")
	}
	
	// Test failed lookup
	appearance = manager.FindAppearanceByID(999)
	if appearance != nil {
		t.Error("FindAppearanceByID(999) should return nil")
	}
	
	// Check statistics
	stats := manager.GetStatistics()
	if totalLookups, ok := stats["total_lookups"].(int64); !ok || totalLookups != 2 {
		t.Errorf("total_lookups = %v, want 2", stats["total_lookups"])
	}
	
	if successfulLookups, ok := stats["successful_lookups"].(int64); !ok || successfulLookups != 1 {
		t.Errorf("successful_lookups = %v, want 1", stats["successful_lookups"])
	}
	
	if failedLookups, ok := stats["failed_lookups"].(int64); !ok || failedLookups != 1 {
		t.Errorf("failed_lookups = %v, want 1", stats["failed_lookups"])
	}
}

func TestManagerAddAppearance(t *testing.T) {
	tests := []struct {
		name          string
		appearance    *Appearance
		saveError     error
		existingID    int32
		wantError     bool
		errorContains string
	}{
		{
			name:       "successful add",
			appearance: NewAppearance(100, "Test", 1000),
			wantError:  false,
		},
		{
			name:          "nil appearance",
			appearance:    nil,
			wantError:     true,
			errorContains: "cannot be nil",
		},
		{
			name:          "empty name",
			appearance:    &Appearance{id: 100, name: "", minClient: 1000},
			wantError:     true,
			errorContains: "name cannot be empty",
		},
		{
			name:          "invalid ID",
			appearance:    NewAppearance(0, "Test", 1000),
			wantError:     true,
			errorContains: "must be positive",
		},
		{
			name:          "duplicate ID",
			appearance:    NewAppearance(100, "Test", 1000),
			existingID:    100,
			wantError:     true,
			errorContains: "already exists",
		},
		{
			name:          "database save error",
			appearance:    NewAppearance(200, "Test", 1000),
			saveError:     fmt.Errorf("save failed"),
			wantError:     true,
			errorContains: "database",
		},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			db := &MockDatabase{saveError: tt.saveError}
			logger := &MockLogger{}
			manager := NewManager(db, logger)
			
			if tt.existingID > 0 {
				manager.appearances.InsertAppearance(NewAppearance(tt.existingID, "Existing", 1000))
			}
			
			err := manager.AddAppearance(tt.appearance)
			
			if (err != nil) != tt.wantError {
				t.Errorf("AddAppearance() error = %v, wantError %v", err, tt.wantError)
			}
			
			if err != nil && tt.errorContains != "" && !contains(err.Error(), tt.errorContains) {
				t.Errorf("Error message %q doesn't contain %q", err.Error(), tt.errorContains)
			}
			
			// Verify appearance was added/not added
			if !tt.wantError && tt.appearance != nil {
				if !manager.appearances.HasAppearance(tt.appearance.GetID()) {
					t.Error("Appearance was not added to collection")
				}
			}
		})
	}
}

func TestManagerUpdateAppearance(t *testing.T) {
	db := &MockDatabase{}
	logger := &MockLogger{}
	manager := NewManager(db, logger)
	
	// Add initial appearance
	original := NewAppearance(100, "Original", 1000)
	manager.AddAppearance(original)
	
	// Test successful update
	updated := NewAppearance(100, "Updated", 1100)
	err := manager.UpdateAppearance(updated)
	if err != nil {
		t.Errorf("UpdateAppearance failed: %v", err)
	}
	
	found := manager.FindAppearanceByID(100)
	if found.GetName() != "Updated" {
		t.Error("Appearance was not updated")
	}
	
	// Test update non-existent
	notExist := NewAppearance(999, "NotExist", 1000)
	err = manager.UpdateAppearance(notExist)
	if err == nil {
		t.Error("UpdateAppearance should fail for non-existent appearance")
	}
	
	// Test nil appearance
	err = manager.UpdateAppearance(nil)
	if err == nil {
		t.Error("UpdateAppearance should fail for nil appearance")
	}
}

func TestManagerRemoveAppearance(t *testing.T) {
	db := &MockDatabase{}
	logger := &MockLogger{}
	manager := NewManager(db, logger)
	
	// Add appearance
	app := NewAppearance(100, "Test", 1000)
	manager.AddAppearance(app)
	
	// Test successful removal
	err := manager.RemoveAppearance(100)
	if err != nil {
		t.Errorf("RemoveAppearance failed: %v", err)
	}
	
	if manager.appearances.HasAppearance(100) {
		t.Error("Appearance was not removed")
	}
	
	// Test removing non-existent
	err = manager.RemoveAppearance(999)
	if err == nil {
		t.Error("RemoveAppearance should fail for non-existent appearance")
	}
	
	// Test database delete error
	db.deleteError = fmt.Errorf("delete failed")
	manager.AddAppearance(NewAppearance(200, "Test2", 1000))
	err = manager.RemoveAppearance(200)
	if err == nil {
		t.Error("RemoveAppearance should fail when database delete fails")
	}
}

func TestManagerCommands(t *testing.T) {
	db := &MockDatabase{
		appearances: []*Appearance{
			NewAppearance(100, "Human Male", 1000),
			NewAppearance(200, "Human Female", 1096),
		},
	}
	logger := &MockLogger{}
	manager := NewManager(db, logger)
	manager.Initialize()
	
	// Test stats command
	result, err := manager.ProcessCommand("stats", nil)
	if err != nil {
		t.Errorf("ProcessCommand(stats) failed: %v", err)
	}
	if !contains(result, "Appearance System Statistics") {
		t.Error("Stats command output incorrect")
	}
	
	// Test validate command
	result, err = manager.ProcessCommand("validate", nil)
	if err != nil {
		t.Errorf("ProcessCommand(validate) failed: %v", err)
	}
	if !contains(result, "valid") {
		t.Error("Validate command output incorrect")
	}
	
	// Test search command
	result, err = manager.ProcessCommand("search", []string{"Human"})
	if err != nil {
		t.Errorf("ProcessCommand(search) failed: %v", err)
	}
	if !contains(result, "Found 2 appearances") {
		t.Error("Search command output incorrect")
	}
	
	// Test search without args
	_, err = manager.ProcessCommand("search", nil)
	if err == nil {
		t.Error("Search command should fail without arguments")
	}
	
	// Test info command
	result, err = manager.ProcessCommand("info", []string{"100"})
	if err != nil {
		t.Errorf("ProcessCommand(info) failed: %v", err)
	}
	if !contains(result, "Human Male") {
		t.Error("Info command output incorrect")
	}
	
	// Test info without args
	_, err = manager.ProcessCommand("info", nil)
	if err == nil {
		t.Error("Info command should fail without arguments")
	}
	
	// Test unknown command
	_, err = manager.ProcessCommand("unknown", nil)
	if err == nil {
		t.Error("Unknown command should return error")
	}
}

func TestManagerResetStatistics(t *testing.T) {
	manager := NewManager(nil, nil)
	
	// Perform some lookups
	manager.FindAppearanceByID(100)
	manager.FindAppearanceByID(200)
	
	stats := manager.GetStatistics()
	if totalLookups, ok := stats["total_lookups"].(int64); !ok || totalLookups != 2 {
		t.Error("Statistics not tracked correctly")
	}
	
	// Reset statistics
	manager.ResetStatistics()
	
	stats = manager.GetStatistics()
	if totalLookups, ok := stats["total_lookups"].(int64); !ok || totalLookups != 0 {
		t.Error("Statistics not reset correctly")
	}
}

func TestManagerShutdown(t *testing.T) {
	logger := &MockLogger{}
	manager := NewManager(nil, logger)
	
	manager.appearances.InsertAppearance(NewAppearance(100, "Test", 1000))
	
	manager.Shutdown()
	
	if manager.GetAppearanceCount() != 0 {
		t.Error("Shutdown did not clear appearances")
	}
	
	// Check for shutdown log
	found := false
	for _, log := range logger.logs {
		if contains(log, "Shutting down") {
			found = true
			break
		}
	}
	if !found {
		t.Error("Shutdown did not log message")
	}
}

// EntityAppearanceAdapter tests
func TestEntityAppearanceAdapter(t *testing.T) {
	entity := &MockEntity{id: 1, name: "TestEntity", databaseID: 100}
	logger := &MockLogger{}
	manager := NewManager(nil, logger)
	
	// Add test appearance
	app := NewAppearance(500, "TestAppearance", 1000)
	manager.AddAppearance(app)
	
	adapter := NewEntityAppearanceAdapter(entity, manager, logger)
	
	// Test initial state
	if adapter.GetAppearanceID() != 0 {
		t.Error("Initial appearance ID should be 0")
	}
	
	if adapter.GetAppearance() != nil {
		t.Error("Initial appearance should be nil")
	}
	
	// Test setting appearance ID
	adapter.SetAppearanceID(500)
	if adapter.GetAppearanceID() != 500 {
		t.Error("SetAppearanceID failed")
	}
	
	// Test getting appearance
	appearance := adapter.GetAppearance()
	if appearance == nil || appearance.GetID() != 500 {
		t.Error("GetAppearance failed")
	}
	
	// Test appearance name
	if name := adapter.GetAppearanceName(); name != "TestAppearance" {
		t.Errorf("GetAppearanceName() = %v, want TestAppearance", name)
	}
	
	// Test validation
	if err := adapter.ValidateAppearance(); err != nil {
		t.Errorf("ValidateAppearance() failed: %v", err)
	}
	
	// Test invalid appearance
	adapter.SetAppearanceID(999)
	if err := adapter.ValidateAppearance(); err == nil {
		t.Error("ValidateAppearance() should fail for invalid ID")
	}
}

func TestEntityAppearanceAdapterClientCompatibility(t *testing.T) {
	entity := &MockEntity{id: 1, name: "TestEntity"}
	manager := NewManager(nil, nil)
	
	// Add appearance with version requirement
	app := NewAppearance(100, "Test", 1096)
	manager.AddAppearance(app)
	
	adapter := NewEntityAppearanceAdapter(entity, manager, nil)
	adapter.SetAppearanceID(100)
	
	// Test compatible client
	if !adapter.IsCompatibleWithClient(1100) {
		t.Error("Should be compatible with client version 1100")
	}
	
	// Test incompatible client
	if adapter.IsCompatibleWithClient(1000) {
		t.Error("Should not be compatible with client version 1000")
	}
	
	// Test no appearance (always compatible)
	adapter.SetAppearanceID(0)
	if !adapter.IsCompatibleWithClient(500) {
		t.Error("No appearance should be compatible with all clients")
	}
}

func TestEntityAppearanceAdapterSendToClient(t *testing.T) {
	entity := &MockEntity{id: 1, name: "TestEntity"}
	logger := &MockLogger{}
	manager := NewManager(nil, logger)
	
	app := NewAppearance(100, "Test", 1096)
	manager.AddAppearance(app)
	
	adapter := NewEntityAppearanceAdapter(entity, manager, logger)
	adapter.SetAppearanceID(100)
	
	// Test successful send
	client := &MockClient{version: 1100}
	err := adapter.SendAppearanceToClient(client)
	if err != nil {
		t.Errorf("SendAppearanceToClient failed: %v", err)
	}
	
	// Test incompatible client
	lowClient := &MockClient{version: 1000}
	err = adapter.SendAppearanceToClient(lowClient)
	if err == nil {
		t.Error("SendAppearanceToClient should fail for incompatible client")
	}
	
	// Test client send error
	errorClient := &MockClient{version: 1100, sendError: fmt.Errorf("send failed")}
	err = adapter.SendAppearanceToClient(errorClient)
	if err == nil {
		t.Error("SendAppearanceToClient should propagate client error")
	}
	
	// Test nil client
	err = adapter.SendAppearanceToClient(nil)
	if err == nil {
		t.Error("SendAppearanceToClient should fail for nil client")
	}
}

// Cache tests
func TestSimpleAppearanceCache(t *testing.T) {
	cache := NewSimpleAppearanceCache()
	
	app1 := NewAppearance(100, "Test1", 1000)
	app2 := NewAppearance(200, "Test2", 1096)
	
	// Test Set and Get
	cache.Set(100, app1)
	cache.Set(200, app2)
	
	if got := cache.Get(100); got != app1 {
		t.Error("Cache Get(100) failed")
	}
	
	if got := cache.Get(999); got != nil {
		t.Error("Cache Get(999) should return nil")
	}
	
	// Test GetSize
	if size := cache.GetSize(); size != 2 {
		t.Errorf("GetSize() = %v, want 2", size)
	}
	
	// Test Remove
	cache.Remove(100)
	if cache.Get(100) != nil {
		t.Error("Remove(100) failed")
	}
	
	if size := cache.GetSize(); size != 1 {
		t.Errorf("GetSize() after remove = %v, want 1", size)
	}
	
	// Test Clear
	cache.Clear()
	if size := cache.GetSize(); size != 0 {
		t.Errorf("GetSize() after clear = %v, want 0", size)
	}
}

func TestCachedAppearanceManager(t *testing.T) {
	db := &MockDatabase{}
	logger := &MockLogger{}
	baseManager := NewManager(db, logger)
	
	// Add test appearances
	app1 := NewAppearance(100, "Test1", 1000)
	app2 := NewAppearance(200, "Test2", 1096)
	baseManager.AddAppearance(app1)
	baseManager.AddAppearance(app2)
	
	cache := NewSimpleAppearanceCache()
	cachedManager := NewCachedAppearanceManager(baseManager, cache)
	
	// Test FindAppearanceByID with caching
	found := cachedManager.FindAppearanceByID(100)
	if found == nil || found.GetID() != 100 {
		t.Error("FindAppearanceByID(100) failed")
	}
	
	// Verify it was cached
	if cache.GetSize() != 1 {
		t.Error("Appearance was not cached")
	}
	
	// Test cache hit
	found2 := cachedManager.FindAppearanceByID(100)
	if found2 != found {
		t.Error("Should have returned cached appearance")
	}
	
	// Test AddAppearance updates cache
	app3 := NewAppearance(300, "Test3", 1200)
	err := cachedManager.AddAppearance(app3)
	if err != nil {
		t.Errorf("AddAppearance failed: %v", err)
	}
	
	if cache.Get(300) == nil {
		t.Error("Added appearance was not cached")
	}
	
	// Test UpdateAppearance updates cache
	updated := NewAppearance(100, "Updated", 1100)
	err = cachedManager.UpdateAppearance(updated)
	if err != nil {
		t.Errorf("UpdateAppearance failed: %v", err)
	}
	
	cached := cache.Get(100)
	if cached == nil || cached.GetName() != "Updated" {
		t.Error("Cache was not updated")
	}
	
	// Test RemoveAppearance updates cache
	err = cachedManager.RemoveAppearance(200)
	if err != nil {
		t.Errorf("RemoveAppearance failed: %v", err)
	}
	
	if cache.Get(200) != nil {
		t.Error("Removed appearance still in cache")
	}
	
	// Test ClearCache
	cachedManager.ClearCache()
	if cache.GetSize() != 0 {
		t.Error("ClearCache failed")
	}
}

func TestCacheConcurrency(t *testing.T) {
	cache := NewSimpleAppearanceCache()
	var wg sync.WaitGroup
	
	// Concurrent operations
	for i := 0; i < 100; i++ {
		wg.Add(3)
		
		// Writer
		go func(id int32) {
			defer wg.Done()
			app := NewAppearance(id, "Test", 1000)
			cache.Set(id, app)
		}(int32(i))
		
		// Reader
		go func(id int32) {
			defer wg.Done()
			_ = cache.Get(id)
		}(int32(i))
		
		// Size checker
		go func() {
			defer wg.Done()
			_ = cache.GetSize()
		}()
	}
	
	wg.Wait()
	
	// Final size should be predictable
	if size := cache.GetSize(); size > 100 {
		t.Errorf("Cache size %v is too large", size)
	}
}

// Additional tests for uncovered code paths
func TestEntityAppearanceAdapterErrorCases(t *testing.T) {
	entity := &MockEntity{id: 1, name: "TestEntity"}
	logger := &MockLogger{}
	
	// Test with nil manager
	adapter := NewEntityAppearanceAdapter(entity, nil, logger)
	adapter.SetAppearanceID(100)
	
	// GetAppearance should return nil and log error
	appearance := adapter.GetAppearance()
	if appearance != nil {
		t.Error("GetAppearance should return nil with nil manager")
	}
	
	// Test UpdateAppearance with nil manager
	err := adapter.UpdateAppearance(100)
	if err == nil {
		t.Error("UpdateAppearance should fail with nil manager")
	}
	
	// Test UpdateAppearance with non-existent appearance
	manager := NewManager(nil, logger)
	adapter = NewEntityAppearanceAdapter(entity, manager, logger)
	err = adapter.UpdateAppearance(999)
	if err == nil {
		t.Error("UpdateAppearance should fail for non-existent appearance")
	}
	
	// Test SendAppearanceToClient with no appearance set
	client := &MockClient{version: 1100}
	adapter.SetAppearanceID(0)
	err = adapter.SendAppearanceToClient(client)
	if err != nil {
		t.Errorf("SendAppearanceToClient should succeed with no appearance: %v", err)
	}
}

func TestManagerGetAppearances(t *testing.T) {
	manager := NewManager(nil, nil)
	
	appearances := manager.GetAppearances()
	if appearances == nil {
		t.Error("GetAppearances should not return nil")
	}
	
	if appearances != manager.appearances {
		t.Error("GetAppearances should return internal appearances collection")
	}
}

func TestManagerCompatibleAndSearch(t *testing.T) {
	manager := NewManager(nil, nil)
	
	// Add test appearances
	app1 := NewAppearance(100, "Human Male", 1000)
	app2 := NewAppearance(200, "Human Female", 1096)
	manager.AddAppearance(app1)
	manager.AddAppearance(app2)
	
	// Test GetCompatibleAppearances
	compatible := manager.GetCompatibleAppearances(1050)
	if len(compatible) != 1 {
		t.Errorf("GetCompatibleAppearances(1050) returned %v results, want 1", len(compatible))
	}
	
	// Test SearchAppearancesByName
	results := manager.SearchAppearancesByName("Human")
	if len(results) != 2 {
		t.Errorf("SearchAppearancesByName('Human') returned %v results, want 2", len(results))
	}
}

func TestManagerReloadFromDatabase(t *testing.T) {
	// Test with nil database
	manager := NewManager(nil, nil)
	err := manager.ReloadFromDatabase()
	if err == nil {
		t.Error("ReloadFromDatabase should fail with nil database")
	}
	
	// Test successful reload
	db := &MockDatabase{
		appearances: []*Appearance{
			NewAppearance(100, "Test", 1000),
		},
	}
	manager = NewManager(db, nil)
	
	// Add some appearances first
	manager.AddAppearance(NewAppearance(200, "Existing", 1000))
	
	err = manager.ReloadFromDatabase()
	if err != nil {
		t.Errorf("ReloadFromDatabase failed: %v", err)
	}
	
	// Should only have the database appearance now
	if count := manager.GetAppearanceCount(); count != 1 {
		t.Errorf("After reload, count = %v, want 1", count)
	}
	
	if !manager.appearances.HasAppearance(100) {
		t.Error("Reloaded appearance not found")
	}
	
	if manager.appearances.HasAppearance(200) {
		t.Error("Previous appearance should be cleared")
	}
}

func TestManagerCommandEdgeCases(t *testing.T) {
	manager := NewManager(nil, nil)
	
	// Test reload command without database
	result, err := manager.ProcessCommand("reload", nil)
	if err == nil {
		t.Error("Reload command should fail without database")
	}
	
	// Test info command with invalid ID
	_, err = manager.ProcessCommand("info", []string{"invalid"})
	if err == nil {
		t.Error("Info command should fail with invalid ID")
	}
	
	// Test info command with non-existent ID
	result, err = manager.ProcessCommand("info", []string{"999"})
	if err != nil {
		t.Errorf("Info command failed: %v", err)
	}
	if !contains(result, "not found") {
		t.Error("Info command should indicate appearance not found")
	}
	
	// Test search with no results
	result, err = manager.ProcessCommand("search", []string{"nonexistent"})
	if err != nil {
		t.Errorf("Search command failed: %v", err)
	}
	if !contains(result, "No appearances found") {
		t.Error("Search command should indicate no results")
	}
}

func TestManagerValidateAllAppearances(t *testing.T) {
	manager := NewManager(nil, nil)
	
	// Add valid appearance
	manager.AddAppearance(NewAppearance(100, "Valid", 1000))
	
	issues := manager.ValidateAllAppearances()
	if len(issues) != 0 {
		t.Errorf("ValidateAllAppearances returned issues for valid data: %v", issues)
	}
}

func TestEntityAppearanceAdapterGetAppearanceName(t *testing.T) {
	entity := &MockEntity{id: 1, name: "TestEntity"}
	manager := NewManager(nil, nil)
	adapter := NewEntityAppearanceAdapter(entity, manager, nil)
	
	// Test with no appearance
	if name := adapter.GetAppearanceName(); name != "" {
		t.Errorf("GetAppearanceName() with no appearance = %v, want empty string", name)
	}
	
	// Test with appearance
	app := NewAppearance(100, "TestName", 1000)
	manager.AddAppearance(app)
	adapter.SetAppearanceID(100)
	
	if name := adapter.GetAppearanceName(); name != "TestName" {
		t.Errorf("GetAppearanceName() = %v, want TestName", name)
	}
}