package presence

import (
	"errors"
	"sync"
	"time"

	"github.com/google/uuid"
)

var (
	ErrUserNotFound   = errors.New("user not found")
	ErrSessionExpired = errors.New("session expired")
)

const (
	IdleTimeout       = 5 * time.Minute
	ConnectionTimeout = 2 * time.Minute
)

// Manager manages user presence and sessions
type Manager struct {
	sessions map[string]*Session // userID -> session
	mu       sync.RWMutex
}

// NewManager creates a new presence manager
func NewManager() *Manager {
	return &Manager{
		sessions: make(map[string]*Session),
	}
}

// CreateSession creates a new user session
func (m *Manager) CreateSession(userID string) *Session {
	m.mu.Lock()
	defer m.mu.Unlock()

	sessionID := uuid.New().String()
	session := NewSession(userID, sessionID)

	m.sessions[userID] = session
	return session
}

// GetSession retrieves a user session
func (m *Manager) GetSession(userID string) (*Session, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	session, exists := m.sessions[userID]
	if !exists {
		return nil, ErrUserNotFound
	}

	return session, nil
}

// EndSession removes a user session
func (m *Manager) EndSession(userID string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	_, exists := m.sessions[userID]
	if !exists {
		return ErrUserNotFound
	}

	delete(m.sessions, userID)
	return nil
}

// UpdatePresence updates a user's presence status
func (m *Manager) UpdatePresence(userID string, status Status) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	session, exists := m.sessions[userID]
	if !exists {
		return ErrUserNotFound
	}

	session.Status = status
	session.UpdateActivity()
	return nil
}

// SetChannelPresence updates which channel user is in
func (m *Manager) SetChannelPresence(userID string, channelID string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	session, exists := m.sessions[userID]
	if !exists {
		return ErrUserNotFound
	}

	session.CurrentChannelID = channelID
	session.UpdateActivity()
	return nil
}

// SetMuteStatus updates mute status
func (m *Manager) SetMuteStatus(userID string, micMuted bool, speakerMuted bool) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	session, exists := m.sessions[userID]
	if !exists {
		return ErrUserNotFound
	}

	session.IsMicrophoneMuted = micMuted
	session.IsSpeakerMuted = speakerMuted
	session.UpdateActivity()
	return nil
}

// GetOnlineUsers returns list of online users
func (m *Manager) GetOnlineUsers() []*Session {
	m.mu.RLock()
	defer m.mu.RUnlock()

	var users []*Session
	for _, session := range m.sessions {
		if session.Status != StatusOffline {
			users = append(users, session)
		}
	}
	return users
}

// GetChannelMembers returns sessions of users in a channel
func (m *Manager) GetChannelMembers(channelID string) []*Session {
	m.mu.RLock()
	defer m.mu.RUnlock()

	var members []*Session
	for _, session := range m.sessions {
		if session.CurrentChannelID == channelID && session.Status != StatusOffline {
			members = append(members, session)
		}
	}
	return members
}

// DetectIdleUsers detects and marks idle users
func (m *Manager) DetectIdleUsers() {
	m.mu.Lock()
	defer m.mu.Unlock()

	for _, session := range m.sessions {
		if session.Status == StatusOnline && session.IsIdle(IdleTimeout) {
			session.MarkIdle()
		}
	}
}

// ReportActivity updates user's activity timestamp
func (m *Manager) ReportActivity(userID string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	session, exists := m.sessions[userID]
	if !exists {
		return ErrUserNotFound
	}

	session.UpdateActivity()
	return nil
}
