import { createDbService } from '@/server/services/db.js';
/**
 * In-memory cache for the languages registry.
 *
 * Keyed off an internal revision counter that is bumped by `invalidateLanguagesCache()`.
 * Default TTL: 60 s. Cross-isolate invalidation is deferred to a later plan.
 *
 * Callers outside this module MUST go through the cached helpers.
 * Direct DB calls belong in `./queries`.
 */

import { env } from '@/server/env';
import type { Language } from '@/server/db/schema';

const TTL_MS = 60_000;

interface CacheEntry {
  data: Language[];
  expiresAt: number;
  revision: number;
}

let entry: CacheEntry | null = null;
let inFlight: Promise<Language[]> | null = null;
let revision = 0;

function isStale(): boolean {
  if (!entry) return true;
  if (entry.revision !== revision) return true;
  return Date.now() > entry.expiresAt;
}

async function loadAll(): Promise<Language[]> {
  const { getActiveLanguages } = await import('./queries.js');
  const db = createDbService({ DATABASE_URL: env.DATABASE_URL });
  const data = await getActiveLanguages(db);
  entry = { data, expiresAt: Date.now() + TTL_MS, revision };
  inFlight = null;
  return data;
}

/** Returns all active languages, using the in-process cache. */
export async function getActiveLanguagesCached(opts?: {
  includeInactive?: boolean;
}): Promise<Language[]> {
  if (opts?.includeInactive) {
    // Bypass cache — inactive languages are an admin-only edge case.
    // All languages (including inactive) fetched directly from DB.
    const { getActiveLanguages } = await import('./queries.js');
    const db = createDbService({ DATABASE_URL: env.DATABASE_URL });
    return getActiveLanguages(db);
  }

  if (!isStale() && entry) return entry.data;
  if (inFlight) return inFlight;

  inFlight = loadAll();
  return inFlight;
}

/** Returns a single language by code from the cache, or null if not found. */
export async function getLanguageCached(code: string): Promise<Language | null> {
  const all = await getActiveLanguagesCached();
  return all.find((l) => l.code === code) ?? null;
}

/**
 * Bust the in-memory cache immediately.
 * Call after any write to the languages table (upsert, setActive, etc.).
 * Also exported for Plan 3+4 invalidation hooks.
 */
export function invalidateLanguagesCache(): void {
  revision += 1;
  entry = null;
  inFlight = null;
}
