import { ReasonCode } from '../types/index.js';
import { assertNoLegacyLocalModelPrefix } from '../internal/legacy-local-model-prefix.js';
import { createNimiError } from './errors.js';
import { REMOTE_PROVIDER_IDS } from './provider-targeting.generated.js';
const REMOTE_PROVIDER_SET = new Set(REMOTE_PROVIDER_IDS);
function normalize(value) {
    return String(value || '').trim();
}
function isLowercaseQualifiedPrefix(prefix) {
    return prefix === prefix.toLowerCase() && /^[a-z0-9_][a-z0-9_-]*$/u.test(prefix);
}
function looksLikeQualifiedRemoteModel(model) {
    const normalized = normalize(model);
    if (!normalized.includes('/')) {
        return false;
    }
    assertNoLegacyLocalModelPrefix(normalized);
    const [prefix = ''] = normalized.split('/', 1);
    const lowered = prefix.toLowerCase();
    if (lowered === 'cloud'
        || lowered === 'local'
        || lowered === 'llama'
        || lowered === 'media'
        || lowered === 'speech'
        || lowered === 'sidecar') {
        return true;
    }
    return isLowercaseQualifiedPrefix(prefix) && REMOTE_PROVIDER_SET.has(lowered);
}
function createRuntimeConvenienceInputError(message, actionHint) {
    return createNimiError({
        message,
        reasonCode: ReasonCode.ACTION_INPUT_INVALID,
        actionHint,
        source: 'sdk',
    });
}
function resolveRuntimeConvenienceTarget(input) {
    const provider = normalize(input.provider);
    const model = normalize(input.model);
    if (!provider && !model) {
        throw createRuntimeConvenienceInputError('high-level Runtime.generate()/stream() requires an explicit local model or provider + model.', 'provide_runtime_target');
    }
    if (!provider) {
        if (looksLikeQualifiedRemoteModel(model)) {
            throw createRuntimeConvenienceInputError('high-level Runtime.generate()/stream() does not accept fully-qualified remote model ids. Use provider + model, or use runtime.ai.text.generate() for explicit remote model ids.', 'remove_remote_model_prefix');
        }
        return {
            model: `local/${model}`,
            route: 'local',
        };
    }
    if (!REMOTE_PROVIDER_SET.has(provider.toLowerCase())) {
        throw createRuntimeConvenienceInputError(`unsupported provider "${provider}". Use a canonical provider id such as gemini, openai, anthropic, or deepseek.`, 'use_supported_provider_id');
    }
    if (!model) {
        throw createRuntimeConvenienceInputError('high-level Runtime.generate()/stream() requires provider + model for cloud routing. It no longer invents an implicit provider/default target.', 'provide_provider_and_model');
    }
    if (model && looksLikeQualifiedRemoteModel(model)) {
        throw createRuntimeConvenienceInputError('provider + model expects a provider-scoped model id. Remove the remote prefix, or use runtime.ai.text.generate() for explicit fully-qualified remote model ids.', 'remove_remote_model_prefix');
    }
    return {
        model: `${provider}/${model}`,
        route: 'cloud',
    };
}
function resolveRuntimeConvenienceSubjectUserId(subjectUserId, route) {
    const normalized = normalize(subjectUserId);
    if (normalized) {
        return normalized;
    }
    if (route === 'local') {
        return undefined;
    }
    throw createRuntimeConvenienceInputError('high-level Runtime.generate()/stream() requires subjectUserId for non-local routing. It no longer invents an implicit subject identity.', 'provide_subject_user_id');
}
function toGenerateInput(input) {
    const target = resolveRuntimeConvenienceTarget(input);
    const subjectUserId = resolveRuntimeConvenienceSubjectUserId(input.subjectUserId, target.route);
    return {
        model: target.model,
        input: input.prompt,
        system: input.system,
        maxTokens: input.maxTokens,
        temperature: input.temperature,
        topP: input.topP,
        reasoning: input.reasoning,
        subjectUserId,
        route: target.route,
        timeoutMs: input.timeoutMs,
        metadata: input.metadata,
    };
}
function toStreamInput(input) {
    return {
        ...toGenerateInput(input),
        signal: input.signal,
    };
}
function toUsage(input) {
    return {
        inputTokens: Number(input?.inputTokens || 0),
        outputTokens: Number(input?.outputTokens || 0),
    };
}
export function toRuntimeGenerateResult(output) {
    return {
        text: output.text,
        usage: toUsage(output.usage),
        finishReason: output.finishReason,
        traceId: String(output.trace.traceId || ''),
        modelResolved: String(output.trace.modelResolved || ''),
        routeDecision: output.trace.routeDecision || 'local',
    };
}
export async function runtimeGenerateConvenience(runtime, input) {
    const output = await runtime.ai.text.generate(toGenerateInput(input));
    return toRuntimeGenerateResult(output);
}
function mapStreamPart(part) {
    if (part.type === 'delta') {
        return {
            type: 'text',
            text: part.text,
        };
    }
    if (part.type === 'reasoning-delta') {
        return {
            type: 'reasoning',
            text: part.text,
        };
    }
    if (part.type === 'finish') {
        return {
            type: 'done',
            usage: toUsage(part.usage),
            finishReason: part.finishReason,
            traceId: String(part.trace.traceId || ''),
            modelResolved: String(part.trace.modelResolved || ''),
            routeDecision: part.trace.routeDecision || 'local',
        };
    }
    if (part.type === 'error') {
        return {
            type: 'error',
            error: part.error,
        };
    }
    return null;
}
export async function runtimeStreamConvenience(runtime, input) {
    const output = await runtime.ai.text.stream(toStreamInput(input));
    return {
        async *[Symbol.asyncIterator]() {
            for await (const part of output.stream) {
                const chunk = mapStreamPart(part);
                if (chunk) {
                    yield chunk;
                }
            }
        },
    };
}
//# sourceMappingURL=runtime-convenience.js.map