// cache.go
package cache

import (
	"context"
	"encoding/json"
	"log"
	"strconv"
	"time"

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

// reference, do not delete that
/*type CacheServer struct {
	Submits int64 `json:"submits"`
	Blocks  int64 `json:"blocks"`

	Accepts float64 `json:"accepts"`
	Rejects int64   `json:"rejects"`
	Shares  int64   `json:"shares"`

	Rewards float64 `json:"reward"`
	Fees    float64 `json:"fee"`

	RefDiff float64 `json:"refdiff"`

	//LastSubmit int64 `json:"lastsubmit"`
}

type CacheUser struct {
	Submits int64 `json:"submits"`
	Blocks  int64 `json:"blocks"`

	Accepts float64 `json:"accepts"`
	Rejects int64   `json:"rejects"`
	Shares  int64   `json:"shares"`

	Rewards float64 `json:"reward"`
	Fees    float64 `json:"fee"`

	User string `json:"user"`
}

type CacheMhs struct {
	User  string `json:"user"`
	Miner string `json:"miner"`
	Index string `json:"index"`

	Accepts []CacheMhsItem `json:"accepts"`
	Rejects []CacheMhsItem `json:"rejects"`

	StartDayTime string `json:"startday"`
}

type CacheMiner struct {
	Submits int64 `json:"submits"`
	Blocks  int64 `json:"blocks"`

	Accepts float64 `json:"accepts"`
	Rejects int64   `json:"rejects"`
	Shares  int64   `json:"shares"`

	LastDiff float64 `json:"diff"`

	Rewards float64 `json:"reward"`
	Fees    float64 `json:"fee"`

	Retry int `json:"retry"`

	LastSubmit int64 `json:"lastsubmit"`

	User  string `json:"user"`
	Miner string `json:"miner"`
	Index string `json:"index"`

	ErrStaleds    int64 `json:"staleds"`
	ErrLowDiffs   int64 `json:"lowdiffs"`
	ErrDuplicates int64 `json:"duplicates"`
	ErrFormats    int64 `json:"formats"`
	ErrOthers     int64 `json:"others"`
}*/

// need used
type CacheMhsItem struct {
	Tt   string  `json:"Tt"`
	Diff float64 `json:"diff"`
}

/*
type CacheMinerItem struct {
	User  string `json:"user"`
	Miner string `json:"miner"`
	Index string `json:"index"`
}*/

func LoadIntCache(client *redis.Client, key_int string) int64 {
	val, err := client.Get(context.Background(), key_int).Result()
	if err != nil {
		log.Printf("[LoadIntCache]Error retrieving data from Redis: %s, %v\n", key_int, err)
		return 0
	}
	val_int, err := strconv.ParseInt(val, 10, 64)
	if err != nil {
		log.Printf("[LoadIntCache]Error parsing integer from string: %v\n", err)
		return 0
	}
	return val_int
}

func StoreIntCache(client *redis.Client, key string, intNumber int64) bool {
	numberStr := strconv.FormatInt(intNumber, 10)
	expiration := 7 * 24 * time.Hour
	err := client.Set(context.Background(), key, numberStr, expiration).Err()
	if err != nil {
		log.Printf("[StoreIntCache]Error storing data in Redis: %s, %v\n", key, err)
		return false
	}
	return true
}

func LoadFloatCache(client *redis.Client, key_float string) float64 {
	val, err := client.Get(context.Background(), key_float).Result()
	if err != nil {
		log.Printf("[LoadFloatCache]Error retrieving data from Redis: %s, %v\n", key_float, err)
		return 0
	}
	val_f, err := strconv.ParseFloat(val, 64)
	if err != nil {
		log.Printf("[LoadFloatCache]Error parsing float from string: %v\n", err)
		return 0
	}
	return val_f
}

func StoreFloatCache(client *redis.Client, key string, floatNumber float64) bool {
	numberStr := strconv.FormatFloat(floatNumber, 'f', -1, 64)
	expiration := 7 * 24 * time.Hour
	err := client.Set(context.Background(), key, numberStr, expiration).Err()
	if err != nil {
		log.Printf("[StoreFloatCache]Error storing data in Redis: %s, %v\n", key, err)
		return false
	}
	return true
}

func LoadStringCache(client *redis.Client, keyStr string) string {
	val, err := client.Get(context.Background(), keyStr).Result()
	if err != nil {
		log.Printf("[LoadStringCache]Error retrieving data from Redis: %s, %v\n", keyStr, err)
		return ""
	}
	return val
}

func StoreStringCache(client *redis.Client, key string, strValue string) bool {
	expiration := 7 * 24 * time.Hour
	err := client.Set(context.Background(), key, strValue, expiration).Err()
	if err != nil {
		log.Printf("[StoreStringCache]Error storing data in Redis: %s, %v\n", key, err)
		return false
	}
	return true
}

func LoadTimeCache(client *redis.Client, keyTime string) (time.Time, bool) {
	val, err := client.Get(context.Background(), keyTime).Result()
	if err != nil {
		log.Printf("[LoadTimeCache]Error retrieving data from Redis: %s, %v\n", keyTime, err)
		return time.Time{}, false
	}

	t, err := time.Parse(time.RFC3339, val)
	if err != nil {
		log.Printf("[LoadTimeCache]Error parsing time from string: %v\n", err)
		return time.Time{}, false
	}

	return t, true
}

func StoreTimeCache(client *redis.Client, key string, timeValue time.Time) bool {
	numberStr := timeValue.Format(time.RFC3339)
	expiration := 7 * 24 * time.Hour
	err := client.Set(context.Background(), key, numberStr, expiration).Err()
	if err != nil {
		log.Printf("[StoreTimeCache]Error storing data in Redis: %s, %v\n", key, err)
		return false
	}
	return true
}

func LoadPoolCache(client *redis.Client, coin string, pool_key string) interface{} {
	k := "pool_" + coin + "_" + pool_key
	switch pool_key {
	case "submits", "blocks", "rejects":
		return LoadIntCache(client, k)
	case "accepts", "rewards", "fee", "refdiff":
		return LoadFloatCache(client, k)
	default:
		log.Printf("[LoadPoolCache]Unknown pool_key: %s\n", pool_key)
		return nil
	}
}

func StorePoolCache(client *redis.Client, coin string, pool_key string, val interface{}) bool {
	k := "pool_" + coin + "_" + pool_key

	switch pool_key {
	case "submits", "blocks", "rejects":
		if pool_key == "rejects" {
			if intVal, ok := val.(float64); ok {
				return StoreIntCache(client, k, int64(intVal))
			}
		} else {
			if intVal, ok := val.(int64); ok {
				return StoreIntCache(client, k, intVal)
			}
		}
		log.Printf("[StorePoolCache]Invalid type for key %s: expected int64, got %T\n", pool_key, val)
		return false
	case "accepts", "rewards", "fee", "refdiff":
		if floatVal, ok := val.(float64); ok {
			return StoreFloatCache(client, k, floatVal)
		}
		log.Printf("[StorePoolCache]Invalid type for key %s: expected float64, got %T\n", pool_key, val)
		return false
	default:
		log.Printf("[StorePoolCache]Unknown pool_key: %s\n", pool_key)
		return false
	}
}

/*
func LoadUserCache(client *redis.Client, coin string, user string, pool_key string) interface{} {
	k := "pool_" + coin + "_" + user + "_" + pool_key
	switch pool_key {
	case "submits", "blocks", "rejects":
		return LoadIntCache(client, k)
	case "accepts", "rewards", "fee":
		return LoadFloatCache(client, k)
	default:
		log.Printf("Unknown pool_key: %s\n", pool_key)
		return nil
	}
}

func StoreUserCache(client *redis.Client, coin string, user string, pool_key string, val interface{}) bool {
	k := "pool_" + coin + "_" + user + "_" + pool_key

	switch pool_key {
	case "submits", "blocks", "rejects":
		if intVal, ok := val.(int64); ok {
			return StoreIntCache(client, k, intVal)
		}
		log.Printf("Invalid type for key %s: expected int64, got %T\n", pool_key, val)
		return false
	case "accepts", "rewards", "fee":
		if floatVal, ok := val.(float64); ok {
			return StoreFloatCache(client, k, floatVal)
		}
		log.Printf("Invalid type for key %s: expected float64, got %T\n", pool_key, val)
		return false
	default:
		log.Printf("Unknown pool_key: %s\n", pool_key)
		return false
	}
}*/

func LoadMinerCache(client *redis.Client, coin string, user string, miner string, index string, pool_key string) interface{} {
	k := "pool_" + coin + "_" + user + "_" + miner + "_" + index + "_" + pool_key
	switch pool_key {
	case "submits", "blocks", "rejects", "retry", "staleds", "lowdiffs", "duplicates", "formats", "others":
		return LoadIntCache(client, k)
	case "accepts", "rewards", "fee", "diff":
		return LoadFloatCache(client, k)
	case "lastsubmit", "startsubmit":
		tT, ok := LoadTimeCache(client, k)
		if ok {
			return tT
		}
		return nil
	default:
		log.Printf("[LoadMinerCache]Unknown pool_key: %s\n", pool_key)
		return nil
	}
}

func StoreMinerCache(client *redis.Client, coin string, user string, miner string, index string, pool_key string, val interface{}) bool {
	k := "pool_" + coin + "_" + user + "_" + miner + "_" + index + "_" + pool_key

	switch pool_key {
	case "submits", "blocks", "rejects", "retry", "staleds", "lowdiffs", "duplicates", "formats", "others":
		if intVal, ok := val.(int64); ok {
			return StoreIntCache(client, k, intVal)
		}
		log.Printf("[StoreMinerCache]Invalid type for %s: expected int64, got %T\n", pool_key, val)
		return false

	case "accepts", "rewards", "fee", "diff":
		if floatVal, ok := val.(float64); ok {
			return StoreFloatCache(client, k, floatVal)
		}
		log.Printf("[StoreMinerCache]Invalid type for %s: expected float64, got %T\n", pool_key, val)
		return false

	case "lastsubmit", "startsubmit":
		if timeVal, ok := val.(time.Time); ok {
			return StoreTimeCache(client, k, timeVal)
		}
		log.Printf("[StoreMinerCache]Invalid type for %s: expected time.Time, got %T\n", pool_key, val)
		return false

	default:
		log.Printf("[StoreMinerCache]Unknown pool_key: %s\n", pool_key)
		return false
	}
}

func StoreCacheMhsItem(client *redis.Client, key string, item CacheMhsItem) bool {
	data, err := json.Marshal(item)
	if err != nil {
		log.Printf("[StoreCacheMhsItem]Error marshalling CacheMhsItem to JSON: %v\n", err)
		return false
	}

	err = client.RPush(context.Background(), key, data).Err()
	if err != nil {
		log.Printf("[StoreCacheMhsItem]Error pushing data to Redis list: %v\n", err)
		return false
	}

	expiration := 7 * 24 * time.Hour
	err = client.Expire(context.Background(), key, expiration).Err()
	if err != nil {
		log.Printf("[StoreCacheMhsItem] Error setting expiration for Redis key: %v\n", err)
		return false
	}

	return true
}

func PopCacheMhsItem(client *redis.Client, key string) (*CacheMhsItem, bool) {
	data, err := client.LPop(context.Background(), key).Result()
	if err != nil {
		log.Printf("[PopCacheMhsItem]Error retrieving data from Redis list: %v\n", err)
		return nil, false
	}

	var item CacheMhsItem
	if err = json.Unmarshal([]byte(data), &item); err != nil {
		log.Printf("[PopCacheMhsItem]Error unmarshalling JSON to CacheMhsItem: %v\n", err)
		return nil, false
	}

	return &item, true
}

func RemoveMhsCache(client *redis.Client, coin string, user string, miner string, index string, key string) bool {
	k := "mhs_" + coin + "_" + user + "_" + miner + "_" + index + "_" + key
	switch key {
	case "accepts", "rejects":
		PopCacheMhsItem(client, k)
		return true
	}
	return false
}

func LoadMhsCache(client *redis.Client, coin string, user string, miner string, index string, key string) interface{} {
	k := "mhs_" + coin + "_" + user + "_" + miner + "_" + index + "_" + key
	switch key {
	case "starttime":
		tT, ok := LoadTimeCache(client, k)
		if ok {
			return tT
		}
		return nil
	case "accepts", "rejects":
		/*listLength, err := client.LLen(context.Background(), k).Result()
		if err != nil {
			log.Printf("Error getting list length: %v\n", err)
			return nil
		}*/
		values, err := client.LRange(context.Background(), k, 0, -1).Result()
		if err != nil {
			log.Printf("[LoadMhsCache]Error getting list values: %v\n", err)
		}
		var items []CacheMhsItem
		for _, value := range values {
			var item CacheMhsItem
			if err = json.Unmarshal([]byte(value), &item); err != nil {
				continue
			}
			items = append(items, item)
		}
		return &items
	default:
		return nil
	}
}

func StoreMhsCache(client *redis.Client, coin string, user string, miner string, index string, key string, val interface{}) bool {
	k := "mhs_" + coin + "_" + user + "_" + miner + "_" + index + "_" + key
	switch key {
	case "starttime":
		if t, ok := val.(time.Time); ok {
			return StoreTimeCache(client, k, t)
		}
		log.Printf("[StoreMhsCache]Invalid type for starttime: expected time.Time, got %T\n", val)
		return false
	case "accepts", "rejects":
		if item, ok := val.(CacheMhsItem); ok {
			return StoreCacheMhsItem(client, k, item)
		}
		log.Printf("[StoreMhsCache]Invalid type for CacheMhsItem: expected CacheMhsItem, got %T\n", val)
		return false
	default:
		log.Printf("[StoreMhsCache]Unknown key: %s\n", key)
		return false
	}
}

/*
func StoreStringToSet(client *redis.Client, setKey string, value string) bool {
	err := client.SAdd(context.Background(), setKey, value).Err()
	if err != nil {
		log.Printf("Error adding value to Redis set: %v\n", err)
		return false
	}
	return true
}

func LoadStringsFromSet(client *redis.Client, setKey string) ([]string, bool) {
	members, err := client.SMembers(context.Background(), setKey).Result()
	if err != nil {
		log.Printf("Error retrieving data from Redis set: %v\n", err)
		return nil, false
	}
	return members, true
}

func RemoveStringFromSet(client *redis.Client, setKey string, value string) bool {
	err := client.SRem(context.Background(), setKey, value).Err()
	if err != nil {
		log.Printf("Error removing value from Redis set: %v\n", err)
		return false
	}
	return true
}

func LoadUsersCache(client *redis.Client, coin string) []string {
	k := "pool_" + coin + "_users"
	users, ok := LoadStringsFromSet(client, k)
	if !ok {
		log.Printf("Error loading users slice from Redis for coin: %s\n", coin)
		return []string{}
	}
	return users
}

func StoreUsersCache(client *redis.Client, coin string, user string) bool {
	k := "pool_" + coin + "_users"
	ok := StoreStringToSet(client, k, user)
	if !ok {
		log.Printf("Error storing users slice to Redis for coin: %s\n", coin)
		return false
	}
	return true
}
*/
/*
func StoreCacheMinerItem(client *redis.Client, setKey string, item CacheMinerItem) bool {
	data, err := json.Marshal(item)
	if err != nil {
		log.Printf("Error marshalling CacheMinerItem to JSON: %v\n", err)
		return false
	}

	err = client.SAdd(context.Background(), setKey, data).Err()
	if err != nil {
		log.Printf("Error adding data to Redis set: %v\n", err)
		return false
	}

	return true
}

func LoadCacheMinerItems(client *redis.Client, setKey string) ([]CacheMinerItem, bool) {
	data, err := client.SMembers(context.Background(), setKey).Result()
	if err != nil {
		log.Printf("Error retrieving data from Redis set: %v\n", err)
		return nil, false
	}

	var items []CacheMinerItem
	for _, itemData := range data {
		var item CacheMinerItem
		err := json.Unmarshal([]byte(itemData), &item)
		if err != nil {
			log.Printf("Error unmarshalling JSON to CacheMinerItem: %v\n", err)
			return nil, false
		}
		items = append(items, item)
	}

	return items, true
}

func RemoveCacheMinerItem(client *redis.Client, setKey string, item CacheMinerItem) bool {

	data, err := json.Marshal(item)
	if err != nil {
		log.Printf("Error marshalling CacheMinerItem to JSON: %v\n", err)
		return false
	}

	err = client.SRem(context.Background(), setKey, data).Err()
	if err != nil {
		log.Printf("Error removing data from Redis set: %v\n", err)
		return false
	}

	return true
}

func LoadMinersCache(client *redis.Client, coin string) []CacheMinerItem {
	k := "pool_" + coin + "_miners"
	miners, ok := LoadCacheMinerItems(client, k)
	if !ok {
		log.Printf("Error loading miners set from Redis for coin: %s\n", coin)
		return []CacheMinerItem{}
	}
	return miners
}

func StoreMinersCache(client *redis.Client, coin string, miner CacheMinerItem) bool {
	k := "pool_" + coin + "_miners"
	ok := StoreCacheMinerItem(client, k, miner)
	if !ok {
		log.Printf("Error storing miners slice to Redis for coin: %s\n", coin)
		return false
	}
	return true
}
*/