import { Meter, PaymentCurrency } from '../store/models';
import logger from './logger';

// Shared TTL cache for reference data (meter, currency) that rarely changes.
// Used by both ingestion route and credit consumption pipeline.
const CACHE_TTL = 5 * 60_000; // 5 minutes (invalidated on model update/destroy)
const CACHE_MAX_SIZE = 500; // safety cap; meter/currency counts are typically < 100

// Performance instrumentation: cache hit/miss counters (logged every 60s)
const cacheStats = { meterHit: 0, meterMiss: 0, currencyHit: 0, currencyMiss: 0, expandedHit: 0, expandedMiss: 0 };
setInterval(() => {
  const total =
    cacheStats.meterHit +
    cacheStats.meterMiss +
    cacheStats.currencyHit +
    cacheStats.currencyMiss +
    cacheStats.expandedHit +
    cacheStats.expandedMiss;
  if (total > 0) {
    logger.info('Reference cache stats (60s)', { ...cacheStats });
    cacheStats.meterHit = 0;
    cacheStats.meterMiss = 0;
    cacheStats.currencyHit = 0;
    cacheStats.currencyMiss = 0;
    cacheStats.expandedHit = 0;
    cacheStats.expandedMiss = 0;
  }
}, 60_000).unref();
type CacheEntry = { data: any; expires: number };

// Evict expired entries when cache exceeds max size
function evictExpired(cache: Map<string, CacheEntry>) {
  if (cache.size <= CACHE_MAX_SIZE) return;
  const now = Date.now();
  for (const [k, v] of cache) {
    if (v.expires < now) cache.delete(k);
  }
}

const meterCache = new Map<string, CacheEntry>();
const currencyCache = new Map<string, CacheEntry>();

// Lazily register hooks after models are initialized (avoid top-level addHook before Sequelize init)
let hooksRegistered = false;
function ensureCacheHooks() {
  if (hooksRegistered) return;
  hooksRegistered = true;
  Meter.addHook('afterUpdate', 'invalidateMeterCache', (model: any) => {
    meterCache.delete(model.event_name);
    meterExpandedCache.delete(model.event_name);
  });
  Meter.addHook('afterDestroy', 'invalidateMeterCacheOnDelete', (model: any) => {
    meterCache.delete(model.event_name);
    meterExpandedCache.delete(model.event_name);
  });
  PaymentCurrency.addHook('afterUpdate', 'invalidateCurrencyCache', (model: any) => {
    currencyCache.delete(model.id);
    // Also invalidate expanded meter cache entries that reference this currency
    meterExpandedCache.clear();
  });
}

export async function getCachedMeter(eventName: string) {
  ensureCacheHooks();
  const cached = meterCache.get(eventName);
  if (cached && cached.expires > Date.now()) {
    cacheStats.meterHit++;
    return cached.data;
  }
  cacheStats.meterMiss++;
  const meter = await Meter.getMeterByEventName(eventName);
  if (meter) {
    evictExpired(meterCache);
    meterCache.set(eventName, { data: meter, expires: Date.now() + CACHE_TTL });
  }
  return meter;
}

export async function getCachedCurrency(currencyId: string) {
  ensureCacheHooks();
  const cached = currencyCache.get(currencyId);
  if (cached && cached.expires > Date.now()) {
    cacheStats.currencyHit++;
    return cached.data;
  }
  cacheStats.currencyMiss++;
  const currency = await PaymentCurrency.findByPk(currencyId);
  if (currency) {
    evictExpired(currencyCache);
    currencyCache.set(currencyId, { data: currency, expires: Date.now() + CACHE_TTL });
  }
  return currency;
}

// Q3: For consumption pipeline — cached meter with PaymentCurrency included
const meterExpandedCache = new Map<string, CacheEntry>();

export async function getCachedMeterExpanded(eventName: string) {
  ensureCacheHooks();
  const cached = meterExpandedCache.get(eventName);
  if (cached && cached.expires > Date.now()) {
    cacheStats.expandedHit++;
    return cached.data;
  }
  cacheStats.expandedMiss++;
  const meter = await Meter.findOne({
    where: { event_name: eventName },
    include: [{ model: PaymentCurrency, as: 'paymentCurrency' }],
  });
  if (meter) {
    evictExpired(meterExpandedCache);
    meterExpandedCache.set(eventName, { data: meter, expires: Date.now() + CACHE_TTL });
  }
  return meter;
}
