package api

import (
	"crypto/rand"
	"encoding/hex"
	"sync"
	"time"
)

// Sessions is an in-memory token store. Replace with a signed JWT or
// a Redis-backed store when we need multi-replica CP. For MVP one process
// is enough.
type Sessions struct {
	mu    sync.RWMutex
	store map[string]session
}

type session struct {
	user    string
	expires time.Time
}

func NewSessions() *Sessions {
	return &Sessions{store: make(map[string]session)}
}

func (s *Sessions) Issue(user string) string {
	b := make([]byte, 16)
	_, _ = rand.Read(b)
	tok := hex.EncodeToString(b)
	s.mu.Lock()
	s.store[tok] = session{user: user, expires: time.Now().Add(12 * time.Hour)}
	s.mu.Unlock()
	return tok
}

func (s *Sessions) Valid(tok string) bool {
	s.mu.RLock()
	sess, ok := s.store[tok]
	s.mu.RUnlock()
	if !ok {
		return false
	}
	return time.Now().Before(sess.expires)
}

// User returns the username for a valid token, or "" if the token is
// unknown or expired.
func (s *Sessions) User(tok string) (string, bool) {
	s.mu.RLock()
	sess, ok := s.store[tok]
	s.mu.RUnlock()
	if !ok {
		return "", false
	}
	if !time.Now().Before(sess.expires) {
		return "", false
	}
	return sess.user, true
}

// Revoke drops a session. Used by /api/logout.
func (s *Sessions) Revoke(tok string) {
	s.mu.Lock()
	delete(s.store, tok)
	s.mu.Unlock()
}
