/**
 * LLM-related error classes
 * Thrown from adapter layer, caught by execution layer
 */

import { ExecutionError } from '../base/errors'

/**
 * Base class for LLM-related errors
 * Thrown from adapter layer, caught by execution layer
 *
 * Severity: warning (transient failures that may be retryable)
 * Category: llm (LLM provider errors)
 */
export class LLMError extends ExecutionError {
  readonly type: string = 'llm_error'
  readonly severity: 'warning' | 'critical' = 'warning' as const
  readonly category = 'llm' as const

  constructor(message: string, context?: Record<string, unknown>) {
    super(message, context)
  }

  override isRetryable(): boolean {
    return true
  }
}

/**
 * Thrown when token configuration is insufficient for the model
 * Wrapped as ExecutionError with code: AGENT_INSUFFICIENT_TOKENS
 *
 * Severity: critical (execution cannot continue without sufficient tokens)
 * Category: llm
 */
export class InsufficientTokensError extends LLMError {
  override readonly type = 'insufficient_tokens' as const
  override readonly severity = 'critical' as const

  constructor(message: string, context?: Record<string, unknown>) {
    super(message, context)
  }

  override isRetryable(): boolean {
    return false
  }
}

/**
 * Thrown when LLM hits token limit during generation
 * Wrapped as ExecutionError with code: AGENT_TOKEN_LIMIT_EXCEEDED
 *
 * Severity: critical (execution cannot continue, token limit exceeded)
 * Category: llm
 */
export class TokenLimitExceededError extends LLMError {
  override readonly type = 'token_limit_exceeded' as const
  override readonly severity = 'critical' as const

  constructor(message: string, context?: Record<string, unknown>) {
    super(message, context)
  }

  override isRetryable(): boolean {
    return false
  }
}

/**
 * Thrown when LLM response cannot be parsed as valid JSON
 * This is a transient error - the LLM may produce valid JSON on retry
 *
 * Severity: warning (transient failure that may succeed on retry)
 * Category: llm
 */
export class LLMResponseParseError extends LLMError {
  override readonly type = 'llm_response_parse_error' as const
  override readonly severity = 'warning' as const

  constructor(message: string, context?: Record<string, unknown>) {
    super(message, context)
  }

  override isRetryable(): boolean {
    return true // JSON parse failures are transient LLM errors
  }
}

/**
 * Thrown when model configuration is invalid
 * Provides structured information for debugging
 *
 * Severity: warning (configuration error that blocks execution but is user-correctable)
 * Category: validation (user/developer input validation)
 */
export class ModelConfigError extends ExecutionError {
  readonly type = 'model_config_error' as const
  readonly severity = 'warning' as const
  readonly category = 'validation' as const

  constructor(
    message: string,
    public field: string,
    public model: string,
    context?: Record<string, unknown>
  ) {
    super(message, { ...context, field, model })
  }
}

/**
 * Thrown when input is blocked by prompt injection sanitization
 * This is NOT a service failure - it means the security layer is working correctly
 *
 * Severity: warning (system working as designed, not a failure)
 * Category: validation (input validation, not infrastructure)
 */
export class LLMInputBlockedError extends ExecutionError {
  readonly type = 'input_blocked' as const
  readonly severity = 'warning' as const
  readonly category = 'validation' as const

  constructor(warnings: string[]) {
    super(`Input blocked: ${warnings.join(', ')} prompt injection patterns detected`, {
      warnings,
      patternCount: warnings.length
    })
  }

  override isRetryable(): boolean {
    return false // Same input will always be blocked
  }
}

// ============================================================================
// Model Configuration Validation
// ============================================================================

import { getModelInfo, type ModelConfig } from './model-info'

/**
 * Validate model configuration using consolidated Zod schema
 * Validates constraints (temperature, maxOutputTokens) AND model-specific options
 *
 * @example
 * // Valid config
 * validateModelConfig({ model: 'gpt-5', temperature: 1, modelOptions: { reasoning_effort: 'low' } }) // ✓
 *
 * // Invalid temperature
 * validateModelConfig({ model: 'gpt-5', temperature: 0.7 }) // ✗ Throws ModelConfigError
 *
 * // Invalid option
 * validateModelConfig({ model: 'mock', modelOptions: { reasoning_effort: 'low' } }) // ✗ Throws ModelConfigError
 *
 * @param config - Complete model configuration to validate
 * @throws {ModelConfigError} If validation fails (uses Zod for runtime validation)
 */
export function validateModelConfig(config: ModelConfig): void {
  const model = config.model as string

  if (!model) {
    throw new ModelConfigError('Missing model field', 'model', 'unknown')
  }

  const modelInfo = getModelInfo(model)

  if (!modelInfo) {
    throw new ModelConfigError(`Unknown model: ${model}`, 'model', model)
  }

  // If model has no schema, skip validation
  if (!modelInfo.configSchema) {
    return
  }

  // Validate using consolidated Zod schema
  const result = modelInfo.configSchema.safeParse(config)

  if (!result.success) {
    const firstError = result.error.issues[0]
    const fieldPath = firstError.path.join('.')
    const fieldName = fieldPath || 'config'

    throw new ModelConfigError(`Invalid config for ${model}: ${firstError.message}`, fieldName, model)
  }
}
