package ratelimit

import (
	"context"
	"database/sql"
	"errors"
	"fmt"

	"go.uber.org/zap"
)

// ErrNotFound is returned when no config exists for the requested tenant.
var ErrNotFound = errors.New("rate limit config not found")

// Store persists per-tenant rate limit configurations in PostgreSQL.
type Store struct {
	db     *sql.DB
	logger *zap.Logger
}

// NewStore creates a new PostgreSQL-backed rate limit store.
func NewStore(db *sql.DB, logger *zap.Logger) *Store {
	return &Store{db: db, logger: logger}
}

// List returns all per-tenant rate limit configurations.
func (s *Store) List(ctx context.Context) ([]RateLimitConfig, error) {
	rows, err := s.db.QueryContext(ctx,
		`SELECT tenant_id, requests_per_min, burst_size, user_rpm, user_burst, is_enabled
		 FROM rate_limit_configs ORDER BY tenant_id`)
	if err != nil {
		return nil, fmt.Errorf("ratelimit: list: %w", err)
	}
	defer rows.Close()

	var cfgs []RateLimitConfig
	for rows.Next() {
		var c RateLimitConfig
		if err := rows.Scan(&c.TenantID, &c.RequestsPerMin, &c.BurstSize,
			&c.UserRPM, &c.UserBurst, &c.IsEnabled); err != nil {
			return nil, fmt.Errorf("ratelimit: scan: %w", err)
		}
		cfgs = append(cfgs, c)
	}
	return cfgs, rows.Err()
}

// Get returns the config for a specific tenant or ErrNotFound.
func (s *Store) Get(ctx context.Context, tenantID string) (RateLimitConfig, error) {
	var c RateLimitConfig
	err := s.db.QueryRowContext(ctx,
		`SELECT tenant_id, requests_per_min, burst_size, user_rpm, user_burst, is_enabled
		 FROM rate_limit_configs WHERE tenant_id = $1`, tenantID).
		Scan(&c.TenantID, &c.RequestsPerMin, &c.BurstSize, &c.UserRPM, &c.UserBurst, &c.IsEnabled)
	if errors.Is(err, sql.ErrNoRows) {
		return RateLimitConfig{}, ErrNotFound
	}
	if err != nil {
		return RateLimitConfig{}, fmt.Errorf("ratelimit: get: %w", err)
	}
	return c, nil
}

// Upsert creates or updates the rate limit config for a tenant.
func (s *Store) Upsert(ctx context.Context, cfg RateLimitConfig) (RateLimitConfig, error) {
	var out RateLimitConfig
	err := s.db.QueryRowContext(ctx, `
		INSERT INTO rate_limit_configs
			(tenant_id, requests_per_min, burst_size, user_rpm, user_burst, is_enabled, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, NOW())
		ON CONFLICT (tenant_id) DO UPDATE SET
			requests_per_min = EXCLUDED.requests_per_min,
			burst_size       = EXCLUDED.burst_size,
			user_rpm         = EXCLUDED.user_rpm,
			user_burst       = EXCLUDED.user_burst,
			is_enabled       = EXCLUDED.is_enabled,
			updated_at       = NOW()
		RETURNING tenant_id, requests_per_min, burst_size, user_rpm, user_burst, is_enabled`,
		cfg.TenantID, cfg.RequestsPerMin, cfg.BurstSize,
		cfg.UserRPM, cfg.UserBurst, cfg.IsEnabled,
	).Scan(&out.TenantID, &out.RequestsPerMin, &out.BurstSize,
		&out.UserRPM, &out.UserBurst, &out.IsEnabled)
	if err != nil {
		return RateLimitConfig{}, fmt.Errorf("ratelimit: upsert: %w", err)
	}
	return out, nil
}

// Delete removes the per-tenant override. Returns ErrNotFound if absent.
func (s *Store) Delete(ctx context.Context, tenantID string) error {
	res, err := s.db.ExecContext(ctx,
		`DELETE FROM rate_limit_configs WHERE tenant_id = $1`, tenantID)
	if err != nil {
		return fmt.Errorf("ratelimit: delete: %w", err)
	}
	n, _ := res.RowsAffected()
	if n == 0 {
		return ErrNotFound
	}
	return nil
}
