/**
 * Anthropic Adapter
 * Stateless translation layer between Universal Protocol and Anthropic API
 *
 * Responsibilities:
 * 1. Translate LLMGenerateRequest to Anthropic message format
 * 2. Call Anthropic SDK with tool_use for structured output
 * 3. Parse Anthropic response (extract from tool_use block)
 * 4. Translate back to LLMGenerateResponse
 */

import type { LLMAdapter, LLMGenerateRequest, LLMGenerateResponse } from '../../types'
import type { LLMModel, ModelSpecificOptions } from '../../model-info'
import { bslogExternalServiceError, isSystemError } from '../../../../../platform/utils/server/betterstack-logger'
import { composeSignal } from './compose-signal'
import { DEFAULT_LLM_TIMEOUT } from '../../../../../platform/constants/timeouts'

/**
 * Anthropic adapter configuration
 */
export interface AnthropicAdapterConfig {
  apiKey: string
  model: LLMModel
  modelOptions?: ModelSpecificOptions
}

/**
 * Check if the model and provider combination is for Anthropic
 *
 * @param model - Model identifier (e.g., 'claude-opus-4-5-20251101')
 * @param provider - Provider name
 * @returns True if this is an Anthropic model configuration
 */
export function isAnthropicModel(model: LLMModel, provider: string): boolean {
  return model.startsWith('claude-') && provider === 'anthropic'
}

/**
 * Anthropic Claude Adapter - Implements universal protocol
 * Uses tool_use for structured output (Anthropic's recommended approach)
 *
 * Key differences from other adapters:
 * - System messages go in separate `system` field (not in messages array)
 * - Structured output via tool_use (not native JSON mode)
 * - Response content is array of blocks (not string)
 * - max_tokens is required (not optional)
 * - Total tokens must be calculated (not provided)
 */
export class AnthropicAdapter implements LLMAdapter {
  private clientPromise: Promise<import('@anthropic-ai/sdk').default>
  private model: LLMModel

  constructor(config: AnthropicAdapterConfig) {
    // Lazy load Anthropic SDK
    this.clientPromise = import('@anthropic-ai/sdk').then(
      ({ default: AnthropicClass }) => new AnthropicClass({ apiKey: config.apiKey })
    )
    this.model = config.model
    // modelOptions reserved for future use (e.g., extended thinking)
  }

  /**
   * Generate structured output from messages using Anthropic Claude
   *
   * @param request - Generic generation request
   * @returns Structured output with usage metadata
   */
  async generate<T>(request: LLMGenerateRequest): Promise<LLMGenerateResponse<T>> {
    const client = await this.clientPromise

    try {
      // Extract system messages (Anthropic uses separate system field)
      const systemMessages = request.messages.filter((msg) => msg.role === 'system')
      const nonSystemMessages = request.messages.filter((msg) => msg.role !== 'system')

      // Build system content (combine multiple system messages)
      const systemContent =
        systemMessages.length > 0 ? systemMessages.map((msg) => msg.content).join('\n\n') : undefined

      // Convert messages to Anthropic format
      const anthropicMessages = nonSystemMessages.map((msg) => ({
        role: msg.role as 'user' | 'assistant',
        content: msg.content
      }))

      // Use tool_use for structured output (Anthropic's recommended approach)
      // This guarantees schema compliance without requiring JSON mode
      // Stream + finalMessage() to avoid SDK non-streaming timeout rejection
      // at high max_tokens values (SDK calculates estimated time > 10 min → error)
      const stream = client.messages.stream(
        {
          model: this.model,
          max_tokens: request.maxOutputTokens || 4000, // Required for Anthropic
          system: systemContent,
          messages: anthropicMessages,
          tools: [
            {
              name: 'structured_output',
              description: 'Return structured output matching the required schema',
              input_schema: request.responseSchema as import('@anthropic-ai/sdk').Anthropic.Tool['input_schema']
            }
          ],
          tool_choice: { type: 'tool', name: 'structured_output' },
          temperature: request.temperature,
          top_p: request.topP
        },
        { signal: composeSignal(DEFAULT_LLM_TIMEOUT, request.signal) }
      )
      const response = await stream.finalMessage()

      // Extract structured output from tool_use block
      const toolUseBlock = response.content.find((block) => block.type === 'tool_use')
      if (!toolUseBlock || toolUseBlock.type !== 'tool_use') {
        throw new Error('Anthropic response missing tool_use block for structured output')
      }
      const output = toolUseBlock.input as T

      // Calculate total tokens (Anthropic doesn't provide total_tokens)
      const usage = {
        inputTokens: response.usage.input_tokens,
        outputTokens: response.usage.output_tokens,
        totalTokens: response.usage.input_tokens + response.usage.output_tokens
      }

      return { output, usage }
    } catch (error) {
      // Add Better Stack logging for system errors (5xx only)
      if (isSystemError(error)) {
        bslogExternalServiceError('anthropic', 'messages.create', error as Error, {
          model: this.model
        })
      }

      throw error
    }
  }
}
