package session

import (
	"log"
	"sync"
	"time"
)

// MemoryStore 基于内存的会话存储（sync.Map + RWMutex，适合单实例部署）
type MemoryStore struct {
	mu              sync.RWMutex
	sessions        map[string]*Session // sid → session
	byUID           map[uint][]string   // uid → []sid（用于批量删除）
	idleTimeout     time.Duration       // 不记住我：空闲超时
	rememberTimeout time.Duration       // 记住我：空闲超时
}

// NewMemoryStore 创建内存存储实例
func NewMemoryStore(idleTimeout, rememberTimeout time.Duration) *MemoryStore {
	return &MemoryStore{
		sessions:        make(map[string]*Session),
		byUID:           make(map[uint][]string),
		idleTimeout:     idleTimeout,
		rememberTimeout: rememberTimeout,
	}
}

// Get 获取会话（根据 RememberMe 选择对应的空闲超时检查过期）
func (ms *MemoryStore) Get(id string) (*Session, error) {
	ms.mu.RLock()
	s, ok := ms.sessions[id]
	ms.mu.RUnlock()
	if !ok {
		return nil, nil
	}
	// 记住我 → 使用更长的超时
	timeout := ms.idleTimeout
	if s.RememberMe {
		timeout = ms.rememberTimeout
	}
	if s.IsExpired(timeout) {
		ms.Delete(id)
		return nil, nil
	}
	return s, nil
}

// Set 写入会话（同步更新 byUID 索引）
func (ms *MemoryStore) Set(s *Session) error {
	ms.mu.Lock()
	defer ms.mu.Unlock()

	if old, ok := ms.sessions[s.ID]; ok {
		// sid 已存在：仅当 uid 变更时更新索引
		if old.UserID != s.UserID {
			ms.removeFromUIDIndex(old.UserID, s.ID)
			ms.byUID[s.UserID] = append(ms.byUID[s.UserID], s.ID)
		}
	} else {
		// 新 sid：追加到索引
		ms.byUID[s.UserID] = append(ms.byUID[s.UserID], s.ID)
	}

	ms.sessions[s.ID] = s
	return nil
}

// Delete 删除会话（同步更新 byUID 索引）
func (ms *MemoryStore) Delete(id string) error {
	ms.mu.Lock()
	defer ms.mu.Unlock()

	s, ok := ms.sessions[id]
	if !ok {
		return nil
	}
	ms.removeFromUIDIndex(s.UserID, id)
	delete(ms.sessions, id)
	return nil
}

// DeleteByUID 删除某用户的所有会话
func (ms *MemoryStore) DeleteByUID(uid uint) error {
	ms.mu.Lock()
	defer ms.mu.Unlock()

	sids, ok := ms.byUID[uid]
	if !ok {
		return nil
	}
	for _, sid := range sids {
		delete(ms.sessions, sid)
	}
	delete(ms.byUID, uid)
	log.Printf("[MemoryStore] DeleteByUID uid=%d count=%d", uid, len(sids))
	return nil
}

// DeleteByUIDExclude 删除某用户的所有会话，但保留 excludeSID
func (ms *MemoryStore) DeleteByUIDExclude(uid uint, excludeSID string) error {
	ms.mu.Lock()
	defer ms.mu.Unlock()

	sids, ok := ms.byUID[uid]
	if !ok {
		return nil
	}
	var kept []string
	var removed int
	for _, sid := range sids {
		if sid == excludeSID {
			kept = append(kept, sid)
			continue
		}
		delete(ms.sessions, sid)
		removed++
	}
	if len(kept) > 0 {
		ms.byUID[uid] = kept
	} else {
		delete(ms.byUID, uid)
	}
	log.Printf("[MemoryStore] DeleteByUIDExclude uid=%d removed=%d kept=%d", uid, removed, len(kept))
	return nil
}

// ListByUID 列出某用户的所有活跃会话（已过期的会被清理掉）
func (ms *MemoryStore) ListByUID(uid uint) ([]*Session, error) {
	ms.mu.RLock()
	sids, ok := ms.byUID[uid]
	if !ok {
		ms.mu.RUnlock()
		return nil, nil
	}
	// 复制 sid 列表避免持锁期间修改
	sidCopy := make([]string, len(sids))
	copy(sidCopy, sids)
	ms.mu.RUnlock()

	var result []*Session
	for _, sid := range sidCopy {
		// Get 会检查过期并自动清理
		s, err := ms.Get(sid)
		if err != nil || s == nil {
			continue
		}
		result = append(result, s)
	}
	return result, nil
}

// Cleanup 清理过期会话（根据 RememberMe 选择对应超时）
func (ms *MemoryStore) Cleanup() int {
	ms.mu.Lock()
	defer ms.mu.Unlock()

	var expiredSids []string
	for sid, s := range ms.sessions {
		timeout := ms.idleTimeout
		if s.RememberMe {
			timeout = ms.rememberTimeout
		}
		if s.IsExpired(timeout) {
			expiredSids = append(expiredSids, sid)
		}
	}
	for _, sid := range expiredSids {
		s := ms.sessions[sid]
		ms.removeFromUIDIndex(s.UserID, sid)
		delete(ms.sessions, sid)
	}
	if len(expiredSids) > 0 {
		log.Printf("[MemoryStore] Cleanup removed=%d remaining=%d", len(expiredSids), len(ms.sessions))
	}
	return len(expiredSids)
}

// removeFromUIDIndex 从 byUID 索引中移除指定 sid（调用者需持有写锁）
func (ms *MemoryStore) removeFromUIDIndex(uid uint, sid string) {
	sids := ms.byUID[uid]
	for i, s := range sids {
		if s == sid {
			ms.byUID[uid] = append(sids[:i], sids[i+1:]...)
			break
		}
	}
	if len(ms.byUID[uid]) == 0 {
		delete(ms.byUID, uid)
	}
}

// StartCleanup 启动后台清理 goroutine（应在 main 中调用）
func (ms *MemoryStore) StartCleanup(interval time.Duration) {
	go func() {
		ticker := time.NewTicker(interval)
		defer ticker.Stop()
		for range ticker.C {
			ms.Cleanup()
		}
	}()
}

// Metrics 返回内存存储状态
func (ms *MemoryStore) Metrics() StoreMetrics {
	ms.mu.RLock()
	count := len(ms.sessions)
	ms.mu.RUnlock()
	return StoreMetrics{
		Type:        "memory",
		Healthy:     true,
		ActiveCount: count,
	}
}

// Snapshot 返回所有活跃会话的副本（用于 Redis 恢复时迁移）
func (ms *MemoryStore) Snapshot() []*Session {
	ms.mu.RLock()
	defer ms.mu.RUnlock()

	var active []*Session
	for _, s := range ms.sessions {
		timeout := ms.idleTimeout
		if s.RememberMe {
			timeout = ms.rememberTimeout
		}
		if !s.IsExpired(timeout) {
			// 返回副本，避免外部修改内部 map
			copyS := *s
			active = append(active, &copyS)
		}
	}
	return active
}
