/**
 * OpenAI Codex CLI account snapshot + model discovery.
 *
 * Why a file read and not an API call: the codex CLI maintains
 * ~/.codex/models_cache.json itself (with fetched_at / etag for refresh
 * tracking) as part of its normal operation. That cache is the authoritative
 * "what models can THIS ChatGPT account actually serve" record — distinct
 * from the static catalog in models.generated.ts which can carry phantom
 * entries like `gpt-5-codex` that 400 with "model is not supported when
 * using Codex with a ChatGPT account."
 *
 * Asymmetry vs gemini-cli (which calls setupUser + retrieveUserQuota over
 * the wire): codex CLI caches locally; gemini-cli does not. Each provider
 * gets the cheapest reliable discovery path.
 *
 * Consumer: SF extension's openai-codex-catalog.js (via discoverCodexCliModels);
 * future usage UI consumers via snapshotCodexCliAccount.
 */
import { existsSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";

export interface CodexCliAccountSnapshot {
	/** Slug ids the account can actually serve (visibility=list ∧ supported_in_api). */
	modelIds: string[];
	/** Path of the cache file we read. */
	cachePath: string;
	/** When the codex CLI last wrote the cache (ISO string), if available. */
	fetchedAt?: string;
}

function codexHome(): string {
	return process.env.CODEX_HOME ?? join(homedir(), ".codex");
}

function codexCachePath(): string {
	return join(codexHome(), "models_cache.json");
}

/**
 * Read the codex CLI's local cache and return a snapshot of the slugs the
 * account can actually serve. Returns null when the cache is missing,
 * malformed, or the codex CLI has not been initialized.
 *
 * Filters: visibility === "list" AND supported_in_api === true.
 *
 * Why both filters: visibility=hide is for codex-internal models like
 * codex-auto-review (not user-facing). supported_in_api=false catches
 * preview slugs that exist in the cache but reject API requests for the
 * current account tier (observed live with gpt-5.3-codex-spark).
 */
export function snapshotCodexCliAccount(): CodexCliAccountSnapshot | null {
	const path = codexCachePath();
	if (!existsSync(path)) return null;
	try {
		const cache = JSON.parse(readFileSync(path, "utf-8")) as {
			models?: unknown;
			fetched_at?: string;
		};
		const models = Array.isArray(cache?.models) ? cache.models : null;
		if (!models) return null;
		const slugs = models
			.filter(
				(m): m is { slug: string } =>
					!!m &&
					typeof m === "object" &&
					(m as Record<string, unknown>).visibility === "list" &&
					(m as Record<string, unknown>).supported_in_api === true &&
					typeof (m as Record<string, unknown>).slug === "string" &&
					((m as Record<string, unknown>).slug as string).length > 0,
			)
			.map((m) => m.slug);
		if (slugs.length === 0) return null;
		return {
			modelIds: slugs,
			cachePath: path,
			fetchedAt:
				typeof cache.fetched_at === "string" ? cache.fetched_at : undefined,
		};
	} catch {
		return null;
	}
}

/**
 * Convenience wrapper that returns just the model IDs (matching the
 * discoverGeminiCliModels signature for symmetry with that package).
 */
export function discoverCodexCliModels(): string[] | null {
	const snap = snapshotCodexCliAccount();
	return snap ? snap.modelIds : null;
}
