package core

import (
	"context"
	"fmt"
	"os"
	"strconv"
	"time"

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

var ctx context.Context

type Cache struct {
	redis *redis.Client
}

// NewCache initializes a new Cache instance with the provided configuration and connects to a Redis database.
// If caching is enabled in the provided configuration but the connection to Redis fails, it causes a panic.
// Returns a pointer to the initialized Cache structure.
func NewCache(cacheConfig CacheConfig) *Cache {
	ctx = context.Background()
	dbStr := os.Getenv("REDIS_DB")
	db64, err := strconv.ParseInt(dbStr, 10, 64)
	if err != nil {
		panic(fmt.Sprintf("error parsing redis db env var: %v", err))
	}
	db := int(db64)
	rdb := redis.NewClient(&redis.Options{
		Addr:     fmt.Sprintf("%v:%v", os.Getenv("REDIS_HOST"), os.Getenv("REDIS_PORT")),
		Password: os.Getenv("REDIS_PASSWORD"), // no password set
		DB:       db,                          // use default DB
	})

	_, err = rdb.Ping(ctx).Result()
	if cacheConfig.EnableCache && err != nil {
		panic(fmt.Sprintf("problem connecting to redis cache, (if it's not needed you can disable it in config/cache.go): %v", err))
	}

	return &Cache{
		redis: rdb,
	}
}

// Set stores a key-value pair in the Redis cache with no expiration. Returns an error if the operation fails.
func (c *Cache) Set(key string, value string) error {
	err := c.redis.Set(ctx, key, value, 0).Err()
	if err != nil {
		return err
	}
	return nil
}

// SetWithExpiration stores a key-value pair in the cache with a specified expiration duration.
// Returns an error if the operation fails.
func (c *Cache) SetWithExpiration(key string, value string, expiration time.Duration) error {
	err := c.redis.Set(ctx, key, value, expiration).Err()
	if err != nil {
		return err
	}
	return nil
}

// Get retrieves the value associated with the provided key from the cache. Returns an error if the operation fails.
func (c *Cache) Get(key string) (string, error) {
	result, err := c.redis.Get(ctx, key).Result()
	if err != nil {
		return "", err
	}
	return result, nil
}

// Delete removes the specified key from the cache and returns an error if the operation fails.
func (c *Cache) Delete(key string) error {
	err := c.redis.Del(ctx, key).Err()
	if err != nil {
		return err
	}
	return nil
}