/**
 * intelligenceProviderFactory — pick the right IntelligenceProvider
 * implementation for a configured framework.
 *
 * Provider-portability v1.0.0: until v0.x, ClaudeCliIntelligenceProvider
 * was the ONLY implementation, and every reviewer/sentinel/canary
 * routed through `claude -p`. This factory lets the composition root
 * pick `claude-code`, `codex-cli`, or another framework's implementation
 * at startup based on `INSTAR_FRAMEWORK` env var or the agent's
 * configured `primaryFramework`.
 *
 * When the configured framework's binary isn't installed, the factory
 * returns null. The composition root decides whether that's a fatal
 * error or a "fall back to whatever IS installed" situation.
 */
import { detectClaudePath, detectCodexPath, detectGeminiPath, detectPiPath } from './Config.js';
import { ClaudeCliIntelligenceProvider } from './ClaudeCliIntelligenceProvider.js';
import { CodexCliIntelligenceProvider } from './CodexCliIntelligenceProvider.js';
import { GeminiCliIntelligenceProvider } from './GeminiCliIntelligenceProvider.js';
import { PiCliIntelligenceProvider } from './PiCliIntelligenceProvider.js';
import { wrapIntelligenceWithCircuitBreaker } from './CircuitBreakingIntelligenceProvider.js';
import { AnthropicSubscriptionRouter, } from './AnthropicSubscriptionRouter.js';
import { InteractivePoolIntelligenceProvider } from './InteractivePoolIntelligenceProvider.js';
/**
 * Build an IntelligenceProvider for the given framework. Returns null
 * if the required binary can't be located. The caller decides what
 * happens next — fatal error, fall-back to a different framework, or
 * disable LLM-backed paths entirely.
 *
 * @example
 *   const intel = buildIntelligenceProvider({ framework: 'codex-cli' });
 *   if (intel === null) {
 *     console.warn('Codex CLI not found — LLM-backed paths disabled.');
 *   }
 */
export function buildIntelligenceProvider(options = {}) {
    const framework = options.framework ?? 'claude-code';
    // Every provider the factory hands out is wrapped with the account-global
    // LLM circuit breaker (CircuitBreakingIntelligenceProvider). A closed breaker
    // is a transparent passthrough, so this is always safe; it only acts when the
    // provider reports a usage/rate limit. See LlmCircuitBreaker for the why.
    switch (framework) {
        case 'claude-code': {
            const path = options.binaryPath ?? detectClaudePath();
            if (!path)
                return null;
            const headless = new ClaudeCliIntelligenceProvider(path);
            // Subscription-path routing (spec 04 Rule 1): when configured, route
            // each call between the SDK-credit `claude -p` path and the
            // subscription interactive pool. The router sits INSIDE the breaker
            // wrap so a rate-limit on the surviving path still trips the breaker.
            // Absent option ⇒ plain provider, byte-for-byte today's behavior.
            if (options.subscriptionPath) {
                const sp = options.subscriptionPath;
                const routed = new AnthropicSubscriptionRouter({
                    headless,
                    pool: new InteractivePoolIntelligenceProvider(sp.poolAdapter),
                    mode: sp.mode,
                    readSdkCredit: sp.readSdkCredit,
                    ...(sp.safetyMarginFraction !== undefined
                        ? { safetyMarginFraction: sp.safetyMarginFraction }
                        : {}),
                    ...(sp.onRoute ? { onRoute: sp.onRoute } : {}),
                    ...(sp.onDegrade ? { onDegrade: sp.onDegrade } : {}),
                });
                return wrapIntelligenceWithCircuitBreaker(routed, options.breaker);
            }
            return wrapIntelligenceWithCircuitBreaker(headless, options.breaker);
        }
        case 'codex-cli': {
            const path = options.binaryPath ?? detectCodexPath();
            if (!path)
                return null;
            return wrapIntelligenceWithCircuitBreaker(new CodexCliIntelligenceProvider({
                codexPath: path,
                ...(options.workingDirectory ? { workingDirectory: options.workingDirectory } : {}),
                ...(options.resolveExecJson ? { resolveExecJson: options.resolveExecJson } : {}),
            }), options.breaker);
        }
        case 'gemini-cli': {
            const path = options.binaryPath ?? detectGeminiPath();
            if (!path)
                return null;
            return wrapIntelligenceWithCircuitBreaker(new GeminiCliIntelligenceProvider({
                geminiPath: path,
                ...(options.workingDirectory ? { workingDirectory: options.workingDirectory } : {}),
                ...(options.quotaStateFile ? { capacityPolicy: { quotaStateFile: options.quotaStateFile } } : {}),
            }), options.breaker);
        }
        case 'pi-cli': {
            // PI-HARNESS-INTEGRATION-SPEC §4.4. Two preconditions, both degrading
            // to null (caller falls back, never a boot failure):
            //   1. the pi binary must be detectable, AND
            //   2. an explicit model pattern must be configured
            //      (frameworkDefaultModels['pi-cli'] → options.piModel) — the
            //      subscription guard denies pattern-less pi calls by design, so
            //      constructing a provider without one would fail every call.
            const path = options.binaryPath ?? detectPiPath();
            if (!path)
                return null;
            if (!options.piModel) {
                console.warn(`[intelligenceProviderFactory] pi-cli routing requested but no model pattern is configured — ` +
                    `set frameworkDefaultModels['pi-cli'] (e.g. "openai-codex/gpt-5.5"). Degrading to default framework.`);
                return null;
            }
            try {
                return wrapIntelligenceWithCircuitBreaker(new PiCliIntelligenceProvider({
                    piPath: path,
                    model: options.piModel,
                    ...(options.piAllowAnthropicProviders !== undefined
                        ? { allowAnthropicProviders: options.piAllowAnthropicProviders }
                        : {}),
                    ...(options.workingDirectory ? { workingDirectory: options.workingDirectory } : {}),
                }), options.breaker);
            }
            catch (err) {
                // @silent-fallback-ok — NOT a silent fallback: a PiAnthropicRouteError
                // at construction (a misconfigured Anthropic pattern without the
                // override) degrades to null LOUDLY (console.warn below), and the
                // caller (IntelligenceRouter) treats null as "framework unavailable →
                // route to the default framework" with its own DegradationReporter
                // emission. Reporting here too would double-count the same degrade.
                console.warn(`[intelligenceProviderFactory] pi-cli provider refused: ${err instanceof Error ? err.message : err}`);
                return null;
            }
        }
        default: {
            // Exhaustiveness check — extending IntelligenceFramework without
            // adding a case here is a type error.
            const _exhaustive = framework;
            void _exhaustive;
            return null;
        }
    }
}
/**
 * Convenience: read the framework selection from the environment.
 * Returns null when no framework is explicitly selected, leaving
 * the caller to apply the default (`claude-code`) or its own logic.
 */
export function frameworkFromEnv(env = process.env) {
    const raw = env['INSTAR_FRAMEWORK']?.trim().toLowerCase();
    if (!raw)
        return null;
    if (raw === 'claude-code' || raw === 'claude')
        return 'claude-code';
    if (raw === 'codex-cli' || raw === 'codex')
        return 'codex-cli';
    if (raw === 'gemini-cli' || raw === 'gemini')
        return 'gemini-cli';
    return null;
}
//# sourceMappingURL=intelligenceProviderFactory.js.map