package redisclient

import (
	"context"
	"testing"
	"time"

	"github.com/alicebob/miniredis/v2"
	"github.com/redis/go-redis/v9"
)

func TestRedisConnection(t *testing.T) {
	// Create a miniredis server
	mr, err := miniredis.Run()
	if err != nil {
		t.Fatalf("Failed to start miniredis: %v", err)
	}
	defer mr.Close()

	// Create a test Redis client
	testClient := redis.NewClient(&redis.Options{
		Addr: mr.Addr(),
	})
	defer testClient.Close()

	ctx := context.Background()

	// Test SET
	err = testClient.Set(ctx, "test_key", "test_value", time.Minute).Err()
	if err != nil {
		t.Errorf("Failed to set key: %v", err)
	}

	// Test GET
	val, err := testClient.Get(ctx, "test_key").Result()
	if err != nil {
		t.Errorf("Failed to get key: %v", err)
	}

	if val != "test_value" {
		t.Errorf("Expected 'test_value', got '%s'", val)
	}

	// Test DEL
	err = testClient.Del(ctx, "test_key").Err()
	if err != nil {
		t.Errorf("Failed to delete key: %v", err)
	}

	// Verify key was deleted
	_, err = testClient.Get(ctx, "test_key").Result()
	if err != redis.Nil {
		t.Errorf("Expected redis.Nil error, got: %v", err)
	}
}

func TestRedisExpiry(t *testing.T) {
	mr, err := miniredis.Run()
	if err != nil {
		t.Fatalf("Failed to start miniredis: %v", err)
	}
	defer mr.Close()

	testClient := redis.NewClient(&redis.Options{
		Addr: mr.Addr(),
	})
	defer testClient.Close()

	ctx := context.Background()

	// Set key with TTL
	err = testClient.Set(ctx, "expiring_key", "value", time.Second).Err()
	if err != nil {
		t.Errorf("Failed to set key with TTL: %v", err)
	}

	// Check TTL
	ttl := mr.TTL("expiring_key")
	if ttl <= 0 {
		t.Error("Expected TTL to be set")
	}

	// Fast forward time in miniredis
	mr.FastForward(2 * time.Second)

	// Key should be expired
	_, err = testClient.Get(ctx, "expiring_key").Result()
	if err != redis.Nil {
		t.Errorf("Expected key to be expired, got error: %v", err)
	}
}

func TestRedisIncrement(t *testing.T) {
	mr, err := miniredis.Run()
	if err != nil {
		t.Fatalf("Failed to start miniredis: %v", err)
	}
	defer mr.Close()

	testClient := redis.NewClient(&redis.Options{
		Addr: mr.Addr(),
	})
	defer testClient.Close()

	ctx := context.Background()

	// Test INCR
	val, err := testClient.Incr(ctx, "counter").Result()
	if err != nil {
		t.Errorf("Failed to increment: %v", err)
	}

	if val != 1 {
		t.Errorf("Expected 1, got %d", val)
	}

	// Increment again
	val, err = testClient.Incr(ctx, "counter").Result()
	if err != nil {
		t.Errorf("Failed to increment: %v", err)
	}

	if val != 2 {
		t.Errorf("Expected 2, got %d", val)
	}
}

func TestRedisExists(t *testing.T) {
	mr, err := miniredis.Run()
	if err != nil {
		t.Fatalf("Failed to start miniredis: %v", err)
	}
	defer mr.Close()

	testClient := redis.NewClient(&redis.Options{
		Addr: mr.Addr(),
	})
	defer testClient.Close()

	ctx := context.Background()

	// Key shouldn't exist initially
	exists, err := testClient.Exists(ctx, "nonexistent").Result()
	if err != nil {
		t.Errorf("Failed to check existence: %v", err)
	}

	if exists != 0 {
		t.Errorf("Expected 0, got %d", exists)
	}

	// Set key
	err = testClient.Set(ctx, "existing_key", "value", 0).Err()
	if err != nil {
		t.Errorf("Failed to set key: %v", err)
	}

	// Key should exist now
	exists, err = testClient.Exists(ctx, "existing_key").Result()
	if err != nil {
		t.Errorf("Failed to check existence: %v", err)
	}

	if exists != 1 {
		t.Errorf("Expected 1, got %d", exists)
	}
}

func TestRedisPing(t *testing.T) {
	mr, err := miniredis.Run()
	if err != nil {
		t.Fatalf("Failed to start miniredis: %v", err)
	}
	defer mr.Close()

	testClient := redis.NewClient(&redis.Options{
		Addr: mr.Addr(),
	})
	defer testClient.Close()

	ctx := context.Background()

	// Test PING
	pong, err := testClient.Ping(ctx).Result()
	if err != nil {
		t.Errorf("Failed to ping: %v", err)
	}

	if pong != "PONG" {
		t.Errorf("Expected 'PONG', got '%s'", pong)
	}
}

func TestRedisMultipleKeys(t *testing.T) {
	mr, err := miniredis.Run()
	if err != nil {
		t.Fatalf("Failed to start miniredis: %v", err)
	}
	defer mr.Close()

	testClient := redis.NewClient(&redis.Options{
		Addr: mr.Addr(),
	})
	defer testClient.Close()

	ctx := context.Background()

	// Set multiple keys
	keys := map[string]string{
		"key1": "value1",
		"key2": "value2",
		"key3": "value3",
	}

	for k, v := range keys {
		err := testClient.Set(ctx, k, v, 0).Err()
		if err != nil {
			t.Errorf("Failed to set %s: %v", k, err)
		}
	}

	// Get all keys
	for k, expectedV := range keys {
		val, err := testClient.Get(ctx, k).Result()
		if err != nil {
			t.Errorf("Failed to get %s: %v", k, err)
		}

		if val != expectedV {
			t.Errorf("Expected '%s', got '%s' for key %s", expectedV, val, k)
		}
	}
}
