/**
 * openai-codex-catalog.js — SF-side cache writer for codex-cli model discovery.
 *
 * The actual cache reader (~/.codex/models_cache.json parsing + visibility +
 * supported_in_api filtering) lives in the dedicated
 * @singularity-forge/openai-codex-provider package. This module only handles
 * the SF-specific concerns: where on disk to mirror the result, how often to
 * refresh, and the session_start lifecycle hook.
 *
 * Cache file shape stays compatible with model-catalog-cache.getKnownModelIds
 * so consumers read both transparently.
 */
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { sfHome } from "./sf-home.js";

const PROVIDER_ID = "openai-codex";
const CATALOG_TTL_MS = 6 * 60 * 60 * 1000;

function sfCacheDir() {
	return join(sfHome(), "model-catalog");
}

function sfCacheFilePath() {
	return join(sfCacheDir(), `${PROVIDER_ID}.json`);
}

function isSfCacheFresh() {
	try {
		const path = sfCacheFilePath();
		if (!existsSync(path)) return false;
		const entry = JSON.parse(readFileSync(path, "utf-8"));
		if (!entry?.fetchedAt || !Array.isArray(entry.modelIds)) return false;
		return Date.now() - new Date(entry.fetchedAt).getTime() <= CATALOG_TTL_MS;
	} catch {
		return false;
	}
}

function writeSfCache(modelIds) {
	try {
		mkdirSync(sfCacheDir(), { recursive: true });
		writeFileSync(
			sfCacheFilePath(),
			JSON.stringify({
				fetchedAt: new Date().toISOString(),
				modelIds,
			}),
			"utf-8",
		);
	} catch {
		// Best-effort — never fail the caller.
	}
}

/**
 * Read the codex CLI's local model cache and return the slugs the user's
 * account can actually serve. Returns null when unavailable.
 *
 * Thin compatibility wrapper around discoverCodexCliModels — kept exported
 * for any direct consumer that wants the raw list without writing the SF
 * cache.
 */
export async function readCodexAvailableModels() {
	try {
		const { discoverCodexCliModels } = await import(
			"@singularity-forge/openai-codex-provider"
		);
		return discoverCodexCliModels();
	} catch {
		return null;
	}
}

/**
 * Discover the codex model list and write it to the SF model-catalog cache.
 * Returns the cached IDs on success, null on any failure.
 *
 * Consumer: scheduleOpenaiCodexCatalogRefresh during session_start.
 */
export async function refreshOpenaiCodexCatalog(_basePath) {
	const modelIds = await readCodexAvailableModels();
	if (!modelIds || modelIds.length === 0) return null;
	writeSfCache(modelIds);
	return modelIds;
}

/**
 * Awaitable refresh — only fetches when the SF cache is stale. Returns the
 * fresh model id list, or null on failure / no codex cache available.
 */
export async function runOpenaiCodexCatalogRefreshIfStale(basePath) {
	if (isSfCacheFresh()) return null;
	try {
		return await refreshOpenaiCodexCatalog(basePath);
	} catch {
		return null;
	}
}

/**
 * Fire-and-forget background refresh. Skipped if the SF cache is fresh.
 *
 * Consumer: bootstrap/register-hooks.js session_start hook.
 */
export function scheduleOpenaiCodexCatalogRefresh(basePath) {
	if (isSfCacheFresh()) return;
	setImmediate(() => runOpenaiCodexCatalogRefreshIfStale(basePath));
}
