package tradeskills

import (
	"testing"
	"time"
)

func TestTradeskillEvent(t *testing.T) {
	event := &TradeskillEvent{
		Name:              "Test Event",
		Icon:              1234,
		Technique:         TechniqueSkillAlchemy,
		SuccessProgress:   25,
		SuccessDurability: 0,
		SuccessHP:         0,
		SuccessPower:      -10,
		SuccessSpellID:    0,
		SuccessItemID:     0,
		FailProgress:      -10,
		FailDurability:    -25,
		FailHP:            0,
		FailPower:         0,
	}

	// Test Copy method
	copied := event.Copy()
	if copied == nil {
		t.Fatal("Copy returned nil")
	}

	if copied.Name != event.Name {
		t.Errorf("Expected name %s, got %s", event.Name, copied.Name)
	}

	if copied.Technique != event.Technique {
		t.Errorf("Expected technique %d, got %d", event.Technique, copied.Technique)
	}

	// Test Copy with nil
	var nilEvent *TradeskillEvent
	copiedNil := nilEvent.Copy()
	if copiedNil != nil {
		t.Error("Copy of nil should return nil")
	}
}

func TestTradeskillManager(t *testing.T) {
	manager := NewTradeskillManager()
	if manager == nil {
		t.Fatal("NewTradeskillManager returned nil")
	}

	// Test initial state
	if manager.IsClientCrafting(12345) {
		t.Error("New manager should not have any active crafting sessions")
	}

	// Test begin crafting
	request := CraftingRequest{
		PlayerID:     12345,
		RecipeID:     67890,
		TableSpawnID: 11111,
		Components: []ComponentUsage{
			{ItemUniqueID: 22222, Quantity: 2},
			{ItemUniqueID: 33333, Quantity: 1},
		},
		Quantity: 1,
	}

	err := manager.BeginCrafting(request)
	if err != nil {
		t.Fatalf("BeginCrafting failed: %v", err)
	}

	// Test client is now crafting
	if !manager.IsClientCrafting(12345) {
		t.Error("Client should be crafting after BeginCrafting")
	}

	// Test get tradeskill
	tradeskill := manager.GetTradeskill(12345)
	if tradeskill == nil {
		t.Fatal("GetTradeskill returned nil")
	}

	if tradeskill.PlayerID != 12345 {
		t.Errorf("Expected player ID 12345, got %d", tradeskill.PlayerID)
	}

	if tradeskill.RecipeID != 67890 {
		t.Errorf("Expected recipe ID 67890, got %d", tradeskill.RecipeID)
	}

	// Test stop crafting
	err = manager.StopCrafting(12345)
	if err != nil {
		t.Fatalf("StopCrafting failed: %v", err)
	}

	// Test client is no longer crafting
	if manager.IsClientCrafting(12345) {
		t.Error("Client should not be crafting after StopCrafting")
	}
}

func TestTradeskillSession(t *testing.T) {
	now := time.Now()
	tradeskill := &Tradeskill{
		PlayerID:          12345,
		TableSpawnID:      11111,
		RecipeID:          67890,
		CurrentProgress:   500,
		CurrentDurability: 800,
		NextUpdateTime:    now.Add(time.Second),
		UsedComponents: []ComponentUsage{
			{ItemUniqueID: 22222, Quantity: 2},
		},
		StartTime:  now,
		LastUpdate: now,
	}

	// Test completion check
	if tradeskill.IsComplete() {
		t.Error("Tradeskill with 500 progress should not be complete")
	}

	tradeskill.CurrentProgress = MaxProgress
	if !tradeskill.IsComplete() {
		t.Error("Tradeskill with max progress should be complete")
	}

	// Test failure check
	if tradeskill.IsFailed() {
		t.Error("Tradeskill with 800 durability should not be failed")
	}

	tradeskill.CurrentDurability = MinDurability
	if !tradeskill.IsFailed() {
		t.Error("Tradeskill with min durability should be failed")
	}

	// Test update check
	tradeskill.NextUpdateTime = now.Add(-time.Second) // Past time
	if !tradeskill.NeedsUpdate() {
		t.Error("Tradeskill with past update time should need update")
	}

	// Test reset
	tradeskill.Reset()
	if tradeskill.CurrentProgress != MinProgress {
		t.Errorf("Expected progress %d after reset, got %d", MinProgress, tradeskill.CurrentProgress)
	}

	if tradeskill.CurrentDurability != MaxDurability {
		t.Errorf("Expected durability %d after reset, got %d", MaxDurability, tradeskill.CurrentDurability)
	}
}

func TestMasterTradeskillEventsList(t *testing.T) {
	eventsList := NewMasterTradeskillEventsList()
	if eventsList == nil {
		t.Fatal("NewMasterTradeskillEventsList returned nil")
	}

	// Test initial state
	if eventsList.Size() != 0 {
		t.Error("New events list should be empty")
	}

	// Test add event
	event := &TradeskillEvent{
		Name:      "Test Event",
		Icon:      1234,
		Technique: TechniqueSkillAlchemy,
	}

	eventsList.AddEvent(event)
	if eventsList.Size() != 1 {
		t.Errorf("Expected size 1 after adding event, got %d", eventsList.Size())
	}

	// Test get by technique
	events := eventsList.GetEventByTechnique(TechniqueSkillAlchemy)
	if len(events) != 1 {
		t.Errorf("Expected 1 event for alchemy, got %d", len(events))
	}

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

	// Test get by non-existent technique
	noEvents := eventsList.GetEventByTechnique(TechniqueSkillFletching)
	if len(noEvents) != 0 {
		t.Errorf("Expected 0 events for fletching, got %d", len(noEvents))
	}

	// Test add nil event
	eventsList.AddEvent(nil)
	if eventsList.Size() != 1 {
		t.Error("Adding nil event should not change size")
	}

	// Test clear
	eventsList.Clear()
	if eventsList.Size() != 0 {
		t.Error("Events list should be empty after Clear")
	}
}

func TestValidTechnique(t *testing.T) {
	validTechniques := []uint32{
		TechniqueSkillAlchemy,
		TechniqueSkillTailoring,
		TechniqueSkillFletching,
		TechniqueSkillJewelcrafting,
		TechniqueSkillProvisioning,
		TechniqueSkillScribing,
		TechniqueSkillTransmuting,
		TechniqueSkillArtistry,
		TechniqueSkillCarpentry,
		TechniqueSkillMetalworking,
		TechniqueSkillMetalshaping,
		TechniqueSkillStoneworking,
	}

	for _, technique := range validTechniques {
		if !IsValidTechnique(technique) {
			t.Errorf("Technique %d should be valid", technique)
		}
	}

	// Test invalid technique
	if IsValidTechnique(999999) {
		t.Error("Invalid technique should not be valid")
	}
}

func TestDatabaseOperations(t *testing.T) {
	t.Skip("Database operations require actual database connection - skipping for basic validation")

	// This test would work with a real database connection
	// For now, just test that the interface methods exist and compile

	// Mock database service
	var dbService DatabaseService
	_ = dbService // Ensure interface compiles
}

func TestAnimationMethods(t *testing.T) {
	manager := NewTradeskillManager()

	testCases := []struct {
		technique     uint32
		version       int16
		expectNonZero bool
	}{
		{TechniqueSkillAlchemy, 500, true},
		{TechniqueSkillAlchemy, 1000, true},
		{TechniqueSkillTailoring, 500, true},
		{TechniqueSkillFletching, 1000, true},
		{TechniqueSkillScribing, 500, false}, // No animations for scribing
		{999999, 500, false},                 // Invalid technique
	}

	for _, tc := range testCases {
		successAnim := manager.GetTechniqueSuccessAnim(tc.version, tc.technique)
		failureAnim := manager.GetTechniqueFailureAnim(tc.version, tc.technique)
		idleAnim := manager.GetTechniqueIdleAnim(tc.version, tc.technique)

		if tc.expectNonZero {
			if successAnim == 0 {
				t.Errorf("Expected non-zero success animation for technique %d, version %d", tc.technique, tc.version)
			}
			if failureAnim == 0 {
				t.Errorf("Expected non-zero failure animation for technique %d, version %d", tc.technique, tc.version)
			}
			if idleAnim == 0 {
				t.Errorf("Expected non-zero idle animation for technique %d, version %d", tc.technique, tc.version)
			}
		}
	}

	// Test miss target animations
	missAnim := manager.GetMissTargetAnim(500)
	if missAnim == 0 {
		t.Error("Expected non-zero miss target animation for version 500")
	}

	killMissAnim := manager.GetKillMissTargetAnim(1000)
	if killMissAnim == 0 {
		t.Error("Expected non-zero kill miss target animation for version 1000")
	}
}

func TestConfigurationUpdate(t *testing.T) {
	manager := NewTradeskillManager()

	// Test valid configuration
	err := manager.UpdateConfiguration(1.0, 2.0, 10.0, 87.0, 30.0)
	if err != nil {
		t.Errorf("Valid configuration update failed: %v", err)
	}

	// Test invalid configuration (doesn't add to 100%)
	err = manager.UpdateConfiguration(1.0, 2.0, 10.0, 80.0, 30.0) // Only adds to 93%
	if err != nil {
		t.Errorf("Invalid configuration should not return error, should use defaults: %v", err)
	}
}

func TestPacketHelper(t *testing.T) {
	helper := &PacketHelper{}

	// Test progress stage calculation
	testCases := []struct {
		progress int32
		expected int8
	}{
		{0, 0},
		{300, 0},
		{400, 1},
		{550, 1},
		{600, 2},
		{750, 2},
		{800, 3},
		{950, 3},
		{1000, 4},
		{1100, 4}, // Clamped to max
	}

	for _, tc := range testCases {
		result := helper.CalculateProgressStage(tc.progress)
		if result != tc.expected {
			t.Errorf("Progress %d: expected stage %d, got %d", tc.progress, tc.expected, result)
		}
	}

	// Test mass production quantities
	quantities := helper.GetMassProductionQuantities(3)
	if len(quantities) == 0 {
		t.Error("Should return at least base quantity")
	}

	if quantities[0] != 1 {
		t.Error("First quantity should always be 1")
	}

	// Test component validation
	components := []ComponentUsage{
		{ItemUniqueID: 12345, Quantity: 2},
		{ItemUniqueID: 67890, Quantity: 1},
	}

	err := helper.ValidateRecipeComponents(12345, components)
	if err != nil {
		t.Errorf("Valid components should pass validation: %v", err)
	}

	// Test invalid components
	invalidComponents := []ComponentUsage{
		{ItemUniqueID: 0, Quantity: 2}, // Invalid unique ID
	}

	err = helper.ValidateRecipeComponents(12345, invalidComponents)
	if err == nil {
		t.Error("Invalid components should fail validation")
	}

	// Test packet type calculation
	packetType := helper.CalculateItemPacketType(500)
	if packetType == 0 {
		t.Error("Should return non-zero packet type")
	}

	// Test value clamping
	clampedProgress := helper.ClampProgress(-100)
	if clampedProgress != MinProgress {
		t.Errorf("Expected clamped progress %d, got %d", MinProgress, clampedProgress)
	}

	clampedDurability := helper.ClampDurability(2000)
	if clampedDurability != MaxDurability {
		t.Errorf("Expected clamped durability %d, got %d", MaxDurability, clampedDurability)
	}
}

func BenchmarkTradeskillManagerProcess(b *testing.B) {
	manager := NewTradeskillManager()

	// Add some test sessions
	for i := 0; i < 10; i++ {
		request := CraftingRequest{
			PlayerID:     uint32(i + 1),
			RecipeID:     67890,
			TableSpawnID: 11111,
			Components: []ComponentUsage{
				{ItemUniqueID: 22222, Quantity: 2},
			},
			Quantity: 1,
		}
		manager.BeginCrafting(request)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		manager.Process()
	}
}

func BenchmarkEventListAccess(b *testing.B) {
	eventsList := NewMasterTradeskillEventsList()

	// Add test events
	for i := 0; i < 100; i++ {
		event := &TradeskillEvent{
			Name:      "Test Event",
			Icon:      int16(i),
			Technique: TechniqueSkillAlchemy,
		}
		eventsList.AddEvent(event)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		eventsList.GetEventByTechnique(TechniqueSkillAlchemy)
	}
}
