// Package store wraps Redis and Postgres. M0 ships Redis only; the
// Postgres layer lands with M2 (recipient resolution).
//
// Redis is used for:
//   - dedupe (SET NX EX, INCR) – SPEC §5
//   - rate limiting (INCR + EXPIRE, or redis-cell) – SPEC §22 layer 3/4
//   - per-source quarantine (local map, see ingestd) – SPEC §22 layer 7
package store

import (
	"context"
	"fmt"
	"time"

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

// Redis is a thin wrapper to keep construction in one place.
type Redis struct {
	*redis.Client
}

// ConnectRedis dials Redis. Two retries with backoff.
func ConnectRedis(ctx context.Context, url string) (*Redis, error) {
	opts, err := redis.ParseURL(url)
	if err != nil {
		return nil, fmt.Errorf("parse redis url %q: %w", url, err)
	}
	c := redis.NewClient(opts)
	pingCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
	defer cancel()
	if err := c.Ping(pingCtx).Err(); err != nil {
		return nil, fmt.Errorf("redis ping: %w", err)
	}
	return &Redis{c}, nil
}

// IsConnRefused reports whether err looks like Redis being down.
func IsConnRefused(err error) bool {
	return err != nil && (contains(err.Error(), "connection refused") || contains(err.Error(), "EOF"))
}

func contains(s, sub string) bool {
	for i := 0; i+len(sub) <= len(s); i++ {
		if s[i:i+len(sub)] == sub {
			return true
		}
	}
	return false
}
