package weblive

import (
	"fmt"
	"sort"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	modMu     sync.RWMutex
	muteAll    bool
	mutedIP    = make(map[string]bool)
	mutedUsers = make(map[string]bool) // key: normMuteUsername
	ipWindow   = make(map[string][]int64) // 每个 IP 最近窗口内的发送时间戳(ms)
	onlineMap = make(map[string]*onlineSession)
	seq       uint64
)

type ModerationSnapshot struct {
	MuteAll          bool             `json:"mute_all"`
	MutedIPs         []string         `json:"muted_ips"`
	MutedUsernames   []string         `json:"muted_usernames"`
	OnlineIPs        []IPOnlineItem   `json:"online_ips"`
	OnlineUsers      []OnlineUserItem `json:"online_users"`
	RateLimit struct {
		WindowMs int `json:"window_ms"`
		MaxHits  int `json:"max_hits"`
	} `json:"rate_limit"`
}

type IPOnlineItem struct {
	IP    string `json:"ip"`
	Count int    `json:"count"`
	Muted bool   `json:"muted"`
}

type onlineSession struct {
	ID        string
	IP        string
	Username  string
	Channel   string
	Connected time.Time
	LastAt    time.Time
}

type OnlineUserItem struct {
	ID         string `json:"id"`
	IP         string `json:"ip"`
	Username   string `json:"username"`
	Channel    string `json:"channel"`
	ConnectedAt string `json:"connected_at"`
	OnlineSec  int64  `json:"online_sec"`
	IdleSec    int64  `json:"idle_sec"`
	Muted      bool   `json:"muted"`
}

func SetMuteAll(enabled bool) {
	modMu.Lock()
	muteAll = enabled
	modMu.Unlock()
}

func SetIPMuted(ip string, enabled bool) {
	if ip == "" {
		return
	}
	modMu.Lock()
	if enabled {
		mutedIP[ip] = true
	} else {
		delete(mutedIP, ip)
	}
	modMu.Unlock()
}

func normMuteUsername(u string) string {
	return strings.ToLower(strings.TrimSpace(u))
}

// SetUserMuted 按登录用户名禁言（弹幕/礼物）；与 IP 禁言、全体禁言叠加。
func SetUserMuted(username string, enabled bool) {
	key := normMuteUsername(username)
	if key == "" {
		return
	}
	modMu.Lock()
	if enabled {
		mutedUsers[key] = true
	} else {
		delete(mutedUsers, key)
	}
	modMu.Unlock()
}

const (
	ipSendWindowMs = 3000
	ipSendMaxHits  = 10
)

func IsIPMuted(ip string) bool {
	modMu.RLock()
	defer modMu.RUnlock()
	return mutedIP[ip]
}

func IsMutedForIP(ip string) bool {
	modMu.RLock()
	defer modMu.RUnlock()
	if muteAll {
		return true
	}
	return mutedIP[ip]
}

// IsMutedForSend 发弹幕/礼物前：全体禁言、IP 禁言、或已登录用户名被禁。
func IsMutedForSend(ip, username string) bool {
	modMu.RLock()
	defer modMu.RUnlock()
	if muteAll {
		return true
	}
	if mutedIP[ip] {
		return true
	}
	if k := normMuteUsername(username); k != "" && mutedUsers[k] {
		return true
	}
	return false
}

func ModerationStateSnapshot() ModerationSnapshot {
	modMu.RLock()
	muteAllNow := muteAll
	muted := make([]string, 0, len(mutedIP))
	for ip := range mutedIP {
		muted = append(muted, ip)
	}
	mutedNames := make([]string, 0, len(mutedUsers))
	for u := range mutedUsers {
		mutedNames = append(mutedNames, u)
	}
	modMu.RUnlock()
	sort.Strings(muted)
	sort.Strings(mutedNames)

	counts := onlineIPCountsLocked()
	online := make([]IPOnlineItem, 0, len(counts))
	for ip, n := range counts {
		online = append(online, IPOnlineItem{IP: ip, Count: n, Muted: IsIPMuted(ip)})
	}
	sort.Slice(online, func(i, j int) bool {
		if online[i].Count == online[j].Count {
			return online[i].IP < online[j].IP
		}
		return online[i].Count > online[j].Count
	})
	users := onlineUsersLocked()
	out := ModerationSnapshot{MuteAll: muteAllNow, MutedIPs: muted, OnlineIPs: online, OnlineUsers: users}
	out.RateLimit.WindowMs = ipSendWindowMs
	out.RateLimit.MaxHits = ipSendMaxHits
	return out
}

// AllowSendByIP 本地内存限频（同 IP 先本地判定，避免刷爆）
func AllowSendByIP(ip string) bool {
	if ip == "" {
		return true
	}
	now := time.Now().UnixMilli()
	cut := now - ipSendWindowMs
	modMu.Lock()
	defer modMu.Unlock()
	arr := ipWindow[ip]
	if len(arr) > 0 {
		k := 0
		for _, ts := range arr {
			if ts >= cut {
				arr[k] = ts
				k++
			}
		}
		arr = arr[:k]
	}
	if len(arr) >= ipSendMaxHits {
		ipWindow[ip] = arr
		return false
	}
	arr = append(arr, now)
	if len(arr) > ipSendMaxHits*4 {
		arr = arr[len(arr)-ipSendMaxHits*2:]
	}
	ipWindow[ip] = arr
	return true
}

func RegisterOnlineSession(channel, ip, username string) string {
	now := time.Now()
	id := fmt.Sprintf("%s-%d", channel, atomic.AddUint64(&seq, 1))
	modMu.Lock()
	onlineMap[id] = &onlineSession{
		ID:        id,
		IP:        ip,
		Username:  username,
		Channel:   channel,
		Connected: now,
		LastAt:    now,
	}
	modMu.Unlock()
	return id
}

func TouchOnlineSession(id string) {
	if id == "" {
		return
	}
	modMu.Lock()
	if s := onlineMap[id]; s != nil {
		s.LastAt = time.Now()
	}
	modMu.Unlock()
}

func UnregisterOnlineSession(id string) {
	if id == "" {
		return
	}
	modMu.Lock()
	delete(onlineMap, id)
	modMu.Unlock()
}

func onlineIPCountsLocked() map[string]int {
	out := make(map[string]int)
	for _, s := range onlineMap {
		if s == nil || s.IP == "" {
			continue
		}
		out[s.IP]++
	}
	return out
}

func onlineUsersLocked() []OnlineUserItem {
	now := time.Now()
	out := make([]OnlineUserItem, 0, len(onlineMap))
	for _, s := range onlineMap {
		if s == nil {
			continue
		}
		out = append(out, OnlineUserItem{
			ID:          s.ID,
			IP:          s.IP,
			Username:    s.Username,
			Channel:     s.Channel,
			ConnectedAt: s.Connected.Format(time.RFC3339),
			OnlineSec:   int64(now.Sub(s.Connected).Seconds()),
			IdleSec:     int64(now.Sub(s.LastAt).Seconds()),
			Muted:       IsMutedForSend(s.IP, s.Username),
		})
	}
	sort.Slice(out, func(i, j int) bool {
		return out[i].OnlineSec > out[j].OnlineSec
	})
	return out
}
