package factions

import (
	"os"
	"testing"

	"eq2emu/internal/database"
)

func TestNewFaction(t *testing.T) {
	faction := NewFaction(1, "Test Faction", "TestType", "A test faction")
	if faction == nil {
		t.Fatal("NewFaction returned nil")
	}

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

	if faction.Name != "Test Faction" {
		t.Errorf("Expected name 'Test Faction', got '%s'", faction.Name)
	}

	if faction.Type != "TestType" {
		t.Errorf("Expected type 'TestType', got '%s'", faction.Type)
	}

	if faction.Description != "A test faction" {
		t.Errorf("Expected description 'A test faction', got '%s'", faction.Description)
	}
}

func TestMasterFactionList(t *testing.T) {
	mfl := NewMasterFactionList()
	if mfl == nil {
		t.Fatal("NewMasterFactionList returned nil")
	}

	// Test adding faction
	faction := NewFaction(100, "Test Faction", "Test", "Test faction")
	err := mfl.AddFaction(faction)
	if err != nil {
		t.Fatalf("Failed to add faction: %v", err)
	}

	// Test getting faction
	retrieved := mfl.GetFaction(100)
	if retrieved == nil {
		t.Error("Failed to retrieve added faction")
	} else if retrieved.Name != "Test Faction" {
		t.Errorf("Expected name 'Test Faction', got '%s'", retrieved.Name)
	}

	// Test getting all factions
	factions := mfl.GetAllFactions()
	if len(factions) == 0 {
		t.Error("Expected at least one faction")
	}
}

func TestPlayerFaction(t *testing.T) {
	pf := NewPlayerFaction(123)
	if pf == nil {
		t.Fatal("NewPlayerFaction returned nil")
	}

	// Test setting faction value
	pf.SetFactionValue(1, 1000)
	value := pf.GetFactionValue(1)
	if value != 1000 {
		t.Errorf("Expected faction value 1000, got %d", value)
	}

	// Test faction modification
	pf.IncreaseFaction(1, 500)
	value = pf.GetFactionValue(1)
	if value != 1500 {
		t.Errorf("Expected faction value 1500 after increase, got %d", value)
	}

	pf.DecreaseFaction(1, 200)
	value = pf.GetFactionValue(1)
	if value != 1300 {
		t.Errorf("Expected faction value 1300 after decrease, got %d", value)
	}

	// Test consideration calculation
	consideration := pf.GetFactionConsideration(1)
	if consideration < -4 || consideration > 4 {
		t.Errorf("Consideration %d is out of valid range [-4, 4]", consideration)
	}
}

func TestFactionRelations(t *testing.T) {
	mfl := NewMasterFactionList()

	// Add test factions
	faction1 := NewFaction(1, "Faction 1", "Test", "Test faction 1")
	faction2 := NewFaction(2, "Faction 2", "Test", "Test faction 2")
	faction3 := NewFaction(3, "Faction 3", "Test", "Test faction 3")

	mfl.AddFaction(faction1)
	mfl.AddFaction(faction2)
	mfl.AddFaction(faction3)

	// Test hostile relations
	err := mfl.AddHostileFaction(1, 2)
	if err != nil {
		t.Fatalf("Failed to add hostile faction: %v", err)
	}

	isHostile := mfl.IsHostile(1, 2)
	if !isHostile {
		t.Error("Expected faction 2 to be hostile to faction 1")
	}

	// Test friendly relations
	err = mfl.AddFriendlyFaction(1, 3)
	if err != nil {
		t.Fatalf("Failed to add friendly faction: %v", err)
	}

	isFriendly := mfl.IsFriendly(1, 3)
	if !isFriendly {
		t.Error("Expected faction 3 to be friendly to faction 1")
	}

	// Test removing relations
	err = mfl.RemoveHostileFaction(1, 2)
	if err != nil {
		t.Fatalf("Failed to remove hostile faction: %v", err)
	}

	isHostile = mfl.IsHostile(1, 2)
	if isHostile {
		t.Error("Expected faction 2 to no longer be hostile to faction 1")
	}
}

func TestFactionDatabaseIntegration(t *testing.T) {
	// Create temporary database
	tempFile := "test_factions.db"
	defer os.Remove(tempFile)

	db, err := database.Open(tempFile)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	// Create database adapter
	dbAdapter := NewDatabaseAdapter(db)

	// Test saving faction
	faction := NewFaction(100, "Test Faction", "Test", "A test faction")
	err = dbAdapter.SaveFaction(faction)
	if err != nil {
		t.Fatalf("Failed to save faction: %v", err)
	}

	// Test loading factions
	factions, err := dbAdapter.LoadAllFactions()
	if err != nil {
		t.Fatalf("Failed to load factions: %v", err)
	}

	if len(factions) != 1 {
		t.Errorf("Expected 1 faction, got %d", len(factions))
	}

	if factions[0].Name != "Test Faction" {
		t.Errorf("Expected name 'Test Faction', got '%s'", factions[0].Name)
	}

	// Test faction relations
	relation := &FactionRelation{
		FactionID:        100,
		HostileFactionID: 200,
	}

	err = dbAdapter.SaveFactionRelation(relation)
	if err != nil {
		t.Fatalf("Failed to save faction relation: %v", err)
	}

	hostileRelations, err := dbAdapter.LoadHostileFactionRelations()
	if err != nil {
		t.Fatalf("Failed to load hostile relations: %v", err)
	}

	if len(hostileRelations) != 1 {
		t.Errorf("Expected 1 hostile relation, got %d", len(hostileRelations))
	}

	// Test player faction values
	playerFactions := map[int32]int32{
		100: 1000,
		200: -500,
	}

	err = dbAdapter.SaveAllPlayerFactions(123, playerFactions)
	if err != nil {
		t.Fatalf("Failed to save player factions: %v", err)
	}

	loadedFactions, err := dbAdapter.LoadPlayerFactions(123)
	if err != nil {
		t.Fatalf("Failed to load player factions: %v", err)
	}

	if len(loadedFactions) != 2 {
		t.Errorf("Expected 2 player factions, got %d", len(loadedFactions))
	}

	if loadedFactions[100] != 1000 {
		t.Errorf("Expected faction 100 value 1000, got %d", loadedFactions[100])
	}

	if loadedFactions[200] != -500 {
		t.Errorf("Expected faction 200 value -500, got %d", loadedFactions[200])
	}
}

func TestFactionValidation(t *testing.T) {
	mfl := NewMasterFactionList()

	// Test nil faction
	err := mfl.AddFaction(nil)
	if err == nil {
		t.Error("Expected error when adding nil faction")
	}

	// Test invalid faction ID
	faction := NewFaction(0, "Invalid", "Test", "Invalid faction")
	err = mfl.AddFaction(faction)
	if err == nil {
		t.Error("Expected error when adding faction with ID 0")
	}

	// Test empty name
	faction = NewFaction(1, "", "Test", "Empty name faction")
	err = mfl.AddFaction(faction)
	if err == nil {
		t.Error("Expected error when adding faction with empty name")
	}
}