package session

import (
	"sync"
	"sync/atomic"
	"time"

	"realtime-gateway/internal/model"
)

type Session struct {
	ID            string
	Role          model.Role
	Authenticated bool
	ChannelID     string
	Subscriptions []model.Subscription
	CreatedAt     time.Time
}

type Snapshot struct {
	ID            string               `json:"id"`
	Role          model.Role           `json:"role"`
	Authenticated bool                 `json:"authenticated"`
	ChannelID     string               `json:"channelId,omitempty"`
	CreatedAt     time.Time            `json:"createdAt"`
	Subscriptions []model.Subscription `json:"subscriptions"`
}

type Manager struct {
	mu       sync.RWMutex
	sequence atomic.Uint64
	sessions map[string]*Session
}

func NewManager() *Manager {
	return &Manager{
		sessions: make(map[string]*Session),
	}
}

func (m *Manager) Create() *Session {
	id := m.sequence.Add(1)
	session := &Session{
		ID:        formatSessionID(id),
		Role:      model.RoleConsumer,
		CreatedAt: time.Now(),
	}

	m.mu.Lock()
	m.sessions[session.ID] = session
	m.mu.Unlock()
	return session
}

func (m *Manager) Delete(sessionID string) {
	m.mu.Lock()
	delete(m.sessions, sessionID)
	m.mu.Unlock()
}

func (m *Manager) Get(sessionID string) (*Session, bool) {
	m.mu.RLock()
	defer m.mu.RUnlock()
	session, ok := m.sessions[sessionID]
	return session, ok
}

func (m *Manager) Count() int {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return len(m.sessions)
}

func (m *Manager) List() []Snapshot {
	m.mu.RLock()
	defer m.mu.RUnlock()

	snapshots := make([]Snapshot, 0, len(m.sessions))
	for _, current := range m.sessions {
		subscriptions := append([]model.Subscription(nil), current.Subscriptions...)
		snapshots = append(snapshots, Snapshot{
			ID:            current.ID,
			Role:          current.Role,
			Authenticated: current.Authenticated,
			ChannelID:     current.ChannelID,
			CreatedAt:     current.CreatedAt,
			Subscriptions: subscriptions,
		})
	}
	return snapshots
}

func formatSessionID(id uint64) string {
	return "sess-" + itoa(id)
}

func itoa(v uint64) string {
	if v == 0 {
		return "0"
	}
	var buf [20]byte
	i := len(buf)
	for v > 0 {
		i--
		buf[i] = byte('0' + v%10)
		v /= 10
	}
	return string(buf[i:])
}
