/**
 * Usage metering middleware for SentryAgent.ai AgentIdP.
 * Tracks per-tenant API call counts in-memory and periodically flushes to the DB.
 */

import { Request, Response, NextFunction, RequestHandler } from 'express';
import { Pool } from 'pg';

/** In-memory counter: tenantId → accumulated API call count since last flush. */
const tenantCounters = new Map<string, number>();

/** Flush interval in milliseconds. */
const FLUSH_INTERVAL_MS = 60_000;

/**
 * Increments the in-memory API call counter for a tenant.
 *
 * @param tenantId - The tenant UUID.
 */
function incrementCounter(tenantId: string): void {
  const current = tenantCounters.get(tenantId) ?? 0;
  tenantCounters.set(tenantId, current + 1);
}

/**
 * Flushes all in-memory counters to the `usage_events` table via UPSERT.
 * Clears the in-memory counters after a successful flush.
 * Uses ON CONFLICT to accumulate counts rather than overwrite.
 *
 * @param pool - PostgreSQL connection pool.
 */
async function flushCounters(pool: Pool): Promise<void> {
  if (tenantCounters.size === 0) {
    return;
  }

  // Snapshot and clear before async DB work to avoid double-counting on slow flushes
  const snapshot = new Map(tenantCounters);
  tenantCounters.clear();

  const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD

  for (const [tenantId, count] of snapshot) {
    if (count <= 0) continue;

    await pool.query(
      `INSERT INTO usage_events (tenant_id, date, metric_type, count)
       VALUES ($1, $2, 'api_calls', $3)
       ON CONFLICT (tenant_id, date, metric_type)
       DO UPDATE SET count = usage_events.count + EXCLUDED.count`,
      [tenantId, today, count],
    );
  }
}

/**
 * Creates the usage metering middleware.
 * On every authenticated request (where `req.user` is populated),
 * increments the in-memory counter for the tenant identified by `organizationId`.
 *
 * @param _pool - PostgreSQL connection pool (used internally by flushCounters).
 * @returns Express RequestHandler.
 */
export function createUsageMeteringMiddleware(_pool: Pool): RequestHandler {
  return (req: Request, _res: Response, next: NextFunction): void => {
    if (req.user?.organization_id) {
      incrementCounter(req.user.organization_id);
    }
    next();
  };
}

/**
 * Starts the periodic flush interval that writes in-memory counters to the DB.
 * Call once at application startup (after the pool is ready).
 *
 * @param pool - PostgreSQL connection pool.
 * @returns The NodeJS.Timeout handle (can be used to stop the interval in tests).
 */
export function startUsageMeteringFlush(pool: Pool): NodeJS.Timeout {
  return setInterval(() => {
    flushCounters(pool).catch((err: unknown) => {
      console.error('[UsageMetering] Failed to flush usage counters:', err);
    });
  }, FLUSH_INTERVAL_MS);
}
