import { getAdapter } from "@/lib/db/driver.js";

// In-memory concurrent request tracking (resets on server restart)
const concurrentCounts = new Map();

/**
 * Increment concurrent request count for a user.
 * Returns true if slot acquired, false if at capacity.
 */
export async function incrementConcurrent(userId) {
  const current = concurrentCounts.get(userId) || 0;
  let limits;
  try {
    const { getByUserId } = await import("@/lib/db/repos/rateLimitRepo");
    limits = await getByUserId(userId);
  } catch {
    limits = null;
  }
  const max = limits?.maxConcurrent ?? 5;
  if (current >= max) return false;
  concurrentCounts.set(userId, current + 1);
  return true;
}

/**
 * Decrement concurrent request count for a user.
 */
export function decrementConcurrent(userId) {
  const current = concurrentCounts.get(userId);
  if (current === undefined || current <= 0) return;
  if (current === 1) {
    concurrentCounts.delete(userId);
  } else {
    concurrentCounts.set(userId, current - 1);
  }
}

/**
 * Get current concurrent requests for a user (for status display).
 */
export function getConcurrentCount(userId) {
  return concurrentCounts.get(userId) || 0;
}

/**
 * Reset all concurrent counts (for testing).
 */
export function resetConcurrent() {
  concurrentCounts.clear();
}

// ─── Usage logging (fire-and-forget) ─────────────────────────────────────

/**
 * Non-blocking usage logging helper.
 * Dynamically imports usageRecordsRepo to avoid circular deps and top-level await.
 */
function logUsageAsync(data) {
  import("@/lib/db/repos/usageRecordsRepo")
    .then(({ logUsage }) => {
      logUsage(data).catch((err) => {
        console.error("[rateLimitMiddleware] logUsage error:", err.message);
      });
    })
    .catch((err) => {
      console.error("[rateLimitMiddleware] import usageRecordsRepo error:", err.message);
    });
}

// ─── Rate limit strategies ────────────────────────────────────────────────

async function checkRTPM(userId, limits) {
  const max = limits.rtpMinute ?? 100;
  if (max <= 0) return null; // 0 or negative = unlimited
  const db = await getAdapter();
  const oneMinuteAgo = new Date(Date.now() - 60 * 1000).toISOString();
  const row = db.get(
    `SELECT COUNT(*) as cnt FROM usageHistory uh
     INNER JOIN apiKeys ak ON uh.apiKey = ak.key
     WHERE ak.userId = ? AND uh.timestamp >= ?`,
    [userId, oneMinuteAgo]
  );
  const count = row?.cnt || 0;
  if (count >= max) {
    return { allowed: false, reason: "Rate limit exceeded (requests per minute)" };
  }
  return null;
}

async function checkRTPD(userId, limits) {
  const max = limits.rtpDay ?? 100000;
  if (max <= 0) return null;
  const db = await getAdapter();
  const today = new Date();
  const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate()).toISOString();
  const row = db.get(
    `SELECT COUNT(*) as cnt FROM usageHistory uh
     INNER JOIN apiKeys ak ON uh.apiKey = ak.key
     WHERE ak.userId = ? AND uh.timestamp >= ?`,
    [userId, startOfDay]
  );
  const count = row?.cnt || 0;
  if (count >= max) {
    return { allowed: false, reason: "Rate limit exceeded (requests per day)" };
  }
  return null;
}

async function checkTokenCount(userId, limits) {
  const max = limits.tokenLimitMonthly ?? 1000000;
  if (max <= 0) return null;
  const { getMonthlyTokens } = await import("@/lib/db/repos/rateLimitRepo");
  const used = await getMonthlyTokens(userId);
  if (used >= max) {
    return { allowed: false, reason: "Token limit exceeded (monthly)" };
  }
  return null;
}

async function checkCreditQuota(userId, limits) {
  const max = limits.creditLimit ?? 5.0;
  if (max <= 0) return null;
  const { getMonthlyCost } = await import("@/lib/db/repos/rateLimitRepo");
  const used = await getMonthlyCost(userId);
  if (used >= max) {
    return { allowed: false, reason: "Credit/quota exceeded (monthly)" };
  }
  return null;
}

async function checkExpiry(userId) {
  const db = await getAdapter();
  const row = db.get(
    `SELECT expiresAt FROM userPackages WHERE userId = ?`,
    [userId]
  );
  if (!row?.expiresAt) return null; // No expiry = always valid
  const expiresAt = new Date(row.expiresAt);
  if (expiresAt < new Date()) {
    return { allowed: false, reason: "Package expired" };
  }
  return null;
}

// ─── Main check function ──────────────────────────────────────────────────

/**
 * Check all 4 rate limit strategies for a given apiKey.
 * Admin users bypass all checks.
 *
 * @param {string} apiKey - The API key being used.
 * @returns {Promise<{allowed: boolean, reason?: string}>}
 */
export async function checkRateLimits(apiKey) {
  if (!apiKey) return { allowed: true }; // No apiKey → local request, skip check

  try {
    // Look up apiKey → userId
    const db = await getAdapter();
    const keyRow = await db.get(`SELECT id, userId FROM apiKeys WHERE key = ?`, [apiKey]);
    if (!keyRow?.userId) return { allowed: true };

    const userId = keyRow.userId;
    const apiKeyId = keyRow.id;

    const { findById } = await import("@/lib/db/repos/usersRepo");
    const user = await findById(userId);
    if (user?.role === "admin") return { allowed: true, userId, apiKeyId };

    // Get rate limits config
    const { getEffectiveLimits } = await import("@/lib/db/repos/rateLimitRepo");
    const limits = await getEffectiveLimits(userId);
    if (!limits) return { allowed: true, userId, apiKeyId }; // No limits = pass

    // Check strategies in order
    const checks = [
      checkExpiry,
      checkRTPM,
      checkRTPD,
      checkTokenCount,
      checkCreditQuota,
    ];

    for (const check of checks) {
      const result = await check(userId, limits);
      if (result) return { ...result, userId, apiKeyId };
    }

    return { allowed: true, userId, apiKeyId };
  } catch (error) {
    console.error("[rateLimitMiddleware] checkRateLimits error:", error.message);
    return { allowed: true }; // Fail open on error
  }
}

/**
 * Middleware wrapper: check rate limits, log usage, and return 429 response if blocked.
 * Call this in the API route handler before processing the request.
 *
 * @param {string} apiKey - The API key from the request.
 * @param {object} [requestData] - Metadata about the request for usage logging.
 * @param {string} [requestData.model]
 * @param {number} [requestData.inputTokens]
 * @param {number} [requestData.outputTokens]
 * @param {number} [requestData.totalTokens]
 * @param {number} [requestData.cost]
 * @param {number} [requestData.creditUsed]
 * @param {string} [requestData.endpoint]
 * @returns {Promise<NextResponse|null>} 429 response if blocked, null if allowed.
 */
export async function enforceRateLimits(apiKey, requestData = {}) {
  const result = await checkRateLimits(apiKey);

  if (result.allowed) {
    logUsageAsync({
      userId: result.userId,
      apiKeyId: result.apiKeyId,
      model: requestData.model || "unknown",
      inputTokens: requestData.inputTokens || 0,
      outputTokens: requestData.outputTokens || 0,
      totalTokens: requestData.totalTokens || 0,
      cost: requestData.cost || 0,
      creditUsed: requestData.creditUsed || 0,
      status: "success",
      endpoint: requestData.endpoint || "/v1/chat/completions",
    });
    return null;
  }

  logUsageAsync({
    userId: result.userId,
    apiKeyId: result.apiKeyId,
    model: requestData.model || "unknown",
    status: "rate_limited",
    errorMessage: result.reason,
    endpoint: requestData.endpoint || "/v1/chat/completions",
  });

  const { NextResponse } = await import("next/server");
  return NextResponse.json(
    { error: result.reason || "Rate limit exceeded" },
    { status: 429, headers: { "Retry-After": "60" } }
  );
}
