package models

import (
	"context"
	"encoding/json"
	"fmt"
	"runtime"

	"github.com/go-redis/redis/v8"
)

type WebsocketRedisRoomZ struct {
	Rdb    *redis.Client
	Host   string
	Passwd string
	Ctx    context.Context
}

var WsRedisRoom WebsocketRedisRoomZ

func init() {
	WsRedisRoom.Select(0) // 选择redis库
}

/*
db 连接哪个redis reids有16个数据库 0-15
数据库0 是websocket使用
*/
func (ggg *WebsocketRedisRoomZ) Select(db int) *redis.Client {
	if runtime.GOOS == "windows" {
		ggg.Host = "192.168.0.200"
	} else {
		ggg.Host = "localhost"
	}
	ggg.Passwd = "qq282246101"
	ggg.Rdb = redis.NewClient(&redis.Options{
		Network:            "",
		Addr:               ggg.Host + ":6379",
		Dialer:             nil,
		OnConnect:          nil,
		Password:           ggg.Passwd,
		DB:                 db,
		MaxRetries:         0,
		MinRetryBackoff:    0,
		MaxRetryBackoff:    0,
		DialTimeout:        0,
		ReadTimeout:        0,
		WriteTimeout:       0,
		PoolSize:           30,
		MinIdleConns:       10,
		MaxConnAge:         0,
		PoolTimeout:        0,
		IdleTimeout:        0,
		IdleCheckFrequency: 0,
		TLSConfig:          nil,
	})
	//检测连接是否success success返回 pong
	ggg.Ctx = ggg.Rdb.Context()
	_, errRedis := ggg.Rdb.Ping(ggg.Ctx).Result()
	if errRedis != nil {
		fmt.Println(errRedis.Error())
		return nil
	}
	return ggg.Rdb
}

// 存储map[房间名]map[用户名]string{ connetID }到 Redis 哈希
func (ggg *WebsocketRedisRoomZ) StoreNestedMapToRedisHash(data map[string]map[string]string) error {
	for outerKey, innerMap := range data {
		serializedInnerMap, err := json.Marshal(innerMap)
		if err != nil {
			return err
		}
		err = ggg.Rdb.HSet(ggg.Ctx, "nested_map_key", outerKey, string(serializedInnerMap)).Err()
		if err != nil {
			return err
		}
	}
	return nil
}

// 读取所有websocket 房间数据
func (ggg *WebsocketRedisRoomZ) RetrieveNestedMapFromRedisHash() (map[string]map[string]string, error) {
	result := make(map[string]map[string]string)
	fields, err := ggg.Rdb.HGetAll(ggg.Ctx, "nested_map_key").Result()
	if err != nil {
		return nil, err
	}
	for outerKey, serializedInnerMap := range fields {
		var innerMap map[string]string
		err = json.Unmarshal([]byte(serializedInnerMap), &innerMap)
		if err != nil {
			return nil, err
		}
		result[outerKey] = innerMap
	}
	return result, err
}

// 通过 房间名,用户名 找到 connetID
func (ggg *WebsocketRedisRoomZ) RetrieveValueByInnerKey(outerKey, innerKey string) (string, error) {
	// 先从Redis获取整个嵌套map数据并还原
	result := make(map[string]map[string]string)
	fields, err := ggg.Rdb.HGetAll(ggg.Ctx, "nested_map_key").Result()
	if err != nil {
		return "", err
	}
	for outerK, serializedInnerMap := range fields {
		var innerMap map[string]string
		err = json.Unmarshal([]byte(serializedInnerMap), &innerMap)
		if err != nil {
			return "", err
		}
		result[outerK] = innerMap
	}

	// 从还原后的map结构中查找指定内层键对应的值
	innerMap, ok := result[outerKey]
	if !ok {
		return "", fmt.Errorf("外层键 %s 不存在", outerKey)
	}
	value, ok := innerMap[innerKey]
	if !ok {
		return "", fmt.Errorf("内层键 %s 在外层键 %s 下不存在", innerKey, outerKey)
	}
	return value, nil
}

// 清空redis库0下的所有key
func (ggg *WebsocketRedisRoomZ) FlushDB0ByKeys() error {
	keys, err := ggg.Rdb.Keys(ggg.Ctx, "*").Result()
	if err != nil {
		return err
	}
	if len(keys) == 0 {
		return nil
	}
	_, err = ggg.Rdb.Del(ggg.Ctx, keys...).Result()
	return err
}

// 通过 房间名,connetID 找到 用户名
func (ggg *WebsocketRedisRoomZ) RetrieveInnerKeyByValue(outerKey, value string) (string, error) {
	// 先从Redis获取整个嵌套map数据并还原
	result := make(map[string]map[string]string)
	fields, err := ggg.Rdb.HGetAll(ggg.Ctx, "nested_map_key").Result()
	if err != nil {
		return "", err
	}
	for outerK, serializedInnerMap := range fields {
		var innerMap map[string]string
		err = json.Unmarshal([]byte(serializedInnerMap), &innerMap)
		if err != nil {
			return "", err
		}
		result[outerK] = innerMap
	}

	// 从还原后的map结构中查找指定外层键对应的内层map
	innerMap, ok := result[outerKey]
	if !ok {
		return "", fmt.Errorf("外层键 %s 不存在", outerKey)
	}

	// 遍历内层map，查找值为value的键
	for innerK, v := range innerMap {
		if v == value {
			return innerK, nil
		}
	}
	return "", fmt.Errorf("未找到值为 %s 的内层键（外层键为 %s）", value, outerKey)
}
