/**
 * Anthropic Claude Provider
 *
 * Implements LLMProvider interface for Anthropic's Claude models.
 * Uses @anthropic-ai/sdk for API calls.
 */
import { MODEL_PRICING, resolveModelAlias } from '../types.js';
import { consoleLogger } from '../../../utils/logger.js';
import { extractJson, extractRequiredFieldsFromSchema } from '../../../utils/llm-json-extractor.js';
/**
 * Anthropic Claude provider implementation
 */
export class AnthropicProvider {
    constructor(config) {
        this.name = 'anthropic';
        this.apiKey = config.apiKey;
        // Support both aliases (opus, sonnet, haiku) and full model IDs
        this.defaultModel = resolveModelAlias(config.model || 'opus');
        this.maxTokens = config.maxTokens || 4096;
        this.temperature = config.temperature ?? 0.3;
        this.baseUrl = config.baseUrl || 'https://api.anthropic.com';
        this.logger = config.logger || consoleLogger;
    }
    /**
     * Get or create SDK client (lazy initialization)
     */
    async getClient() {
        if (!this.client) {
            // Dynamic import to avoid requiring SDK if not used
            const Anthropic = (await import('@anthropic-ai/sdk')).default;
            this.client = new Anthropic({
                apiKey: this.apiKey,
                baseURL: this.baseUrl,
            });
        }
        return this.client;
    }
    /**
     * Analyze text with Claude
     */
    async analyze(prompt, options = {}) {
        const startTime = Date.now();
        // Resolve model alias (opus → claude-opus-4-6)
        const model = resolveModelAlias(options.model || this.defaultModel);
        const maxTokens = options.maxTokens || this.maxTokens;
        const temperature = options.temperature ?? this.temperature;
        const retries = options.retries ?? 2;
        const timeout = options.timeout ?? 60000;
        let lastError = null;
        let wasRetry = false;
        for (let attempt = 0; attempt <= retries; attempt++) {
            try {
                const client = await this.getClient();
                const messages = [
                    { role: 'user', content: prompt },
                ];
                const response = await client.messages.create({
                    model,
                    max_tokens: maxTokens,
                    temperature,
                    system: options.systemPrompt,
                    messages,
                });
                const content = response.content
                    .filter((block) => block.type === 'text')
                    .map((block) => block.text)
                    .join('\n');
                const usage = {
                    inputTokens: response.usage?.input_tokens || 0,
                    outputTokens: response.usage?.output_tokens || 0,
                    totalTokens: (response.usage?.input_tokens || 0) + (response.usage?.output_tokens || 0),
                };
                return {
                    content,
                    usage,
                    estimatedCost: this.estimateCost(usage.inputTokens, usage.outputTokens, model),
                    model,
                    durationMs: Date.now() - startTime,
                    wasRetry,
                };
            }
            catch (error) {
                lastError = error;
                wasRetry = true;
                // Don't retry on auth errors
                if (error.status === 401 || error.status === 403) {
                    throw error;
                }
                // Don't retry on validation errors
                if (error.status === 400) {
                    throw error;
                }
                // Retry on rate limits and server errors
                if (attempt < retries) {
                    const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
                    this.logger.warn(`Anthropic request failed, retrying in ${delay}ms...`);
                    await new Promise(resolve => setTimeout(resolve, delay));
                }
            }
        }
        throw lastError || new Error('Anthropic request failed after retries');
    }
    /**
     * Analyze with structured JSON output
     */
    async analyzeStructured(prompt, options) {
        // Add JSON instruction to prompt
        const jsonPrompt = `${prompt}

IMPORTANT: Respond with valid JSON matching this schema:
${JSON.stringify(options.schema, null, 2)}

Return ONLY the JSON object, no markdown formatting or explanation.`;
        const result = await this.analyze(jsonPrompt, {
            ...options,
            temperature: options.temperature ?? 0.1, // Lower temp for structured output
        });
        // Extract required fields from schema for validation
        const requiredFields = extractRequiredFieldsFromSchema(options.schema);
        // Use robust JSON extraction (handles prose + JSON, code blocks, nested structures)
        const extraction = extractJson(result.content, { requiredFields });
        if (extraction.success && extraction.data) {
            return {
                data: extraction.data,
                usage: result.usage,
                estimatedCost: result.estimatedCost,
            };
        }
        // Extraction failed
        if (options.strict) {
            throw new Error(`Failed to parse structured response: ${extraction.error}`);
        }
        return {
            data: { error: 'parse_failed', raw: result.content },
            usage: result.usage,
            estimatedCost: result.estimatedCost,
        };
    }
    /**
     * Estimate cost for a request
     */
    estimateCost(inputTokens, outputTokens, model) {
        const modelId = model || this.defaultModel;
        const pricing = MODEL_PRICING[modelId] || { inputPer1M: 3, outputPer1M: 15 };
        const inputCost = (inputTokens / 1000000) * pricing.inputPer1M;
        const outputCost = (outputTokens / 1000000) * pricing.outputPer1M;
        return inputCost + outputCost;
    }
    /**
     * Check if provider is available
     */
    async isAvailable() {
        try {
            const status = await this.getStatus();
            return status.available;
        }
        catch {
            return false;
        }
    }
    /**
     * Get provider status
     */
    async getStatus() {
        const startTime = Date.now();
        try {
            const client = await this.getClient();
            // Simple ping - count tokens for minimal text
            await client.messages.count_tokens({
                model: this.defaultModel,
                messages: [{ role: 'user', content: 'ping' }],
            });
            return {
                available: true,
                latencyMs: Date.now() - startTime,
            };
        }
        catch (error) {
            return {
                available: false,
                error: error.message || 'Unknown error',
            };
        }
    }
}
//# sourceMappingURL=anthropic-provider.js.map