package widget

import (
	"sync"
	"time"
)

// Manager manages widgets within a zone
type Manager struct {
	widgets        map[int32]*Widget       // Widget ID -> Widget
	widgetsByDBID  map[int32]*Widget       // Database ID -> Widget
	widgetTimers   map[*Widget]*time.Timer // Active timers for widgets
	timerCallbacks map[*Widget]func()      // Timer callbacks
	mutex          sync.RWMutex
	timerMutex     sync.Mutex
}

// NewManager creates a new widget manager
func NewManager() *Manager {
	return &Manager{
		widgets:        make(map[int32]*Widget),
		widgetsByDBID:  make(map[int32]*Widget),
		widgetTimers:   make(map[*Widget]*time.Timer),
		timerCallbacks: make(map[*Widget]func()),
	}
}

// AddWidget adds a widget to the manager
func (m *Manager) AddWidget(widget *Widget) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	if widget.GetWidgetID() > 0 {
		m.widgets[widget.GetWidgetID()] = widget
	}

	if widget.GetDatabaseID() > 0 {
		m.widgetsByDBID[widget.GetDatabaseID()] = widget
	}
}

// RemoveWidget removes a widget from the manager
func (m *Manager) RemoveWidget(widgetID int32) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	if widget, exists := m.widgets[widgetID]; exists {
		// Cancel any active timers
		m.cancelWidgetTimer(widget)

		// Remove from maps
		delete(m.widgets, widgetID)
		if widget.GetDatabaseID() > 0 {
			delete(m.widgetsByDBID, widget.GetDatabaseID())
		}
	}
}

// GetWidget gets a widget by ID
func (m *Manager) GetWidget(widgetID int32) *Widget {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	return m.widgets[widgetID]
}

// GetWidgetByDatabaseID gets a widget by database ID
func (m *Manager) GetWidgetByDatabaseID(databaseID int32) *Widget {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	return m.widgetsByDBID[databaseID]
}

// GetAllWidgets returns all widgets
func (m *Manager) GetAllWidgets() []*Widget {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	widgets := make([]*Widget, 0, len(m.widgets))
	for _, widget := range m.widgets {
		widgets = append(widgets, widget)
	}

	return widgets
}

// GetWidgetsByType returns all widgets of a specific type
func (m *Manager) GetWidgetsByType(widgetType int8) []*Widget {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	widgets := make([]*Widget, 0)
	for _, widget := range m.widgets {
		if widget.GetWidgetType() == widgetType {
			widgets = append(widgets, widget)
		}
	}

	return widgets
}

// AddWidgetTimer adds a timer for a widget
func (m *Manager) AddWidgetTimer(widget *Widget, seconds float32, callback func()) {
	m.timerMutex.Lock()
	defer m.timerMutex.Unlock()

	// Cancel existing timer if any
	m.cancelWidgetTimerLocked(widget)

	// Create new timer
	duration := time.Duration(seconds * float32(time.Second))
	timer := time.AfterFunc(duration, func() {
		m.handleWidgetTimer(widget)
	})

	m.widgetTimers[widget] = timer
	if callback != nil {
		m.timerCallbacks[widget] = callback
	} else {
		// Default callback for auto-close
		m.timerCallbacks[widget] = func() {
			widget.HandleTimerUpdate()
		}
	}
}

// HasWidgetTimer checks if a widget has an active timer
func (m *Manager) HasWidgetTimer(widget *Widget) bool {
	m.timerMutex.Lock()
	defer m.timerMutex.Unlock()

	_, exists := m.widgetTimers[widget]
	return exists
}

// CancelWidgetTimer cancels a widget's timer
func (m *Manager) CancelWidgetTimer(widget *Widget) {
	m.timerMutex.Lock()
	defer m.timerMutex.Unlock()

	m.cancelWidgetTimerLocked(widget)
}

// cancelWidgetTimerLocked cancels a timer (must hold timerMutex)
func (m *Manager) cancelWidgetTimerLocked(widget *Widget) {
	if timer, exists := m.widgetTimers[widget]; exists {
		timer.Stop()
		delete(m.widgetTimers, widget)
		delete(m.timerCallbacks, widget)
	}
}

// cancelWidgetTimer cancels a timer (must hold timerMutex)
func (m *Manager) cancelWidgetTimer(widget *Widget) {
	m.timerMutex.Lock()
	defer m.timerMutex.Unlock()

	m.cancelWidgetTimerLocked(widget)
}

// handleWidgetTimer handles a widget timer expiration
func (m *Manager) handleWidgetTimer(widget *Widget) {
	m.timerMutex.Lock()
	callback, exists := m.timerCallbacks[widget]
	if exists {
		delete(m.widgetTimers, widget)
		delete(m.timerCallbacks, widget)
	}
	m.timerMutex.Unlock()

	// Execute callback outside of lock
	if callback != nil {
		callback()
	}
}

// ProcessWidgetTimers processes all widget timers (called periodically)
func (m *Manager) ProcessWidgetTimers() {
	// Timers are handled automatically by time.AfterFunc
	// This method is here for compatibility but doesn't need to do anything
}

// GetDoorWidgets returns all door widgets
func (m *Manager) GetDoorWidgets() []*Widget {
	return m.GetWidgetsByType(WidgetTypeDoor)
}

// GetLiftWidgets returns all lift widgets
func (m *Manager) GetLiftWidgets() []*Widget {
	return m.GetWidgetsByType(WidgetTypeLift)
}

// GetOpenWidgets returns all open widgets
func (m *Manager) GetOpenWidgets() []*Widget {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	widgets := make([]*Widget, 0)
	for _, widget := range m.widgets {
		if widget.IsOpen() {
			widgets = append(widgets, widget)
		}
	}

	return widgets
}

// GetWidgetsByHouseID returns all widgets for a house
func (m *Manager) GetWidgetsByHouseID(houseID int32) []*Widget {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	widgets := make([]*Widget, 0)
	for _, widget := range m.widgets {
		if widget.GetHouseID() == houseID {
			widgets = append(widgets, widget)
		}
	}

	return widgets
}

// GetLinkedWidgets returns all widgets linked to the given widget
func (m *Manager) GetLinkedWidgets(widget *Widget) []*Widget {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	linked := make([]*Widget, 0)

	// Add linked spawn
	if widget.GetLinkedSpawnID() > 0 {
		if linkedWidget := m.widgetsByDBID[widget.GetLinkedSpawnID()]; linkedWidget != nil {
			linked = append(linked, linkedWidget)
		}
	}

	// Add action spawn
	if widget.GetActionSpawnID() > 0 {
		if actionWidget := m.widgetsByDBID[widget.GetActionSpawnID()]; actionWidget != nil {
			linked = append(linked, actionWidget)
		}
	}

	// Find widgets that link to this one
	for _, w := range m.widgets {
		if w.GetLinkedSpawnID() == widget.GetDatabaseID() ||
			w.GetActionSpawnID() == widget.GetDatabaseID() {
			linked = append(linked, w)
		}
	}

	return linked
}

// ResolveLinkedSpawns resolves all linked spawn references
func (m *Manager) ResolveLinkedSpawns() {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	for _, widget := range m.widgets {
		// Resolve linked spawn
		if widget.GetLinkedSpawnID() > 0 && widget.GetLinkedSpawn() == nil {
			if linked := m.widgetsByDBID[widget.GetLinkedSpawnID()]; linked != nil {
				widget.SetLinkedSpawn(linked)
			}
		}

		// Resolve action spawn
		if widget.GetActionSpawnID() > 0 && widget.GetActionSpawn() == nil {
			if action := m.widgetsByDBID[widget.GetActionSpawnID()]; action != nil {
				widget.SetActionSpawn(action)
			}
		}
	}
}

// Clear removes all widgets and cancels all timers
func (m *Manager) Clear() {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	// Cancel all timers
	m.timerMutex.Lock()
	for widget, timer := range m.widgetTimers {
		timer.Stop()
		delete(m.widgetTimers, widget)
		delete(m.timerCallbacks, widget)
	}
	m.timerMutex.Unlock()

	// Clear maps
	m.widgets = make(map[int32]*Widget)
	m.widgetsByDBID = make(map[int32]*Widget)
}

// GetStatistics returns widget statistics
func (m *Manager) GetStatistics() map[string]interface{} {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	stats := make(map[string]interface{})
	stats["total_widgets"] = len(m.widgets)
	stats["door_count"] = len(m.GetDoorWidgets())
	stats["lift_count"] = len(m.GetLiftWidgets())
	stats["open_count"] = len(m.GetOpenWidgets())

	m.timerMutex.Lock()
	stats["active_timers"] = len(m.widgetTimers)
	m.timerMutex.Unlock()

	return stats
}
