/**
 * Response class for LLM calls.
 */

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

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

/**
 * Initialization options for creating a Response.
 *
 * Accepts `tools` as either a Toolkit or a list of tools, which gets
 * converted to a Toolkit before passing to BaseResponse.
 */
export interface ResponseInit extends Omit<BaseResponseInit, "toolkit"> {
  /**
   * The tools available for this response.
   * Can be a Toolkit instance or an array of tools.
   */
  tools?: Tools | Toolkit;
}

/**
 * The response generated by an LLM.
 *
 * This is the primary response class for non-streaming LLM calls. Since TypeScript
 * IO is always async, this single class handles all async response scenarios.
 *
 * @template F - The type of the formatted output when using structured outputs.
 *
 * @example
 * ```typescript
 * const response = await model.call([user("Hello!")]);
 * console.log(response.text());
 * ```
 *
 * @example With structured output
 * ```typescript
 * interface Book { title: string; author: string; }
 * const response = await model.call<Book>('Recommend a book', { format: BookSchema });
 * const book = response.parse(); // Type: Book | DeepPartial<Book> | null
 * ```
 */
export class Response<F = unknown> extends BaseResponse<F> {
  /**
   * Override base toolkit with correct type for execute() support.
   */
  declare readonly toolkit: Toolkit;

  constructor(init: ResponseInit) {
    // Convert tools to toolkit (matching Python's pattern)
    const toolkit =
      init.tools instanceof Toolkit
        ? init.tools
        : new Toolkit(init.tools ?? []);

    super({ ...init, toolkit });
  }

  /**
   * Execute all tool calls in this response using the registered tools.
   *
   * @returns An array of ToolOutput objects, one for each tool call.
   *
   * @example
   * ```typescript
   * const response = await model.call('What is the weather?', [weatherTool]);
   * if (response.toolCalls.length > 0) {
   *   const outputs = await response.executeTools();
   *   const followUp = await response.resume(outputs);
   * }
   * ```
   */
  async executeTools(): Promise<ToolOutput<Jsonable>[]> {
    return Promise.all(
      this.toolCalls.map((call) => this.toolkit.execute(call)),
    );
  }

  /**
   * Generate a new Response 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 content - The new user message content to append to the message history.
   * @returns A new Response instance generated from the extended message history.
   *
   * @example
   * ```typescript
   * const response = await model.call('Hello!');
   * console.log(response.text());
   *
   * // Continue the conversation
   * const followUp = await response.resume('Tell me more about that');
   * console.log(followUp.text());
   * ```
   */
  async resume(content: UserContent): Promise<Response<F>> {
    const model = await this.model;
    return model.resume(this, content) as Promise<Response<F>>;
  }
}
