package race_types

import (
	"database/sql"
	"fmt"
	"log"
	"strings"
	"sync"
)

// Manager provides high-level race type management
type Manager struct {
	masterList *MasterRaceTypeList
	dbLoader   *DatabaseLoader
	db         *sql.DB
	
	// Thread safety for manager operations
	mutex sync.RWMutex
}

// NewManager creates a new race type manager
func NewManager(db *sql.DB) *Manager {
	return &Manager{
		masterList: NewMasterRaceTypeList(),
		dbLoader:   NewDatabaseLoader(db),
		db:         db,
	}
}

// Initialize sets up the race type system
func (m *Manager) Initialize() error {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	
	// Create table if needed
	if err := m.dbLoader.CreateRaceTypesTable(); err != nil {
		return fmt.Errorf("failed to create race types table: %w", err)
	}
	
	// Load race types from database
	if err := m.dbLoader.LoadRaceTypes(m.masterList); err != nil {
		return fmt.Errorf("failed to load race types: %w", err)
	}
	
	log.Printf("Race type system initialized with %d race types", m.masterList.Count())
	return nil
}

// GetRaceType returns the race type ID for a given model ID
func (m *Manager) GetRaceType(modelID int16) int16 {
	return m.masterList.GetRaceType(modelID)
}

// GetRaceTypeInfo returns complete race type information for a model ID
func (m *Manager) GetRaceTypeInfo(modelID int16) *RaceType {
	return m.masterList.GetRaceTypeByModelID(modelID)
}

// GetRaceBaseType returns the base category type for a model ID
func (m *Manager) GetRaceBaseType(modelID int16) int16 {
	return m.masterList.GetRaceBaseType(modelID)
}

// GetRaceTypeCategory returns the category name for a model ID
func (m *Manager) GetRaceTypeCategory(modelID int16) string {
	return m.masterList.GetRaceTypeCategory(modelID)
}

// GetRaceTypeSubCategory returns the subcategory name for a model ID
func (m *Manager) GetRaceTypeSubCategory(modelID int16) string {
	return m.masterList.GetRaceTypeSubCategory(modelID)
}

// GetRaceTypeModelName returns the model name for a model ID
func (m *Manager) GetRaceTypeModelName(modelID int16) string {
	return m.masterList.GetRaceTypeModelName(modelID)
}

// AddRaceType adds a new race type to the system
func (m *Manager) AddRaceType(modelID int16, raceTypeID int16, category, subcategory, modelName string) error {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	
	// Validate input
	if raceTypeID <= 0 {
		return fmt.Errorf("invalid race type ID: %d", raceTypeID)
	}
	
	// Add to master list
	if !m.masterList.AddRaceType(modelID, raceTypeID, category, subcategory, modelName, false) {
		return fmt.Errorf("race type already exists for model ID %d", modelID)
	}
	
	// Save to database
	raceType := &RaceType{
		RaceTypeID:  raceTypeID,
		Category:    category,
		Subcategory: subcategory,
		ModelName:   modelName,
	}
	
	if err := m.dbLoader.SaveRaceType(modelID, raceType); err != nil {
		// Rollback from master list
		m.masterList.Clear() // This is not ideal but ensures consistency
		m.dbLoader.LoadRaceTypes(m.masterList)
		return fmt.Errorf("failed to save race type: %w", err)
	}
	
	return nil
}

// UpdateRaceType updates an existing race type
func (m *Manager) UpdateRaceType(modelID int16, raceTypeID int16, category, subcategory, modelName string) error {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	
	// Check if exists
	if m.masterList.GetRaceType(modelID) == 0 {
		return fmt.Errorf("race type not found for model ID %d", modelID)
	}
	
	// Update in master list
	if !m.masterList.AddRaceType(modelID, raceTypeID, category, subcategory, modelName, true) {
		return fmt.Errorf("failed to update race type in master list")
	}
	
	// Save to database
	raceType := &RaceType{
		RaceTypeID:  raceTypeID,
		Category:    category,
		Subcategory: subcategory,
		ModelName:   modelName,
	}
	
	if err := m.dbLoader.SaveRaceType(modelID, raceType); err != nil {
		// Reload from database to ensure consistency
		m.masterList.Clear()
		m.dbLoader.LoadRaceTypes(m.masterList)
		return fmt.Errorf("failed to update race type in database: %w", err)
	}
	
	return nil
}

// RemoveRaceType removes a race type from the system
func (m *Manager) RemoveRaceType(modelID int16) error {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	
	// Check if exists
	if m.masterList.GetRaceType(modelID) == 0 {
		return fmt.Errorf("race type not found for model ID %d", modelID)
	}
	
	// Delete from database first
	if err := m.dbLoader.DeleteRaceType(modelID); err != nil {
		return fmt.Errorf("failed to delete race type from database: %w", err)
	}
	
	// Reload master list to ensure consistency
	m.masterList.Clear()
	m.dbLoader.LoadRaceTypes(m.masterList)
	
	return nil
}

// GetRaceTypesByCategory returns all race types for a given category
func (m *Manager) GetRaceTypesByCategory(category string) map[int16]*RaceType {
	return m.masterList.GetRaceTypesByCategory(category)
}

// GetRaceTypesBySubcategory returns all race types for a given subcategory
func (m *Manager) GetRaceTypesBySubcategory(subcategory string) map[int16]*RaceType {
	return m.masterList.GetRaceTypesBySubcategory(subcategory)
}

// GetAllRaceTypes returns all race types in the system
func (m *Manager) GetAllRaceTypes() map[int16]*RaceType {
	return m.masterList.GetAllRaceTypes()
}

// GetStatistics returns race type usage statistics
func (m *Manager) GetStatistics() *Statistics {
	return m.masterList.GetStatistics()
}

// IsCreatureType checks if a model ID represents a specific creature type
func (m *Manager) IsCreatureType(modelID int16, creatureType int16) bool {
	raceType := m.masterList.GetRaceType(modelID)
	if raceType == 0 {
		return false
	}
	
	// Check exact match
	if raceType == creatureType {
		return true
	}
	
	// Check if it's in the same category
	return GetRaceTypeCategory(raceType) == GetRaceTypeCategory(creatureType)
}

// IsUndead checks if a model ID represents an undead creature
func (m *Manager) IsUndead(modelID int16) bool {
	baseType := m.masterList.GetRaceBaseType(modelID)
	return baseType == Undead
}

// IsDragonkind checks if a model ID represents a dragon-type creature
func (m *Manager) IsDragonkind(modelID int16) bool {
	baseType := m.masterList.GetRaceBaseType(modelID)
	return baseType == Dragonkind
}

// IsMagical checks if a model ID represents a magical creature
func (m *Manager) IsMagical(modelID int16) bool {
	baseType := m.masterList.GetRaceBaseType(modelID)
	return baseType == Magical || baseType == Mechanimagical
}

// IsNatural checks if a model ID represents a natural creature
func (m *Manager) IsNatural(modelID int16) bool {
	baseType := m.masterList.GetRaceBaseType(modelID)
	return baseType == Natural
}

// IsSentient checks if a model ID represents a sentient being
func (m *Manager) IsSentient(modelID int16) bool {
	baseType := m.masterList.GetRaceBaseType(modelID)
	return baseType == Sentient
}

// GetCategoryDescription returns a human-readable description of a category
func (m *Manager) GetCategoryDescription(category string) string {
	switch strings.ToUpper(category) {
	case CategoryDragonkind:
		return "Dragons and dragon-like creatures"
	case CategoryFay:
		return "Fae and fairy-type creatures"
	case CategoryMagical:
		return "Magical constructs and animated beings"
	case CategoryMechanimagical:
		return "Mechanical and magical hybrid creatures"
	case CategoryNatural:
		return "Natural animals and beasts"
	case CategoryPlanar:
		return "Planar and elemental beings"
	case CategoryPlant:
		return "Plant-based creatures"
	case CategorySentient:
		return "Sentient humanoid races"
	case CategoryUndead:
		return "Undead creatures"
	case CategoryWere:
		return "Lycanthropes and shape-shifters"
	default:
		return "Unknown category"
	}
}

// ProcessCommand handles race type related commands
func (m *Manager) ProcessCommand(args []string) string {
	if len(args) == 0 {
		return "Usage: racetype <stats|list|info|category> [args...]"
	}
	
	switch strings.ToLower(args[0]) {
	case "stats":
		stats := m.GetStatistics()
		result := fmt.Sprintf("Race Type Statistics:\n")
		result += fmt.Sprintf("Total Race Types: %d\n", stats.TotalRaceTypes)
		result += fmt.Sprintf("Queries by Model: %d\n", stats.QueriesByModel)
		result += fmt.Sprintf("Queries by Category: %d\n", stats.QueriesByCategory)
		result += fmt.Sprintf("\nCategory Counts:\n")
		for cat, count := range stats.CategoryCounts {
			result += fmt.Sprintf("  %s: %d\n", cat, count)
		}
		return result
		
	case "list":
		if len(args) < 2 {
			return "Usage: racetype list <category>"
		}
		raceTypes := m.GetRaceTypesByCategory(args[1])
		if len(raceTypes) == 0 {
			return fmt.Sprintf("No race types found for category: %s", args[1])
		}
		result := fmt.Sprintf("Race types in category %s:\n", args[1])
		for modelID, rt := range raceTypes {
			result += fmt.Sprintf("  Model %d: %s (%d)\n", modelID, rt.ModelName, rt.RaceTypeID)
		}
		return result
		
	case "info":
		if len(args) < 2 {
			return "Usage: racetype info <model_id>"
		}
		var modelID int16
		if _, err := fmt.Sscanf(args[1], "%d", &modelID); err != nil {
			return "Invalid model ID"
		}
		rt := m.GetRaceTypeInfo(modelID)
		if rt == nil {
			return fmt.Sprintf("No race type found for model ID: %d", modelID)
		}
		return fmt.Sprintf("Model %d:\n  Race Type: %d\n  Category: %s\n  Subcategory: %s\n  Model Name: %s\n  Base Type: %s (%d)",
			modelID, rt.RaceTypeID, rt.Category, rt.Subcategory, rt.ModelName,
			GetCategoryName(GetRaceTypeCategory(rt.RaceTypeID)), GetRaceTypeCategory(rt.RaceTypeID))
		
	case "category":
		result := "Race Type Categories:\n"
		for _, cat := range []string{CategoryDragonkind, CategoryFay, CategoryMagical, CategoryMechanimagical,
			CategoryNatural, CategoryPlanar, CategoryPlant, CategorySentient, CategoryUndead, CategoryWere} {
			result += fmt.Sprintf("  %s: %s\n", cat, m.GetCategoryDescription(cat))
		}
		return result
		
	default:
		return "Unknown racetype command. Use: stats, list, info, or category"
	}
}