import type { Request, Response, NextFunction } from "express";
import { getDb } from "../db.ts";
import { rateLimitLog } from "../../drizzle/schema.ts";
import { and, gt, lte } from "drizzle-orm";

interface RateLimitConfig {
  windowMs: number; // Time window in milliseconds
  maxRequests: number; // Max requests per window
  keyGenerator?: (req: Request) => string; // Custom key generator
  skipSuccessfulRequests?: boolean;
  skipFailedRequests?: boolean;
}

interface RateLimitStore {
  [key: string]: {
    count: number;
    resetTime: number;
  };
}

// In-memory store for rate limiting (in production, use Redis)
const rateLimitStore: RateLimitStore = {};

export function createRateLimiter(config: RateLimitConfig) {
  const {
    windowMs = 15 * 60 * 1000, // 15 minutes default
    maxRequests = 100,
    keyGenerator = (req) => req.ip || "unknown",
    skipSuccessfulRequests = false,
    skipFailedRequests = false,
  } = config;

  return async (req: Request, res: Response, next: NextFunction) => {
    const key = keyGenerator(req);
    const now = Date.now();

    // Initialize or reset if window expired
    if (!rateLimitStore[key] || rateLimitStore[key].resetTime < now) {
      rateLimitStore[key] = {
        count: 0,
        resetTime: now + windowMs,
      };
    }

    // Increment request count
    rateLimitStore[key].count++;

    // Check if limit exceeded
    const isLimited = rateLimitStore[key].count > maxRequests;

    // Log to database
    try {
      const dbInstance = await getDb();
      if (dbInstance) {
        const windowStart = new Date(rateLimitStore[key].resetTime - windowMs);
        const windowEnd = new Date(rateLimitStore[key].resetTime);

        await dbInstance.insert(rateLimitLog).values({
          ipAddress: key,
          endpoint: req.path,
          userId: (req as any).user?.id,
          requestCount: rateLimitStore[key].count,
          windowStart,
          windowEnd,
          isBlocked: isLimited ? 1 : 0,
        });
      }
    } catch (error) {
      console.error("[RateLimit] Error logging to database:", error);
    }

    // Set rate limit headers
    res.setHeader("X-RateLimit-Limit", maxRequests);
    res.setHeader("X-RateLimit-Remaining", Math.max(0, maxRequests - rateLimitStore[key].count));
    res.setHeader("X-RateLimit-Reset", rateLimitStore[key].resetTime.toString());

    if (isLimited) {
      console.warn(`[RateLimit] Rate limit exceeded for ${key} on ${req.path}`);
      return res.status(429).json({
        error: "Too many requests",
        retryAfter: Math.ceil((rateLimitStore[key].resetTime - now) / 1000),
      });
    }

    next();
  };
}

/**
 * Auth-specific rate limiter (stricter limits)
 */
export const authRateLimiter = createRateLimiter({
  windowMs: 15 * 60 * 1000, // 15 minutes
  maxRequests: 5, // 5 attempts per 15 minutes
  keyGenerator: (req) => req.ip || "unknown",
});

/**
 * Login rate limiter
 */
export const loginRateLimiter = createRateLimiter({
  windowMs: 15 * 60 * 1000,
  maxRequests: 5,
  keyGenerator: (req) => {
    const email = (req.body?.email || "unknown").toLowerCase();
    return `${req.ip}:${email}`;
  },
});

/**
 * Magic link rate limiter
 */
export const magicLinkRateLimiter = createRateLimiter({
  windowMs: 60 * 60 * 1000, // 1 hour
  maxRequests: 3, // 3 magic link requests per hour
  keyGenerator: (req) => {
    const email = (req.body?.email || "unknown").toLowerCase();
    return `magic-link:${email}`;
  },
});

/**
 * OAuth rate limiter
 */
export const oauthRateLimiter = createRateLimiter({
  windowMs: 15 * 60 * 1000,
  maxRequests: 10,
  keyGenerator: (req) => {
    const provider = req.params.provider || "unknown";
    return `oauth:${req.ip}:${provider}`;
  },
});

/**
 * 2FA rate limiter
 */
export const twoFactorRateLimiter = createRateLimiter({
  windowMs: 5 * 60 * 1000, // 5 minutes
  maxRequests: 5, // 5 attempts per 5 minutes
  keyGenerator: (req) => {
    const userId = (req as any).user?.id || "unknown";
    return `2fa:${userId}`;
  },
});

/**
 * General API rate limiter
 */
export const apiRateLimiter = createRateLimiter({
  windowMs: 60 * 1000, // 1 minute
  maxRequests: 60, // 60 requests per minute
  keyGenerator: (req) => req.ip || "unknown",
});

/**
 * Clean up expired rate limit entries
 */
export function cleanupRateLimitStore() {
  const now = Date.now();
  let cleaned = 0;

  for (const key in rateLimitStore) {
    if (rateLimitStore[key].resetTime < now) {
      delete rateLimitStore[key];
      cleaned++;
    }
  }

  console.log(`[RateLimit] Cleaned up ${cleaned} expired entries`);
  return cleaned;
}

// Run cleanup every hour
setInterval(cleanupRateLimitStore, 60 * 60 * 1000);
