package lib

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

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

// RedisStorage implements a Redis-backed caching layer for test data.
// It provides fast access to frequently requested data while reducing database load.
type RedisStorage struct {
    client *redis.Client
}

// NewRedisStorage creates and initializes a new Redis connection with the provided URL.
// It verifies the connection and configures default timeouts.
func NewRedisStorage(url string) (*RedisStorage, error) {
    opt, err := redis.ParseURL(url)
    if err != nil {
        return nil, fmt.Errorf("failed to parse Redis URL: %v", err)
    }

    client := redis.NewClient(opt)

    // Verify connection is working
    ctx := context.Background()
    if err := client.Ping(ctx).Err(); err != nil {
        return nil, fmt.Errorf("failed to connect to Redis: %v", err)
    }

    log.Printf("Successfully connected to Redis")
    return &RedisStorage{client: client}, nil
}

// GetTestData retrieves cached test data if available.
// Returns (nil, redis.Nil) if key doesn't exist.
func (s *RedisStorage) GetTestData(ctx context.Context) (*TestData, error) {
    data, err := s.client.Get(ctx, "test_data").Bytes()
    if err != nil {
        if err == redis.Nil {
            log.Printf("Redis: Cache miss - key not found")
        } else {
            log.Printf("Redis: Error retrieving data: %v", err)
        }
        return nil, err
    }

    var testData TestData
    if err := json.Unmarshal(data, &testData); err != nil {
        log.Printf("Redis: Error deserializing cached data: %v", err)
        return nil, err
    }

    log.Printf("Redis: Cache hit - retrieved data: %+v", testData)
    return &testData, nil
}

// SaveTestData caches the provided test data with a 1-hour TTL.
// Existing data for the same key will be overwritten.
func (s *RedisStorage) SaveTestData(ctx context.Context, data *TestData) error {
    jsonData, err := json.Marshal(data)
    if err != nil {
        log.Printf("Redis: Error serializing data: %v", err)
        return err
    }

    err = s.client.Set(ctx, "test_data", jsonData, 1*time.Hour).Err()
    if err != nil {
        log.Printf("Redis: Error writing to cache: %v", err)
        return err
    }

    log.Printf("Redis: Successfully cached data: %+v", data)
    return nil
}

// InvalidateTestData removes the test data from cache.
// This is typically called when the underlying data is updated.
func (s *RedisStorage) InvalidateTestData(ctx context.Context) error {
    err := s.client.Del(ctx, "test_data").Err()
    if err != nil {
        log.Printf("Redis: Error invalidating cache: %v", err)
    } else {
        log.Printf("Redis: Successfully invalidated cached data")
    }
    return err
}
