package gsmaster

import (
	"bamort/database"
	"bamort/models"
	"fmt"
	"log"
)

// EnhanceLearningDataWithExistingTables ergänzt die neuen Lernkosten-Tabellen mit Daten aus den bereits vorhandenen Tabellen
func EnhanceLearningDataWithExistingTables() error {
	log.Println("Starting enhancement of learning data with existing tables...")

	// 1. Fertigkeiten aus der bestehenden Skill-Tabelle in SkillCategoryDifficulty einbinden
	if err := linkExistingSkillsToCategories(); err != nil {
		return fmt.Errorf("failed to link existing skills to categories: %w", err)
	}

	// 2. Zauber aus der bestehenden Spell-Tabelle mit Zauberschulen verknüpfen
	if err := linkExistingSpellsToSchools(); err != nil {
		return fmt.Errorf("failed to link existing spells to schools: %w", err)
	}

	// 3. Charaktere aus der bestehenden Character-Tabelle mit Charakterklassen verknüpfen
	if err := linkExistingCharactersToClasses(); err != nil {
		return fmt.Errorf("failed to link existing characters to classes: %w", err)
	}

	// 4. Fehlende Fertigkeiten aus der bestehenden Tabelle zu den Kategorien hinzufügen
	if err := addMissingSkillsToCategories(); err != nil {
		return fmt.Errorf("failed to add missing skills to categories: %w", err)
	}

	// 5. Verknüpfe bestehende Fertigkeiten und Zauber mit Standardquellen
	if err := linkExistingItemsToDefaultSources(); err != nil {
		return fmt.Errorf("failed to link existing items to default sources: %w", err)
	}

	log.Println("Enhancement completed successfully!")
	return nil
}

// linkExistingSkillsToCategories verknüpft vorhandene Fertigkeiten mit Kategorien und Schwierigkeiten
func linkExistingSkillsToCategories() error {
	var skills []models.Skill
	if err := database.DB.Find(&skills).Error; err != nil {
		return fmt.Errorf("failed to fetch existing skills: %w", err)
	}

	for _, skill := range skills {
		// Prüfe ob bereits Verknüpfungen für diese Fertigkeit existieren
		var existingLinks []models.SkillCategoryDifficulty
		if err := database.DB.Where("skill_id = ?", skill.ID).Find(&existingLinks).Error; err != nil {
			return fmt.Errorf("failed to check existing links for skill %s: %w", skill.Name, err)
		}

		// Wenn bereits Verknüpfungen existieren, überspringe
		if len(existingLinks) > 0 {
			log.Printf("Skill %s already has category links, skipping", skill.Name)
			continue
		}

		// Suche nach der Fertigkeit in den lerningCostsData
		category, difficulty := findSkillInLearningData(skill.Name)
		if category == "" {
			log.Printf("Warning: Skill %s not found in learning data, using default category", skill.Name)
			// Verwende eine Standard-Kategorie basierend auf der bestehenden Category
			if skill.Category != "" {
				category = skill.Category
				difficulty = skill.Difficulty
				if difficulty == "" {
					difficulty = "normal"
				}
			} else {
				category = "Unbekannt"
				difficulty = "normal"
			}
		}

		// Erstelle Verknüpfung
		if err := createSkillCategoryLink(skill.ID, skill.Name, category, difficulty); err != nil {
			log.Printf("Warning: Failed to create category link for skill %s: %v", skill.Name, err)
		}
	}

	return nil
}

// linkExistingSpellsToSchools verknüpft vorhandene Zauber mit Zauberschulen
func linkExistingSpellsToSchools() error {
	var spells []models.Spell
	if err := database.DB.Find(&spells).Error; err != nil {
		return fmt.Errorf("failed to fetch existing spells: %w", err)
	}

	for _, spell := range spells {
		// Prüfe ob die Zauberschule existiert
		if spell.Category == "" {
			log.Printf("Warning: Spell %s has no category, skipping", spell.Name)
			continue
		}

		var spellSchool models.SpellSchool
		if err := spellSchool.FirstByName(spell.Category); err != nil {
			// Zauberschule existiert nicht, erstelle sie
			newSchool := models.SpellSchool{
				Name:       spell.Category,
				GameSystem: "midgard",
			}
			if err := newSchool.Create(); err != nil {
				log.Printf("Warning: Failed to create spell school %s for spell %s: %v", spell.Category, spell.Name, err)
				continue
			}
			log.Printf("Created new spell school: %s", spell.Category)
		}

		log.Printf("Spell %s linked to school %s", spell.Name, spell.Category)
	}

	return nil
}

// linkExistingCharactersToClasses verknüpft vorhandene Charaktere mit Charakterklassen
func linkExistingCharactersToClasses() error {
	var characters []models.Char
	if err := database.DB.Find(&characters).Error; err != nil {
		return fmt.Errorf("failed to fetch existing characters: %w", err)
	}

	// Lade alle verfügbaren Charakterklassen aus der Datenbank
	var characterClasses []models.CharacterClass
	if err := database.DB.Find(&characterClasses).Error; err != nil {
		return fmt.Errorf("failed to fetch character classes from database: %w", err)
	}

	// Erstelle Mapping-Maps für beide Richtungen (Code->Klasse und Name->Klasse)
	codeToClass := make(map[string]models.CharacterClass)
	nameToClass := make(map[string]models.CharacterClass)

	for _, class := range characterClasses {
		codeToClass[class.Code] = class
		nameToClass[class.Name] = class
	}

	for _, character := range characters {
		var characterClass models.CharacterClass
		var found bool

		// Versuche zuerst über Code zu finden (falls character.Typ bereits ein Code ist)
		if class, exists := codeToClass[character.Typ]; exists {
			characterClass = class
			found = true
		} else if class, exists := nameToClass[character.Typ]; exists {
			// Wenn nicht über Code gefunden, versuche über vollständigen Namen
			characterClass = class
			found = true
		}

		if !found {
			log.Printf("Warning: Character class '%s' not found in database for character %s", character.Typ, character.Name)
			continue
		}

		log.Printf("Character %s linked to class %s (%s)", character.Name, characterClass.Code, characterClass.Name)
	}

	return nil
}

// addMissingSkillsToCategories fügt Fertigkeiten hinzu, die in der Datenbank existieren aber nicht in den Lernkosten-Daten
func addMissingSkillsToCategories() error {
	var skills []models.Skill
	if err := database.DB.Find(&skills).Error; err != nil {
		return fmt.Errorf("failed to fetch skills: %w", err)
	}

	for _, skill := range skills {
		// Prüfe ob bereits Kategorien-Verknüpfungen existieren
		var count int64
		if err := database.DB.Model(&models.SkillCategoryDifficulty{}).Where("skill_id = ?", skill.ID).Count(&count).Error; err != nil {
			return fmt.Errorf("failed to count category links for skill %s: %w", skill.Name, err)
		}

		if count == 0 {
			// Keine Kategorien-Verknüpfung gefunden, erstelle eine Standard-Verknüpfung
			category := "Unbekannt"
			difficulty := "normal"

			// Verwende bestehende Kategorie falls vorhanden
			if skill.Category != "" {
				category = skill.Category
			}
			if skill.Difficulty != "" {
				difficulty = skill.Difficulty
			}

			if err := createSkillCategoryLink(skill.ID, skill.Name, category, difficulty); err != nil {
				log.Printf("Warning: Failed to create default category link for skill %s: %v", skill.Name, err)
			} else {
				log.Printf("Created default category link for skill %s: %s - %s", skill.Name, category, difficulty)
			}
		}
	}

	return nil
}

// Hilfsfunktionen

// findSkillInLearningData sucht eine Fertigkeit in den learningCostsData und gibt Kategorie und Schwierigkeit zurück
func findSkillInLearningData(skillName string) (string, string) {
	for categoryName, difficulties := range learningCostsData.ImprovementCost {
		for difficultyName, data := range difficulties {
			for _, skill := range data.Skills {
				if skill == skillName {
					return categoryName, difficultyName
				}
			}
		}
	}
	return "", ""
}

// createSkillCategoryLink erstellt eine Verknüpfung zwischen Fertigkeit, Kategorie und Schwierigkeit
func createSkillCategoryLink(skillID uint, skillName, categoryName, difficultyName string) error {
	// Hole oder erstelle die Kategorie
	var skillCategory models.SkillCategory
	if err := skillCategory.FirstByName(categoryName); err != nil {
		// Kategorie existiert nicht, erstelle sie
		skillCategory = models.SkillCategory{
			Name:       categoryName,
			GameSystem: "midgard",
		}
		if err := skillCategory.Create(); err != nil {
			return fmt.Errorf("failed to create skill category %s: %w", categoryName, err)
		}
		log.Printf("Created new skill category: %s", categoryName)
	}

	// Hole oder erstelle die Schwierigkeit
	var skillDifficulty models.SkillDifficulty
	if err := skillDifficulty.FirstByName(difficultyName); err != nil {
		// Schwierigkeit existiert nicht, erstelle sie
		skillDifficulty = models.SkillDifficulty{
			Name:       difficultyName,
			GameSystem: "midgard",
		}
		if err := skillDifficulty.Create(); err != nil {
			return fmt.Errorf("failed to create skill difficulty %s: %w", difficultyName, err)
		}
		log.Printf("Created new skill difficulty: %s", difficultyName)
	}

	// Bestimme LearnCost basierend auf den learningCostsData
	learnCost := 2 // Standard-Wert
	if categoryData, exists := learningCostsData.ImprovementCost[categoryName]; exists {
		if difficultyData, exists := categoryData[difficultyName]; exists {
			learnCost = difficultyData.LearnCost
		}
	}

	// Erstelle die Verknüpfung
	categoryDifficulty := models.SkillCategoryDifficulty{
		SkillID:           skillID,
		SkillCategoryID:   skillCategory.ID,
		SkillDifficultyID: skillDifficulty.ID,
		LearnCost:         learnCost,
	}

	if err := categoryDifficulty.Create(); err != nil {
		return fmt.Errorf("failed to create skill category difficulty link: %w", err)
	}

	log.Printf("Created skill category link: %s - %s - %s (Learn: %d LE)", skillName, categoryName, difficultyName, learnCost)
	return nil
}

// CreateLearningCostsTables erstellt alle neuen Tabellen für das Lernkosten-System
func CreateLearningCostsTables() error {
	log.Println("Creating learning costs tables...")

	// Liste aller neuen Modelle
	models := []interface{}{
		&models.Source{},
		&models.CharacterClass{},
		&models.SkillCategory{},
		&models.SkillDifficulty{},
		&models.SpellSchool{},
		&models.ClassCategoryEPCost{},
		&models.ClassSpellSchoolEPCost{},
		&models.SpellLevelLECost{},
		&models.SkillCategoryDifficulty{},
		&models.SkillImprovementCost{},
	}

	// Erstelle oder migriere alle Tabellen
	for _, model := range models {
		if err := database.DB.AutoMigrate(model); err != nil {
			return fmt.Errorf("failed to migrate table for model %T: %w", model, err)
		}
	}

	log.Println("Learning costs tables created successfully!")
	return nil
}

// linkExistingItemsToDefaultSources verknüpft bestehende Fertigkeiten und Zauber mit Standardquellen
func linkExistingItemsToDefaultSources() error {
	// Hole die Standardquellen
	var kodSource models.Source
	if err := kodSource.FirstByCode("KOD"); err != nil {
		return fmt.Errorf("KOD source not found: %w", err)
	}

	var arkSource models.Source
	if err := arkSource.FirstByCode("ARK"); err != nil {
		return fmt.Errorf("ARK source not found: %w", err)
	}

	// Verknüpfe Fertigkeiten mit KOD (falls noch keine Quelle zugeordnet)
	var skills []models.Skill
	if err := database.DB.Where("source_id IS NULL OR source_id = 0").Find(&skills).Error; err != nil {
		return fmt.Errorf("failed to fetch skills without source: %w", err)
	}

	for _, skill := range skills {
		if err := database.DB.Model(&skill).Update("source_id", kodSource.ID).Error; err != nil {
			log.Printf("Warning: Failed to update source for skill %s: %v", skill.Name, err)
		} else {
			log.Printf("Linked skill %s to source KOD", skill.Name)
		}
	}

	// Verknüpfe Zauber mit ARK (falls noch keine Quelle zugeordnet)
	var spells []models.Spell
	if err := database.DB.Where("source_id IS NULL OR source_id = 0").Find(&spells).Error; err != nil {
		return fmt.Errorf("failed to fetch spells without source: %w", err)
	}

	for _, spell := range spells {
		if err := database.DB.Model(&spell).Update("source_id", arkSource.ID).Error; err != nil {
			log.Printf("Warning: Failed to update source for spell %s: %v", spell.Name, err)
		} else {
			log.Printf("Linked spell %s to source ARK", spell.Name)
		}
	}

	return nil
}
