package connector_throttle

import (
	"context"
	"database/sql"
	"fmt"
	"strings"
)

// LimitProvider looks up the rate-limit policy for a (namespace, endpointClass)
// pair. The only authoritative source in v1 is the connector-pack manifest,
// materialised by the pack-installer into _System_ConnectorRateLimit keyed by
// (OwnerPack, EndpointClass).
type LimitProvider interface {
	// LimitFor returns (rps, burst, true, nil) when an authoritative policy
	// exists for the given namespace+endpointClass pair, or (0, 0, false, nil)
	// when none exists (caller applies the documented default fallback).
	LimitFor(ctx context.Context, namespace, endpointClass string) (rps float64, burst int, ok bool, err error)
}

// sqlLimitProvider reads _System_ConnectorRateLimit rows directly via
// database/sql. No ORM or SObject query path — the table is BLOCKED from
// generic CRUD and this is the authoritative read path.
type sqlLimitProvider struct {
	db *sql.DB
}

// NewSQLLimitProvider returns a LimitProvider backed by _System_ConnectorRateLimit.
func NewSQLLimitProvider(db *sql.DB) LimitProvider {
	return &sqlLimitProvider{db: db}
}

func (p *sqlLimitProvider) LimitFor(ctx context.Context, namespace, endpointClass string) (float64, int, bool, error) {
	// _System_ConnectorRateLimit stores OwnerPack and EndpointClass lower-trimmed
	// (connector_rate_limit_component.go). Normalize the lookup keys the same way
	// so a mixed-case connector pack namespace ("Acme-Connector") or endpointClass
	// variant matches the stored row instead of silently falling back to default
	// quota — which would reintroduce a pack-metadata SSoT bypass (codex Review
	// PR #1537). endpointClass is already normalized by Throttle, but normalize
	// here too so this SQL reader is correct for any direct caller.
	namespace = strings.ToLower(strings.TrimSpace(namespace))
	endpointClass = strings.ToLower(strings.TrimSpace(endpointClass))
	var rps float64
	var burst int
	err := p.db.QueryRowContext(ctx, `
SELECT RequestsPerSecond, Burst
FROM _System_ConnectorRateLimit
WHERE OwnerPack = ? AND EndpointClass = ? AND COALESCE(IsDeleted, 0) = 0
LIMIT 1`, namespace, endpointClass).Scan(&rps, &burst)
	if err == sql.ErrNoRows {
		return 0, 0, false, nil
	}
	if err != nil {
		return 0, 0, false, fmt.Errorf("connector throttle: query limit %s/%s: %w", namespace, endpointClass, err)
	}
	return rps, burst, true, nil
}

// NoopLimitProvider always returns ok=false so callers apply the documented
// default fallback. Used in tests and as a safe fallback when no DB is wired.
type NoopLimitProvider struct{}

// NewNoopLimitProvider returns the documented-default-fallback LimitProvider.
func NewNoopLimitProvider() LimitProvider {
	return NoopLimitProvider{}
}

// LimitFor always returns ok=false; throttler.Service applies DefaultRate /
// DefaultBurst from its Config.
func (NoopLimitProvider) LimitFor(_ context.Context, _ string, _ string) (float64, int, bool, error) {
	return 0, 0, false, nil
}
