package ground_spawn

import (
	"fmt"
	"time"
)

// NewManager creates a new ground spawn manager
func NewManager(database Database, logger Logger) *Manager {
	return &Manager{
		groundSpawns:    make(map[int32]*GroundSpawn),
		spawnsByZone:    make(map[int32][]*GroundSpawn),
		entriesByID:     make(map[int32][]*GroundSpawnEntry),
		itemsByID:       make(map[int32][]*GroundSpawnEntryItem),
		respawnQueue:    make(map[int32]time.Time),
		database:        database,
		logger:          logger,
		harvestsBySkill: make(map[string]int64),
	}
}

// Initialize loads ground spawn data from database
func (m *Manager) Initialize() error {
	if m.logger != nil {
		m.logger.LogInfo("Initializing ground spawn manager...")
	}

	if m.database == nil {
		if m.logger != nil {
			m.logger.LogWarning("No database provided, starting with empty ground spawn list")
		}
		return nil
	}

	// Load ground spawns from database
	groundSpawns, err := m.database.LoadAllGroundSpawns()
	if err != nil {
		return fmt.Errorf("failed to load ground spawns from database: %w", err)
	}

	m.mutex.Lock()
	defer m.mutex.Unlock()

	for _, gs := range groundSpawns {
		m.groundSpawns[gs.GetID()] = gs

		// Group by zone (placeholder - zone ID would come from spawn location)
		zoneID := int32(1) // TODO: Get actual zone ID from spawn
		m.spawnsByZone[zoneID] = append(m.spawnsByZone[zoneID], gs)

		// Load harvest entries and items
		if err := m.loadGroundSpawnData(gs); err != nil && m.logger != nil {
			m.logger.LogWarning("Failed to load data for ground spawn %d: %v", gs.GetID(), err)
		}
	}

	if m.logger != nil {
		m.logger.LogInfo("Loaded %d ground spawns from database", len(groundSpawns))
	}

	return nil
}

// loadGroundSpawnData loads entries and items for a ground spawn
func (m *Manager) loadGroundSpawnData(gs *GroundSpawn) error {
	groundspawnID := gs.GetGroundSpawnEntryID()

	// Load harvest entries
	entries, err := m.database.LoadGroundSpawnEntries(groundspawnID)
	if err != nil {
		return fmt.Errorf("failed to load entries for groundspawn %d: %w", groundspawnID, err)
	}
	m.entriesByID[groundspawnID] = entries

	// Load harvest items
	items, err := m.database.LoadGroundSpawnItems(groundspawnID)
	if err != nil {
		return fmt.Errorf("failed to load items for groundspawn %d: %w", groundspawnID, err)
	}
	m.itemsByID[groundspawnID] = items

	return nil
}

// CreateGroundSpawn creates a new ground spawn
func (m *Manager) CreateGroundSpawn(config GroundSpawnConfig) *GroundSpawn {
	gs := NewGroundSpawn(config)

	m.mutex.Lock()
	defer m.mutex.Unlock()

	// Generate ID (placeholder implementation)
	newID := int32(len(m.groundSpawns) + 1)
	gs.SetID(newID)

	// Store ground spawn
	m.groundSpawns[newID] = gs

	// Group by zone
	zoneID := int32(1) // TODO: Get actual zone ID from config.Location
	m.spawnsByZone[zoneID] = append(m.spawnsByZone[zoneID], gs)

	if m.logger != nil {
		m.logger.LogInfo("Created ground spawn %d: %s", newID, gs.GetName())
	}

	return gs
}

// GetGroundSpawn returns a ground spawn by ID
func (m *Manager) GetGroundSpawn(id int32) *GroundSpawn {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	return m.groundSpawns[id]
}

// GetGroundSpawnsByZone returns all ground spawns in a zone
func (m *Manager) GetGroundSpawnsByZone(zoneID int32) []*GroundSpawn {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	spawns := m.spawnsByZone[zoneID]
	if spawns == nil {
		return []*GroundSpawn{}
	}

	// Return a copy to prevent external modification
	result := make([]*GroundSpawn, len(spawns))
	copy(result, spawns)

	return result
}

// ProcessHarvest handles harvesting for a player
func (m *Manager) ProcessHarvest(gs *GroundSpawn, player *Player) (*HarvestResult, error) {
	if gs == nil {
		return nil, fmt.Errorf("ground spawn cannot be nil")
	}

	if player == nil {
		return nil, fmt.Errorf("player cannot be nil")
	}

	// Record statistics
	m.mutex.Lock()
	m.totalHarvests++
	skill := gs.GetCollectionSkill()
	m.harvestsBySkill[skill]++
	m.mutex.Unlock()

	// Build harvest context
	context, err := m.buildHarvestContext(gs, player)
	if err != nil {
		return nil, fmt.Errorf("failed to build harvest context: %w", err)
	}

	// Process the harvest
	result, err := gs.ProcessHarvest(context)
	if err != nil {
		return nil, fmt.Errorf("harvest processing failed: %w", err)
	}

	// Update statistics
	if result != nil && result.Success {
		m.mutex.Lock()
		m.successfulHarvests++

		// Count rare items
		for _, item := range result.ItemsAwarded {
			if item.IsRare {
				m.rareItemsHarvested++
			}
		}

		if result.SkillGained {
			m.skillUpsGenerated++
		}
		m.mutex.Unlock()
	}

	// Handle respawn if depleted
	if gs.IsDepleted() {
		m.scheduleRespawn(gs)
	}

	return result, nil
}

// buildHarvestContext creates a harvest context for processing
func (m *Manager) buildHarvestContext(gs *GroundSpawn, player *Player) (*HarvestContext, error) {
	groundspawnID := gs.GetGroundSpawnEntryID()

	m.mutex.RLock()
	entries := m.entriesByID[groundspawnID]
	items := m.itemsByID[groundspawnID]
	m.mutex.RUnlock()

	if entries == nil || len(entries) == 0 {
		return nil, fmt.Errorf("no harvest entries found for groundspawn %d", groundspawnID)
	}

	if items == nil || len(items) == 0 {
		return nil, fmt.Errorf("no harvest items found for groundspawn %d", groundspawnID)
	}

	// Get player skill
	skillName := gs.GetCollectionSkill()
	if skillName == SkillCollecting {
		skillName = SkillGathering // Collections use gathering skill
	}

	playerSkill := player.GetSkillByName(skillName)
	if playerSkill == nil {
		return nil, fmt.Errorf("player lacks required skill: %s", skillName)
	}

	// Calculate total skill (base + bonuses)
	totalSkill := playerSkill.GetCurrentValue()
	// TODO: Add stat bonuses when stat system is integrated

	// Find max skill required
	var maxSkillRequired int16
	for _, entry := range entries {
		if entry.MinSkillLevel > maxSkillRequired {
			maxSkillRequired = entry.MinSkillLevel
		}
	}

	return &HarvestContext{
		Player:             player,
		GroundSpawn:        gs,
		PlayerSkill:        playerSkill,
		TotalSkill:         totalSkill,
		GroundSpawnEntries: entries,
		GroundSpawnItems:   items,
		IsCollection:       gs.GetCollectionSkill() == SkillCollecting,
		MaxSkillRequired:   maxSkillRequired,
	}, nil
}

// scheduleRespawn schedules a ground spawn for respawn
func (m *Manager) scheduleRespawn(gs *GroundSpawn) {
	if gs == nil {
		return
	}

	// TODO: Get respawn timer from configuration or database
	respawnDelay := 5 * time.Minute // Default 5 minutes
	respawnTime := time.Now().Add(respawnDelay)

	m.mutex.Lock()
	m.respawnQueue[gs.GetID()] = respawnTime
	m.mutex.Unlock()

	if m.logger != nil {
		m.logger.LogDebug("Scheduled ground spawn %d for respawn at %v", gs.GetID(), respawnTime)
	}
}

// ProcessRespawns handles ground spawn respawning
func (m *Manager) ProcessRespawns() {
	now := time.Now()
	var toRespawn []int32

	m.mutex.Lock()
	for spawnID, respawnTime := range m.respawnQueue {
		if now.After(respawnTime) {
			toRespawn = append(toRespawn, spawnID)
			delete(m.respawnQueue, spawnID)
		}
	}
	m.mutex.Unlock()

	// Respawn outside of lock
	for _, spawnID := range toRespawn {
		if gs := m.GetGroundSpawn(spawnID); gs != nil {
			gs.Respawn()
			if m.logger != nil {
				m.logger.LogDebug("Ground spawn %d respawned", spawnID)
			}
		}
	}
}

// GetStatistics returns ground spawn system statistics
func (m *Manager) GetStatistics() *HarvestStatistics {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	// Count spawns by zone
	spawnsByZone := make(map[int32]int)
	for zoneID, spawns := range m.spawnsByZone {
		spawnsByZone[zoneID] = len(spawns)
	}

	// Copy harvests by skill
	harvestsBySkill := make(map[string]int64)
	for skill, count := range m.harvestsBySkill {
		harvestsBySkill[skill] = count
	}

	return &HarvestStatistics{
		TotalHarvests:      m.totalHarvests,
		SuccessfulHarvests: m.successfulHarvests,
		RareItemsHarvested: m.rareItemsHarvested,
		SkillUpsGenerated:  m.skillUpsGenerated,
		HarvestsBySkill:    harvestsBySkill,
		ActiveGroundSpawns: len(m.groundSpawns),
		GroundSpawnsByZone: spawnsByZone,
	}
}

// ResetStatistics resets all statistics
func (m *Manager) ResetStatistics() {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	m.totalHarvests = 0
	m.successfulHarvests = 0
	m.rareItemsHarvested = 0
	m.skillUpsGenerated = 0
	m.harvestsBySkill = make(map[string]int64)
}

// AddGroundSpawn adds a ground spawn to the manager
func (m *Manager) AddGroundSpawn(gs *GroundSpawn) error {
	if gs == nil {
		return fmt.Errorf("ground spawn cannot be nil")
	}

	m.mutex.Lock()
	defer m.mutex.Unlock()

	// Check if ID is already used
	if _, exists := m.groundSpawns[gs.GetID()]; exists {
		return fmt.Errorf("ground spawn with ID %d already exists", gs.GetID())
	}

	m.groundSpawns[gs.GetID()] = gs

	// Group by zone (placeholder)
	zoneID := int32(1) // TODO: Get actual zone ID
	m.spawnsByZone[zoneID] = append(m.spawnsByZone[zoneID], gs)

	// Load harvest data if database is available
	if m.database != nil {
		if err := m.loadGroundSpawnData(gs); err != nil && m.logger != nil {
			m.logger.LogWarning("Failed to load data for ground spawn %d: %v", gs.GetID(), err)
		}
	}

	return nil
}

// RemoveGroundSpawn removes a ground spawn from the manager
func (m *Manager) RemoveGroundSpawn(id int32) bool {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	gs, exists := m.groundSpawns[id]
	if !exists {
		return false
	}

	delete(m.groundSpawns, id)
	delete(m.respawnQueue, id)

	// Remove from zone list
	// TODO: Get actual zone ID from ground spawn
	zoneID := int32(1)
	if spawns, exists := m.spawnsByZone[zoneID]; exists {
		for i, spawn := range spawns {
			if spawn.GetID() == id {
				m.spawnsByZone[zoneID] = append(spawns[:i], spawns[i+1:]...)
				break
			}
		}
	}

	// Clean up harvest data
	if gs != nil {
		groundspawnID := gs.GetGroundSpawnEntryID()
		delete(m.entriesByID, groundspawnID)
		delete(m.itemsByID, groundspawnID)
	}

	return true
}

// GetGroundSpawnCount returns the total number of ground spawns
func (m *Manager) GetGroundSpawnCount() int {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	return len(m.groundSpawns)
}

// GetActiveGroundSpawns returns all active (harvestable) ground spawns
func (m *Manager) GetActiveGroundSpawns() []*GroundSpawn {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	var active []*GroundSpawn
	for _, gs := range m.groundSpawns {
		if gs.IsAvailable() {
			active = append(active, gs)
		}
	}

	return active
}

// GetDepletedGroundSpawns returns all depleted ground spawns
func (m *Manager) GetDepletedGroundSpawns() []*GroundSpawn {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	var depleted []*GroundSpawn
	for _, gs := range m.groundSpawns {
		if gs.IsDepleted() {
			depleted = append(depleted, gs)
		}
	}

	return depleted
}

// ProcessCommand handles ground spawn management commands
func (m *Manager) ProcessCommand(command string, args []string) (string, error) {
	switch command {
	case "stats":
		return m.handleStatsCommand(args)
	case "list":
		return m.handleListCommand(args)
	case "respawn":
		return m.handleRespawnCommand(args)
	case "info":
		return m.handleInfoCommand(args)
	case "reload":
		return m.handleReloadCommand(args)
	default:
		return "", fmt.Errorf("unknown ground spawn command: %s", command)
	}
}

// handleStatsCommand shows ground spawn system statistics
func (m *Manager) handleStatsCommand(args []string) (string, error) {
	stats := m.GetStatistics()

	result := "Ground Spawn System Statistics:\n"
	result += fmt.Sprintf("Total Harvests: %d\n", stats.TotalHarvests)
	result += fmt.Sprintf("Successful Harvests: %d\n", stats.SuccessfulHarvests)
	result += fmt.Sprintf("Rare Items Harvested: %d\n", stats.RareItemsHarvested)
	result += fmt.Sprintf("Skill Ups Generated: %d\n", stats.SkillUpsGenerated)
	result += fmt.Sprintf("Active Ground Spawns: %d\n", stats.ActiveGroundSpawns)

	if len(stats.HarvestsBySkill) > 0 {
		result += "\nHarvests by Skill:\n"
		for skill, count := range stats.HarvestsBySkill {
			result += fmt.Sprintf("  %s: %d\n", skill, count)
		}
	}

	return result, nil
}

// handleListCommand lists ground spawns
func (m *Manager) handleListCommand(args []string) (string, error) {
	count := m.GetGroundSpawnCount()
	if count == 0 {
		return "No ground spawns loaded.", nil
	}

	active := m.GetActiveGroundSpawns()
	depleted := m.GetDepletedGroundSpawns()

	result := fmt.Sprintf("Ground Spawns (Total: %d, Active: %d, Depleted: %d):\n",
		count, len(active), len(depleted))

	// Show first 10 active spawns
	shown := 0
	for _, gs := range active {
		if shown >= 10 {
			result += "... (and more)\n"
			break
		}
		result += fmt.Sprintf("  %d: %s (%s) - %d harvests remaining\n",
			gs.GetID(), gs.GetName(), gs.GetCollectionSkill(), gs.GetNumberHarvests())
		shown++
	}

	return result, nil
}

// handleRespawnCommand respawns ground spawns
func (m *Manager) handleRespawnCommand(args []string) (string, error) {
	if len(args) > 0 {
		// Respawn specific ground spawn
		var spawnID int32
		if _, err := fmt.Sscanf(args[0], "%d", &spawnID); err != nil {
			return "", fmt.Errorf("invalid ground spawn ID: %s", args[0])
		}

		gs := m.GetGroundSpawn(spawnID)
		if gs == nil {
			return fmt.Sprintf("Ground spawn %d not found.", spawnID), nil
		}

		gs.Respawn()
		return fmt.Sprintf("Ground spawn %d respawned.", spawnID), nil
	}

	// Respawn all depleted spawns
	depleted := m.GetDepletedGroundSpawns()
	for _, gs := range depleted {
		gs.Respawn()
	}

	return fmt.Sprintf("Respawned %d depleted ground spawns.", len(depleted)), nil
}

// handleInfoCommand shows information about a specific ground spawn
func (m *Manager) handleInfoCommand(args []string) (string, error) {
	if len(args) == 0 {
		return "", fmt.Errorf("ground spawn ID required")
	}

	var spawnID int32
	if _, err := fmt.Sscanf(args[0], "%d", &spawnID); err != nil {
		return "", fmt.Errorf("invalid ground spawn ID: %s", args[0])
	}

	gs := m.GetGroundSpawn(spawnID)
	if gs == nil {
		return fmt.Sprintf("Ground spawn %d not found.", spawnID), nil
	}

	result := fmt.Sprintf("Ground Spawn Information:\n")
	result += fmt.Sprintf("ID: %d\n", gs.GetID())
	result += fmt.Sprintf("Name: %s\n", gs.GetName())
	result += fmt.Sprintf("Collection Skill: %s\n", gs.GetCollectionSkill())
	result += fmt.Sprintf("Harvests Remaining: %d\n", gs.GetNumberHarvests())
	result += fmt.Sprintf("Attempts per Harvest: %d\n", gs.GetAttemptsPerHarvest())
	result += fmt.Sprintf("Ground Spawn Entry ID: %d\n", gs.GetGroundSpawnEntryID())
	result += fmt.Sprintf("Available: %v\n", gs.IsAvailable())
	result += fmt.Sprintf("Depleted: %v\n", gs.IsDepleted())

	return result, nil
}

// handleReloadCommand reloads ground spawns from database
func (m *Manager) handleReloadCommand(args []string) (string, error) {
	if m.database == nil {
		return "", fmt.Errorf("no database available")
	}

	// Clear current data
	m.mutex.Lock()
	m.groundSpawns = make(map[int32]*GroundSpawn)
	m.spawnsByZone = make(map[int32][]*GroundSpawn)
	m.entriesByID = make(map[int32][]*GroundSpawnEntry)
	m.itemsByID = make(map[int32][]*GroundSpawnEntryItem)
	m.respawnQueue = make(map[int32]time.Time)
	m.mutex.Unlock()

	// Reload from database
	if err := m.Initialize(); err != nil {
		return "", fmt.Errorf("failed to reload ground spawns: %w", err)
	}

	count := m.GetGroundSpawnCount()
	return fmt.Sprintf("Successfully reloaded %d ground spawns from database.", count), nil
}

// Shutdown gracefully shuts down the manager
func (m *Manager) Shutdown() {
	if m.logger != nil {
		m.logger.LogInfo("Shutting down ground spawn manager...")
	}

	m.mutex.Lock()
	defer m.mutex.Unlock()

	// Clear all data
	m.groundSpawns = make(map[int32]*GroundSpawn)
	m.spawnsByZone = make(map[int32][]*GroundSpawn)
	m.entriesByID = make(map[int32][]*GroundSpawnEntry)
	m.itemsByID = make(map[int32][]*GroundSpawnEntryItem)
	m.respawnQueue = make(map[int32]time.Time)
}
