import { readFile } from 'node:fs/promises';
import { query } from '@anthropic-ai/claude-agent-sdk';
import { sessionsDb } from '../../../../modules/database/index.js';
import { resolveClaudeCodeExecutablePath } from '../../../../shared/claude-cli-path.js';
import { buildDefaultProviderCurrentActiveModel, writeProviderSessionActiveModelChange, } from '../../../../shared/utils.js';
export const CLAUDE_FALLBACK_MODELS = {
    OPTIONS: [
        { value: 'default', label: 'Default (recommended)' },
        { value: 'sonnet[1m]', label: 'Sonnet (1M context)' },
        { value: 'opus', label: 'Opus' },
        { value: 'opus[1m]', label: 'Opus (1M context)' },
        { value: 'haiku', label: 'Haiku' },
        { value: 'sonnet', label: 'sonnet' },
    ],
    DEFAULT: 'default',
};
const ANSI_PATTERN = new RegExp('[\\u001B\\u009B][[\\]()#;?]*(?:'
    + '(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]'
    + '|(?:[\\dA-PR-TZcf-ntqry=><~]))', 'g');
const buildClaudeQueryOptions = () => ({
    env: { ...process.env },
    pathToClaudeCodeExecutable: resolveClaudeCodeExecutablePath(process.env.CLAUDE_CLI_PATH),
    permissionMode: 'default',
});
const mapClaudeModel = (model) => ({
    value: model.value,
    label: model.displayName || model.value,
    description: model.description || undefined,
});
const buildClaudeModelsDefinition = (models) => {
    const options = [];
    const seenValues = new Set();
    for (const model of models) {
        const mappedModel = mapClaudeModel(model);
        if (seenValues.has(mappedModel.value)) {
            continue;
        }
        seenValues.add(mappedModel.value);
        options.push(mappedModel);
    }
    if (options.length === 0) {
        return CLAUDE_FALLBACK_MODELS;
    }
    const defaultValue = options.find((option) => option.value === 'default')?.value
        ?? options[0]?.value
        ?? CLAUDE_FALLBACK_MODELS.DEFAULT;
    return {
        OPTIONS: options,
        DEFAULT: defaultValue,
    };
};
const extractClaudeEventModel = (event, sessionId) => {
    const eventSessionId = event.sessionId ?? event.session_id;
    if (eventSessionId && eventSessionId !== sessionId) {
        return null;
    }
    const contentModel = extractClaudeModelFromMessageContent(event.message?.content);
    if (contentModel) {
        return contentModel;
    }
    const directModel = event.model?.trim();
    if (directModel) {
        return directModel;
    }
    const messageModel = event.message?.model?.trim();
    return messageModel || null;
};
const stripAnsi = (value) => value.replace(ANSI_PATTERN, '');
const extractTaggedContent = (content, tagName) => {
    const escapedTagName = tagName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    const match = new RegExp(`<${escapedTagName}>([\\s\\S]*?)<\\/${escapedTagName}>`).exec(content);
    return match ? match[1] : null;
};
const extractClaudeModelFromTextContent = (content) => {
    const localCommandStdout = extractTaggedContent(content, 'local-command-stdout');
    if (localCommandStdout !== null) {
        const cleanedStdout = stripAnsi(localCommandStdout).replace(/\s+/g, ' ').trim();
        const changedModel = /(?:set|changed|switched)\s+model\s+to\s+(.+?)\.?$/i.exec(cleanedStdout);
        if (changedModel?.[1]?.trim()) {
            return changedModel[1].trim();
        }
    }
    const modelTag = extractTaggedContent(content, 'model')?.trim();
    return modelTag || null;
};
const extractClaudeModelFromMessageContent = (content) => {
    if (typeof content === 'string') {
        return extractClaudeModelFromTextContent(content);
    }
    if (!Array.isArray(content)) {
        return null;
    }
    for (const part of content) {
        if (!part || typeof part !== 'object' || !('text' in part) || typeof part.text !== 'string') {
            continue;
        }
        const model = extractClaudeModelFromTextContent(part.text);
        if (model) {
            return model;
        }
    }
    return null;
};
const readClaudeSessionModelFromJsonl = async (sessionId, jsonlPath) => {
    const content = await readFile(jsonlPath, 'utf8');
    const lines = content
        .split(/\r?\n/)
        .map((line) => line.trim())
        .filter(Boolean);
    for (let index = lines.length - 1; index >= 0; index -= 1) {
        try {
            const event = JSON.parse(lines[index]);
            const model = extractClaudeEventModel(event, sessionId);
            if (model) {
                return { model };
            }
        }
        catch {
            // Skip malformed JSONL lines that can happen during concurrent writes.
        }
    }
    return null;
};
export class ClaudeProviderModels {
    async getSupportedModels() {
        let queryInstance = null;
        try {
            // The SDK exposes its runtime model catalog on the initialized query
            // instance, so we create a lightweight query and immediately close it
            // after reading the control-plane metadata.
            queryInstance = query({
                prompt: 'Get supported models',
                options: buildClaudeQueryOptions(),
            });
            const supportedModels = await queryInstance.supportedModels();
            return buildClaudeModelsDefinition(supportedModels);
        }
        catch {
            return CLAUDE_FALLBACK_MODELS;
        }
        finally {
            queryInstance?.close();
        }
    }
    async getCurrentActiveModel(sessionId) {
        if (!sessionId?.trim()) {
            return buildDefaultProviderCurrentActiveModel(await this.getSupportedModels());
        }
        try {
            const jsonlPath = sessionsDb.getSessionById(sessionId)?.jsonl_path;
            const activeModel = jsonlPath
                ? await readClaudeSessionModelFromJsonl(sessionId, jsonlPath)
                : null;
            if (activeModel?.model) {
                return activeModel;
            }
        }
        catch {
            // Fall through to the provider default when the session-backed lookup fails.
        }
        return buildDefaultProviderCurrentActiveModel(await this.getSupportedModels());
    }
    async changeActiveModel(input) {
        return writeProviderSessionActiveModelChange('claude', input);
    }
}
//# sourceMappingURL=claude-models.provider.js.map