package service

import (
	"fmt"
	"sort"
	"strings"
	"time"
	"xboard-go/internal/database"
)

const deviceStateTTL = 10 * time.Minute

type userDevicesSnapshot map[string][]string

func SaveUserNodeDevices(userID, nodeID int, ips []string) error {
	unique := make([]string, 0, len(ips))
	seen := make(map[string]struct{})
	for _, ip := range ips {
		trimmed := strings.TrimSpace(ip)
		if trimmed == "" {
			continue
		}
		if _, ok := seen[trimmed]; ok {
			continue
		}
		seen[trimmed] = struct{}{}
		unique = append(unique, trimmed)
	}
	sort.Strings(unique)

	return database.CacheSet(deviceStateKey(userID, nodeID), unique, deviceStateTTL)
}

func GetUsersDevices(userIDs []int) map[int][]string {
	result := make(map[int][]string, len(userIDs))

	for _, userID := range userIDs {
		snapshot, ok := database.CacheGetJSON[userDevicesSnapshot](deviceStateUserIndexKey(userID))
		if !ok {
			result[userID] = []string{}
			continue
		}

		merged := make([]string, 0)
		seen := make(map[string]struct{})
		for _, ips := range snapshot {
			for _, ip := range ips {
				if _, exists := seen[ip]; exists {
					continue
				}
				seen[ip] = struct{}{}
				merged = append(merged, ip)
			}
		}
		sort.Strings(merged)
		result[userID] = merged
	}

	return result
}

func SetDevices(userID, nodeID int, ips []string) error {
	if err := SaveUserNodeDevices(userID, nodeID, ips); err != nil {
		return err
	}

	indexKey := deviceStateUserIndexKey(userID)
	indexSnapshot, _ := database.CacheGetJSON[userDevicesSnapshot](indexKey)
	indexSnapshot[fmt.Sprintf("%d", nodeID)] = normalizeIPs(ips)
	return database.CacheSet(indexKey, indexSnapshot, deviceStateTTL)
}

func normalizeIPs(ips []string) []string {
	seen := make(map[string]struct{})
	result := make([]string, 0, len(ips))
	for _, ip := range ips {
		ip = strings.TrimSpace(ip)
		if ip == "" {
			continue
		}
		if _, ok := seen[ip]; ok {
			continue
		}
		seen[ip] = struct{}{}
		result = append(result, ip)
	}
	sort.Strings(result)
	return result
}

func deviceStateKey(userID, nodeID int) string {
	return fmt.Sprintf("device_state:user:%d:node:%d", userID, nodeID)
}

func deviceStateUserIndexKey(userID int) string {
	return fmt.Sprintf("device_state:user:%d:index", userID)
}
