/**
 * LLM Call Tool Factory
 * Creates a tool that allows agents to make additional LLM calls during iteration
 */

import { z } from 'zod'
import type { Tool, ToolExecutionOptions } from '../../types'
import { createLLMAdapter } from '../../../llm/adapters/server/adapter-factory'
import type { ModelConfig } from '../../../llm/model-info'

/**
 * Input type for LLM call tool
 */
interface LLMCallToolInput {
  messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>
  responseSchema?: Record<string, unknown>
  temperature?: number
  maxOutputTokens?: number
}

/**
 * Check if a schema is valid (has a type field)
 * Invalid/placeholder schemas like { placeholderField: "ignored" } are rejected
 */
function isValidSchema(schema: unknown): schema is Record<string, unknown> {
  if (!schema || typeof schema !== 'object') return false
  const obj = schema as Record<string, unknown>
  // Valid JSON Schema must have a 'type' field (object, string, array, etc.)
  return 'type' in obj && typeof obj.type === 'string'
}

export interface LLMCallToolConfig {
  /** Model configuration (provider, model, API key) */
  modelConfig: ModelConfig
  /** Optional custom tool name (default: 'llm_call') */
  name?: string
  /** Optional custom description */
  description?: string
}

/**
 * Create an LLM call tool for agent use
 *
 * @example
 * ```typescript
 * const tool = createLLMCallTool({
 *   modelConfig: {
 *     provider: 'openrouter',
 *     model: 'openrouter/anthropic/claude-sonnet-4.5',
 *     apiKey: process.env.OPENROUTER_API_KEY!
 *   }
 * })
 *
 * // Add to agent definition
 * const agent: AgentDefinition = {
 *   tools: [tool, ...otherTools]
 * }
 * ```
 */
export function createLLMCallTool(config: LLMCallToolConfig): Tool {
  const toolName = config.name ?? 'llm_call'

  return {
    name: toolName,
    description:
      config.description ??
      'Make an LLM call with structured output. Use for text analysis, content generation, or AI assistance.',

    inputSchema: z.object({
      messages: z
        .array(
          z.object({
            role: z.enum(['system', 'user', 'assistant']),
            content: z.string()
          })
        )
        .min(1),
      responseSchema: z.record(z.string(), z.unknown()).optional(),
      temperature: z.number().min(0).max(2).optional(),
      maxOutputTokens: z.number().min(100).optional()
    }),

    outputSchema: z.unknown(),

    execute: async ({ input, executionContext, iterationContext }: ToolExecutionOptions) => {
      const { messages, responseSchema, temperature, maxOutputTokens } = input as LLMCallToolInput

      const adapter = createLLMAdapter(
        {
          ...config.modelConfig,
          ...(temperature !== undefined && { temperature }),
          ...(maxOutputTokens !== undefined && { maxOutputTokens })
        },
        executionContext?.aiUsageCollector,
        'tool',
        {
          type: 'tool',
          toolName,
          parentIteration: iterationContext?.iteration
        }
      )

      // Only pass responseSchema if it's a valid JSON Schema (has a 'type' field)
      // Invalid/placeholder schemas like { placeholderField: "ignored" } are skipped
      // This prevents Google API errors when the agent doesn't need structured output
      const validSchema = isValidSchema(responseSchema) ? responseSchema : undefined

      const response = await adapter.generate({
        messages,
        responseSchema: validSchema,
        temperature,
        maxOutputTokens
      })

      return response.output
    }
  }
}
