package cache

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/config"
)

// silentRedisLogger filters out noisy "maintnotifications" warnings from go-redis
// This warning occurs when the Redis client tries to use newer Redis 7.2+ features
// that may not be fully supported by the current Redis version.
// The client automatically falls back to compatible mode, so this is harmless.
type silentRedisLogger struct {
	logger *zap.Logger
}

func (l *silentRedisLogger) Printf(ctx context.Context, format string, v ...interface{}) {
	msg := fmt.Sprintf(format, v...)

	// Filter out harmless compatibility warnings
	if strings.Contains(msg, "maintnotifications disabled") ||
		strings.Contains(msg, "auto mode fallback") {
		return
	}

	// Log other Redis messages at debug level
	l.logger.Debug(msg)
}

// RedisCacher defines the interface for Redis cache operations
type RedisCacher interface {
	Shutdown(ctx context.Context)
	Get(ctx context.Context, key string) ([]byte, error)
	Set(ctx context.Context, key string, val []byte) error
	SetWithExpiry(ctx context.Context, key string, val []byte, expiry time.Duration) error
	Delete(ctx context.Context, key string) error
}

type redisCache struct {
	client *redis.Client
	logger *zap.Logger
}

// NewRedisCache creates a new Redis cache instance
func NewRedisCache(cfg *config.Config, logger *zap.Logger) (RedisCacher, error) {
	logger = logger.Named("redis-cache")

	logger.Info("⏳ Connecting to Redis...",
		zap.String("host", cfg.Cache.Host),
		zap.Int("port", cfg.Cache.Port))

	// Build Redis URL from config
	redisURL := fmt.Sprintf("redis://:%s@%s:%d/%d",
		cfg.Cache.Password,
		cfg.Cache.Host,
		cfg.Cache.Port,
		cfg.Cache.DB,
	)

	// If no password, use simpler URL format
	if cfg.Cache.Password == "" {
		redisURL = fmt.Sprintf("redis://%s:%d/%d",
			cfg.Cache.Host,
			cfg.Cache.Port,
			cfg.Cache.DB,
		)
	}

	opt, err := redis.ParseURL(redisURL)
	if err != nil {
		return nil, fmt.Errorf("failed to parse Redis URL: %w", err)
	}

	// Suppress noisy "maintnotifications" warnings from go-redis
	// Use a custom logger that filters out these harmless compatibility warnings
	redis.SetLogger(&silentRedisLogger{logger: logger.Named("redis-client")})

	client := redis.NewClient(opt)

	// Test connection
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if _, err = client.Ping(ctx).Result(); err != nil {
		return nil, fmt.Errorf("failed to connect to Redis: %w", err)
	}

	logger.Info("✓ Redis connected",
		zap.String("host", cfg.Cache.Host),
		zap.Int("port", cfg.Cache.Port),
		zap.Int("db", cfg.Cache.DB))

	return &redisCache{
		client: client,
		logger: logger,
	}, nil
}

func (c *redisCache) Shutdown(ctx context.Context) {
	c.logger.Info("shutting down Redis cache")
	if err := c.client.Close(); err != nil {
		c.logger.Error("error closing Redis connection", zap.Error(err))
	}
}

func (c *redisCache) Get(ctx context.Context, key string) ([]byte, error) {
	val, err := c.client.Get(ctx, key).Result()
	if errors.Is(err, redis.Nil) {
		// Key doesn't exist - this is not an error
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("redis get failed: %w", err)
	}
	return []byte(val), nil
}

func (c *redisCache) Set(ctx context.Context, key string, val []byte) error {
	if err := c.client.Set(ctx, key, val, 0).Err(); err != nil {
		return fmt.Errorf("redis set failed: %w", err)
	}
	return nil
}

func (c *redisCache) SetWithExpiry(ctx context.Context, key string, val []byte, expiry time.Duration) error {
	if err := c.client.Set(ctx, key, val, expiry).Err(); err != nil {
		return fmt.Errorf("redis set with expiry failed: %w", err)
	}
	return nil
}

func (c *redisCache) Delete(ctx context.Context, key string) error {
	if err := c.client.Del(ctx, key).Err(); err != nil {
		return fmt.Errorf("redis delete failed: %w", err)
	}
	return nil
}
