/**
 * ContextResponse class for context-based LLM calls.
 *
 * Extends BaseResponse with context-aware functionality including:
 * - executeTools(): Execute tool calls with context dependency injection
 * - resume(): Continue the conversation with additional user content
 */

import type { ToolOutput } from "@/llm/content/tool-output";
import type { Context } from "@/llm/context";
import type { UserContent } from "@/llm/messages";
import type { Jsonable } from "@/llm/types/jsonable";

import {
  BaseResponse,
  type BaseResponseInit,
} from "@/llm/responses/base-response";
import { ContextToolkit, type ContextTools } from "@/llm/tools";

/**
 * Initialization options for creating a ContextResponse.
 *
 * Accepts `tools` as either a ContextToolkit or a list of tools, which gets
 * converted to a ContextToolkit before passing to BaseResponse.
 *
 * Supports both regular tools (BaseTool) and context tools (BaseContextTool),
 * matching Python's `ContextTools[DepsT]` pattern.
 *
 * @template DepsT - The type of dependencies in the context.
 */
export interface ContextResponseInit<DepsT = unknown> extends Omit<
  BaseResponseInit,
  "toolkit"
> {
  /**
   * The tools available for this response.
   * Can be a ContextToolkit instance or an array of tools.
   * Accepts both regular tools and context tools.
   */
  tools?: ContextTools<DepsT> | ContextToolkit<DepsT>;
}

/**
 * The response generated by an LLM from a context call.
 *
 * This class provides context-aware functionality on top of the standard response:
 * - `executeTools()`: Execute all tool calls with context dependency injection
 * - `resume()`: Continue the conversation with additional user content
 *
 * @template DepsT - The type of dependencies in the context.
 * @template F - The type of the formatted output when using structured outputs.
 *
 * @example
 * ```typescript
 * interface MyDeps { userId: string; }
 *
 * const ctx = createContext<MyDeps>({ userId: '123' });
 * const response = await myPrompt(model, ctx);
 * console.log(response.text());
 *
 * // Continue the conversation
 * const followUp = await response.resume(ctx, 'Tell me more');
 * ```
 */
export class ContextResponse<
  DepsT = unknown,
  F = unknown,
> extends BaseResponse<F> {
  /**
   * The context toolkit containing tools that can receive context.
   */
  readonly contextToolkit: ContextToolkit<DepsT>;

  constructor(init: ContextResponseInit<DepsT>) {
    // Convert tools to context toolkit (matching Python's pattern)
    const contextToolkit =
      init.tools instanceof ContextToolkit
        ? init.tools
        : new ContextToolkit(init.tools ?? []);

    // Pass an empty toolkit to base since context responses use contextToolkit
    super({ ...init, toolkit: contextToolkit });

    this.contextToolkit = contextToolkit;
  }

  /**
   * Execute all tool calls in this response using the registered context tools.
   *
   * @param ctx - The context containing dependencies to pass to tools.
   * @returns An array of ToolOutput objects, one for each tool call.
   *
   * @example
   * ```typescript
   * const response = await myPrompt(model, ctx, [searchTool]);
   * if (response.toolCalls.length > 0) {
   *   const outputs = await response.executeTools(ctx);
   *   const followUp = await response.resume(ctx, outputs);
   * }
   * ```
   */
  async executeTools(ctx: Context<DepsT>): Promise<ToolOutput<Jsonable>[]> {
    return this.contextToolkit.executeAll(ctx, this.toolCalls);
  }

  /**
   * Generate a new ContextResponse using this response's messages with additional user content.
   *
   * Uses this response's tools and format type. Also uses this response's provider,
   * model, and params.
   *
   * @param ctx - A Context with the required deps type.
   * @param content - The new user message content to append to the message history.
   * @returns A new ContextResponse instance generated from the extended message history.
   *
   * @example
   * ```typescript
   * const response = await myPrompt(model, ctx);
   * console.log(response.text());
   *
   * // Continue the conversation
   * const followUp = await response.resume(ctx, 'Tell me more about that');
   * console.log(followUp.text());
   * ```
   */
  async resume(
    ctx: Context<DepsT>,
    content: UserContent,
  ): Promise<ContextResponse<DepsT, F>> {
    const model = await this.model;
    return model.contextResume(ctx, this, content) as Promise<
      ContextResponse<DepsT, F>
    >;
  }
}
