/**
 * @fileoverview Rate limiting utilities for Supabase Edge Functions
 * @description Postgres-based rate limiting via an atomic PostgreSQL function (RPC).
 *
 * **Why RPC?**
 * The previous SELECT → UPDATE pattern had a TOCTOU race condition: two concurrent
 * requests could both read `attempts < maxAttempts`, both increment, and both be allowed —
 * effectively bypassing the rate limit under load. The PostgreSQL function executes as a
 * single atomic transaction, eliminating the race.
 *
 * **Required migration:**
 * Deploy `RATE_LIMIT_CHECK_SQL` to your Supabase project before using this function.
 * Run: `supabase migration new rate_limit_check` then paste the SQL into the file.
 *
 * **Fail behaviour:**
 * On RPC error or unexpected failure, this function FAILS CLOSED (blocks the request).
 * A broken rate limiter must not silently allow unlimited access to protected resources.
 *
 * @version 0.1.0
 * @since 0.5.0
 * @author AMBROISE PARK Consulting
 */
import type { ServerRateLimitConfig as RateLimitConfig, ServerRateLimitResult as RateLimitResult } from '@donotdev/core';
import type { SupabaseClient } from '@supabase/supabase-js';
export type { RateLimitConfig, RateLimitResult };
/**
 * PostgreSQL function required for atomic rate limiting.
 * Deploy this SQL to your Supabase project via a migration.
 *
 * The function uses `INSERT … ON CONFLICT DO UPDATE` which executes as a single
 * atomic statement — no TOCTOU race between SELECT and UPDATE.
 *
 * @example
 * ```bash
 * supabase migration new rate_limit_check
 * # Paste RATE_LIMIT_CHECK_SQL into the generated migration file
 * supabase db push
 * ```
 */
export declare const RATE_LIMIT_CHECK_SQL = "\n-- Required table (create once if not already present)\nCREATE TABLE IF NOT EXISTS public.rate_limits (\n  key          TEXT PRIMARY KEY,\n  attempts     INT           NOT NULL DEFAULT 0,\n  window_start TIMESTAMPTZ   NOT NULL DEFAULT now(),\n  block_until  TIMESTAMPTZ,\n  last_updated TIMESTAMPTZ   NOT NULL DEFAULT now()\n);\n\n-- Required atomic rate-limit function\nCREATE OR REPLACE FUNCTION public.rate_limit_check(\n  p_key              TEXT,\n  p_max_attempts     INT,\n  p_window_ms        BIGINT,\n  p_block_duration_ms BIGINT\n)\nRETURNS jsonb\nLANGUAGE plpgsql\nSECURITY DEFINER\nAS $$\nDECLARE\n  v_now             TIMESTAMPTZ := clock_timestamp();\n  v_window_interval INTERVAL    := (p_window_ms || ' milliseconds')::INTERVAL;\n  v_block_interval  INTERVAL    := (p_block_duration_ms || ' milliseconds')::INTERVAL;\n  v_row             public.rate_limits%ROWTYPE;\nBEGIN\n  -- Single atomic upsert: INSERT on first call, conditional UPDATE thereafter.\n  -- No separate SELECT \u2192 eliminates the TOCTOU race of the previous implementation.\n  INSERT INTO public.rate_limits (key, attempts, window_start, block_until, last_updated)\n  VALUES (p_key, 1, v_now, NULL, v_now)\n  ON CONFLICT (key) DO UPDATE SET\n    window_start = CASE\n      WHEN rate_limits.window_start + v_window_interval <= v_now THEN v_now\n      ELSE rate_limits.window_start\n    END,\n    attempts = CASE\n      -- Currently blocked: do not increment (request will be rejected below)\n      WHEN rate_limits.block_until IS NOT NULL AND v_now < rate_limits.block_until\n        THEN rate_limits.attempts\n      -- Window expired: reset counter\n      WHEN rate_limits.window_start + v_window_interval <= v_now\n        THEN 1\n      -- Within window: increment\n      ELSE rate_limits.attempts + 1\n    END,\n    block_until = CASE\n      -- Preserve existing block\n      WHEN rate_limits.block_until IS NOT NULL AND v_now < rate_limits.block_until\n        THEN rate_limits.block_until\n      -- Window just reset \u2014 no block\n      WHEN rate_limits.window_start + v_window_interval <= v_now\n        THEN NULL\n      -- Threshold just exceeded \u2014 apply block\n      WHEN rate_limits.attempts + 1 >= p_max_attempts\n        THEN v_now + v_block_interval\n      ELSE NULL\n    END,\n    last_updated = v_now\n  RETURNING * INTO v_row;\n\n  -- Blocked?\n  IF v_row.block_until IS NOT NULL AND v_now < v_row.block_until THEN\n    RETURN jsonb_build_object(\n      'allowed',                   false,\n      'remaining',                 0,\n      'reset_at_epoch_ms',         EXTRACT(EPOCH FROM v_row.block_until)::BIGINT * 1000,\n      'block_remaining_seconds',   CEIL(EXTRACT(EPOCH FROM (v_row.block_until - v_now)))::INT\n    );\n  END IF;\n\n  RETURN jsonb_build_object(\n    'allowed',                 true,\n    'remaining',               GREATEST(0, p_max_attempts - v_row.attempts),\n    'reset_at_epoch_ms',       EXTRACT(EPOCH FROM (v_row.window_start + v_window_interval))::BIGINT * 1000,\n    'block_remaining_seconds', NULL\n  );\nEND;\n$$;\n";
/**
 * Default rate limits (same as Firebase equivalent).
 */
export declare const DEFAULT_RATE_LIMITS: Record<string, RateLimitConfig>;
/**
 * Atomically check and increment a rate limit counter using the `rate_limit_check`
 * PostgreSQL function. Requires the function to be deployed (see `RATE_LIMIT_CHECK_SQL`).
 *
 * **Fail behaviour:** FAILS CLOSED on any error — a broken rate limiter must not
 * allow unlimited access. Log the error and alert your team; do not suppress it.
 *
 * @param supabaseAdmin - Supabase admin client (service role key — never expose to clients)
 * @param key           - Rate limit key, e.g. `"create_userId"` or `"api_ip"`
 * @param config        - Rate limit configuration
 */
export declare function checkRateLimitWithPostgres(supabaseAdmin: SupabaseClient, key: string, config: RateLimitConfig): Promise<RateLimitResult>;
