/**
 * Rate Limiting System for Middleware
 *
 * Provides rate limiting specifically for middleware usage.
 * Primary: Convex rate_limit_windows table (distributed).
 * Fallback: In-memory sliding window (single-instance).
 *
 * Migrated from Upstash Redis to Convex (T15)
 */

import { convexQuery, convexMutation, isConvexConfigured } from "@/lib/convex-client";

// ── In-memory fallback store ─────────────────────────────────────────

const memoryStore = new Map<string, { count: number; reset: number }>();

// Increase limits for development, strict for production
const isDevelopment = process.env.NODE_ENV === "development";

interface RateLimitResult {
  success: boolean;
  limit: number;
  reset: number;
  remaining: number;
}

interface MiddlewareRateLimiter {
  limit: (identifier: string) => Promise<RateLimitResult>;
}

function parseWindow(window: string): number {
  const match = window.match(/(\d+)\s*(s|m|h|d)/);
  if (!match) return 60000;
  const [, value, unit] = match;
  const num = parseInt(value);
  switch (unit) {
    case "s": return num * 1000;
    case "m": return num * 60 * 1000;
    case "h": return num * 60 * 60 * 1000;
    case "d": return num * 24 * 60 * 60 * 1000;
    default: return 60000;
  }
}

function inMemoryLimit(
  key: string,
  requests: number,
  windowMs: number
): RateLimitResult {
  const now = Date.now();
  const record = memoryStore.get(key);

  if (!record || now > record.reset) {
    memoryStore.set(key, { count: 1, reset: now + windowMs });
    return { success: true, limit: requests, reset: now + windowMs, remaining: requests - 1 };
  }

  if (record.count >= requests) {
    return { success: false, limit: requests, reset: record.reset, remaining: 0 };
  }

  record.count++;
  return { success: true, limit: requests, reset: record.reset, remaining: requests - record.count };
}

function createLimiter(
  requests: number,
  window: string,
  prefix: string
): MiddlewareRateLimiter {
  const windowMs = parseWindow(window);
  const limiterType = prefix;

  return {
    limit: async (identifier: string): Promise<RateLimitResult> => {
      // Try Convex first (distributed rate limiting)
      if (isConvexConfigured()) {
        try {
          const result = await convexQuery<{
            allowed: boolean;
            remaining: number;
            resetAt: number;
          }>("rate_limiting/queries:checkRateLimit", {
            key: identifier,
            limiterType,
            limit: requests,
            windowMs,
          });

          if (result) {
            if (result.allowed) {
              await convexMutation(
                "rate_limiting/mutations:incrementRateLimit",
                { key: identifier, limiterType, windowMs }
              );
            }
            return {
              success: result.allowed,
              limit: requests,
              reset: result.resetAt,
              remaining: result.remaining,
            };
          }
        } catch (err) {
          console.error("[RateLimit:MW] Convex error, falling back to in-memory:", err);
        }
      }

      // Fallback to in-memory
      return inMemoryLimit(`${limiterType}:${identifier}`, requests, windowMs);
    },
  };
}

// Rate limiters for different endpoint types
export const otpRateLimiter: MiddlewareRateLimiter = createLimiter(
  isDevelopment ? 50 : 5,
  "15 m",
  "ratelimit:otp"
);

export const authRateLimiter: MiddlewareRateLimiter = createLimiter(
  isDevelopment ? 100 : 20,
  "1 m",
  "ratelimit:auth"
);

// Authenticated routes: lenient catch-all for dashboard/subscription/user/org/staff/admin
// Dashboard loads 8 APIs per page + auto-refresh every 60s = ~480/hr per user
// Behind Cloudflare tunnel, IP-based fallback can hit limits fast
export const authenticatedRateLimiter: MiddlewareRateLimiter = createLimiter(
  isDevelopment ? 5000 : 2000,
  "1 h",
  "ratelimit:authenticated"
);

// Dashboard write operations: per-user rate limiting (300/min)
// Generous limit for legitimate users; prevents abuse from compromised accounts
export const dashboardWriteRateLimiter: MiddlewareRateLimiter = createLimiter(
  isDevelopment ? 1000 : 300,
  "1 m",
  "ratelimit:dashboard_write"
);

// Webhook endpoints without unconditional route-level limiters (Stripe, Twilio).
// Generous per-minute limit to handle legitimate bursts without blocking real deliveries.
export const webhookRateLimiter: MiddlewareRateLimiter = createLimiter(
  isDevelopment ? 500 : 200,
  "1 m",
  "ratelimit:webhook"
);

export const generalRateLimiter: MiddlewareRateLimiter = createLimiter(
  process.env.NODE_ENV === 'development' ? 1000 : 500,
  "1 h",
  "ratelimit:general"
);

/**
 * Whether rate limiting is available (always true with in-memory + Convex).
 * Kept for backward compatibility with middleware imports.
 */
export const requireRateLimiting = true;

/**
 * Get client IP address from request headers.
 * Checks Cloudflare's CF-Connecting-IP first (real client IP behind CF proxy),
 * then falls back to standard proxy headers.
 */
export function getClientIp(request: Request): string {
  const cfIp = request.headers.get("cf-connecting-ip");
  if (cfIp) return cfIp.trim();
  const forwarded = request.headers.get("x-forwarded-for");
  const realIp = request.headers.get("x-real-ip");
  const ip = forwarded?.split(",")[0].trim() || realIp || "127.0.0.1";
  return ip;
}
