import {
  IJsonParseResult,
  ILlmApplication,
  ILlmController,
  ILlmSchema,
  ILlmStructuredOutput,
} from "@typia/interface";

import { NoTransformConfigurationError } from "./transformers/NoTransformConfigurationError";

/* -----------------------------------------------------------
  FUNCTION CALLING
----------------------------------------------------------- */
/**
 * Creates LLM function calling controller.
 *
 * @danger You must configure the generic argument `Class`
 */
export function controller(
  name: string,
  execute: object,
  config?: Partial<Pick<ILlmApplication.IConfig<any>, "validate">>,
): never;

/**
 * Creates LLM function calling controller from class/interface.
 *
 * Generates {@link ILlmController} from a TypeScript class or interface,
 * containing both function calling schemas ({@link ILlmFunction}) and an
 * executor ({@link ILlmController.execute}).
 *
 * Each {@link ILlmFunction} includes a built-in {@link ILlmFunction.validate}
 * function that validates LLM-generated arguments before execution. When
 * validation fails, use `LlmJson.stringify()` from `@typia/utils` to format
 * errors for LLM feedback, enabling auto-correction.
 *
 * When passed to LLM providers (ChatGPT, Claude, Gemini, etc.), the LLM
 * automatically selects functions and fills arguments from conversation.
 * Execute the selected function via {@link ILlmController.execute}.
 *
 * Related functions:
 *
 * - {@link application} — Schemas only, without executor
 * - {@link parameters} — Single parameters schema for structured output
 * - {@link schema} — Single type schema
 *
 * @template Class Target class or interface type
 * @template Config LLM schema configuration
 * @param name Controller identifier name
 * @param execute Executor instance
 * @param config LLM application options
 * @returns LLM function calling controller
 */
export function controller<
  Class extends Record<string, any>,
  Config extends Partial<
    ILlmSchema.IConfig & {
      /**
       * Whether to disallow superfluous properties or not.
       *
       * If configure as `true`, {@link validateEquals} function would be used
       * for validation feedback, which is more strict than {@link validate}
       * function.
       *
       * @default false
       */
      equals: boolean;
    }
  > = {},
>(
  name: string,
  execute: Class,
  config?: Partial<Pick<ILlmApplication.IConfig<Class>, "validate">>,
): ILlmController<Class>;

/** @internal */
export function controller(..._args: any[]): never {
  NoTransformConfigurationError("llm.controller");
}

/**
 * Creates LLM function calling application.
 *
 * @danger You must configure the generic argument `Class`
 */
export function application(
  config?: Partial<Pick<ILlmApplication.IConfig<any>, "validate">>,
): never;

/**
 * Creates LLM function calling application from class/interface.
 *
 * Generates {@link ILlmApplication} from a TypeScript class or interface,
 * containing function calling schemas ({@link ILlmFunction}). Does not include
 * an executor—use {@link controller} if you need execution capability.
 *
 * Each {@link ILlmFunction} includes a built-in {@link ILlmFunction.validate}
 * function that validates LLM-generated arguments before execution. When
 * validation fails, use `LlmJson.stringify()` from `@typia/utils` to format
 * errors for LLM feedback, enabling auto-correction.
 *
 * When passed to LLM providers (ChatGPT, Claude, Gemini, etc.), the LLM
 * automatically selects functions and fills arguments from conversation. You
 * execute the function manually with the LLM-prepared arguments.
 *
 * Related functions:
 *
 * - {@link controller} — Includes executor alongside schemas
 * - {@link parameters} — Single parameters schema for structured output
 * - {@link schema} — Single type schema
 *
 * @template Class Target class or interface type
 * @template Config LLM schema configuration
 * @param config LLM application options
 * @returns LLM function calling application
 */
export function application<
  Class extends Record<string, any>,
  Config extends Partial<
    ILlmSchema.IConfig & {
      /**
       * Whether to disallow superfluous properties or not.
       *
       * If configure as `true`, {@link validateEquals} function would be used
       * for validation feedback, which is more strict than {@link validate}
       * function.
       *
       * @default false
       */
      equals: boolean;
    }
  > = {},
>(
  config?: Partial<Pick<ILlmApplication.IConfig<Class>, "validate">>,
): ILlmApplication<Class>;

/** @internal */
export function application(): never {
  NoTransformConfigurationError("llm.application");
}

/**
 * Creates LLM structured output interface.
 *
 * @danger You must configure the generic argument `T`
 */
export function structuredOutput(): never;

/**
 * Creates LLM structured output interface from TypeScript object type.
 *
 * Generates {@link ILlmStructuredOutput} containing everything needed for
 * handling LLM structured outputs: the JSON schema for prompting, and functions
 * for parsing, coercing, and validating responses.
 *
 * Structured outputs allow LLMs to generate data conforming to a predefined
 * schema instead of free-form text. This is useful for:
 *
 * - Extracting structured data from conversations
 * - Generating typed responses for downstream processing
 * - Ensuring consistent output formats across LLM calls
 *
 * Workflow:
 *
 * 1. Pass {@link ILlmStructuredOutput.parameters} schema to LLM provider
 * 2. Receive LLM response (JSON string or pre-parsed object)
 * 3. Use {@link ILlmStructuredOutput.parse} for raw strings or
 *    {@link ILlmStructuredOutput.coerce} for pre-parsed objects
 * 4. Use {@link ILlmStructuredOutput.validate} to check the result
 *
 * Related functions:
 *
 * - {@link parameters} — Schema only, without parse/coerce/validate
 * - {@link application} — Multiple function schemas from class/interface
 * - {@link controller} — Application with executor
 *
 * @template T Target output type (object with static properties)
 * @template Config LLM schema configuration
 * @returns LLM structured output interface
 */
export function structuredOutput<
  T extends Record<string, any>,
  Config extends Partial<
    ILlmSchema.IConfig & {
      /**
       * Whether to disallow superfluous properties or not.
       *
       * If configure as `true`, {@link validateEquals} function would be used
       * for validation feedback, which is more strict than {@link validate}
       * function.
       *
       * @default false
       */
      equals: boolean;
    }
  > = {},
>(): ILlmStructuredOutput<T>;

/** @internal */
export function structuredOutput(): never {
  NoTransformConfigurationError("llm.structuredOutput");
}

/* -----------------------------------------------------------
  RAW SCHEMAS
----------------------------------------------------------- */
/**
 * Creates LLM parameters schema.
 *
 * @danger You must configure the generic argument `Parameters`
 */
export function parameters(): never;

/**
 * Creates LLM parameters schema from TypeScript object type.
 *
 * Generates {@link ILlmSchema.IParameters} for LLM function calling or
 * structured outputs. LLMs use keyworded arguments only, so the type must be an
 * object with static properties (no dynamic properties allowed).
 *
 * Use cases:
 *
 * - Function calling: LLM fills parameters from conversation
 * - Structured outputs: LLM generates structured data, not plain text
 *
 * Related functions:
 *
 * - {@link application} — Multiple function schemas from class/interface
 * - {@link controller} — Application with executor
 * - {@link schema} — Single type schema (not parameters-specific)
 *
 * @template Parameters Target parameters type (object with static properties)
 * @template Config LLM schema configuration
 * @returns LLM parameters schema
 */
export function parameters<
  Parameters extends Record<string, any>,
  Config extends Partial<ILlmSchema.IConfig> = {},
>(): ILlmSchema.IParameters;

/** @internal */
export function parameters(): never {
  NoTransformConfigurationError("llm.parameters");
}

/**
 * Creates LLM type schema.
 *
 * @danger You must configure the generic argument `T`
 */
export function schema(): never;

/**
 * Creates LLM type schema from TypeScript type.
 *
 * Generates {@link ILlmSchema} for use in LLM function calling. For actual
 * function calling with TypeScript functions, use {@link application}. For
 * structured output generation, use {@link parameters}.
 *
 * LLM function calling flow:
 *
 * 1. LLM selects function and fills arguments from conversation
 * 2. You execute the function with LLM-prepared arguments
 * 3. Return value is passed back to LLM via system prompt
 * 4. LLM continues conversation based on return value
 *
 * Related functions:
 *
 * - {@link application} — Multiple function schemas from class/interface
 * - {@link controller} — Application with executor
 * - {@link parameters} — Parameters schema for structured output
 *
 * @template T Target type
 * @template Config LLM schema configuration
 * @param $defs Shared schema definitions for `$ref` referencing
 * @returns LLM type schema
 */
export function schema<T, Config = {}>(
  $defs: Record<string, ILlmSchema>,
): ILlmSchema;

/** @internal */
export function schema(): never {
  NoTransformConfigurationError("llm.schema");
}

/* -----------------------------------------------------------
  UTILITY FUNCTIONS
----------------------------------------------------------- */
/**
 * Parse LLM response JSON with type coercion.
 *
 * @danger You must configure the generic argument `Parameters`
 */
export function parse(input: string): never;

/**
 * Parse lenient JSON with schema-based type coercion.
 *
 * Handles incomplete or malformed JSON commonly produced by LLMs:
 *
 * - Unclosed brackets, strings, trailing commas
 * - JavaScript-style comments (`//` and multi-line)
 * - Unquoted object keys, incomplete keywords (`tru`, `fal`, `nul`)
 * - Markdown code block extraction, junk prefix skipping
 *
 * Also coerces double-stringified values based on the `Parameters` schema:
 *
 * - `"42"` → `42` (when schema expects number)
 * - `"true"` → `true` (when schema expects boolean)
 * - `"null"` → `null` (when schema expects null)
 * - `"{...}"` → `{...}` (when schema expects object)
 * - `"[...]"` → `[...]` (when schema expects array)
 *
 * Type validation is NOT performed—use {@link ILlmFunction.validate} or
 * `typia.validate()` for that.
 *
 * For repeated parsing, use {@link createParse} to avoid regenerating the schema
 * each time.
 *
 * Related functions:
 *
 * - {@link createParse} — Create reusable parser function
 * - {@link coerce} — Type coercion for already-parsed objects
 * - {@link parameters} — Generate parameters schema from type
 *
 * @template Parameters Target parameters type (object with static properties)
 * @template Config LLM schema configuration
 * @param input Raw JSON string (potentially incomplete or malformed)
 * @returns Parse result with typed data on success, or partial data with errors
 */
export function parse<
  Parameters extends Record<string, any>,
  Config extends Partial<ILlmSchema.IConfig> = {},
>(input: string): IJsonParseResult<Parameters>;

/** @internal */
export function parse(): never {
  NoTransformConfigurationError("llm.parse");
}

/**
 * Coerce LLM arguments to match expected schema types.
 *
 * LLMs often return values with incorrect types (e.g., numbers as strings).
 * This function recursively coerces values based on the `Parameters` schema:
 *
 * - `"42"` → `42` (when schema expects number)
 * - `"true"` → `true` (when schema expects boolean)
 * - `"null"` → `null` (when schema expects null)
 * - `"{...}"` → `{...}` (when schema expects object)
 * - `"[...]"` → `[...]` (when schema expects array)
 *
 * Use this when your SDK provides already-parsed objects but values may have
 * wrong types. For raw JSON strings, use {@link parse} instead.
 *
 * For repeated coercion, use {@link createCoerce} to avoid regenerating the
 * schema each time.
 *
 * Type validation is NOT performed—use {@link ILlmFunction.validate} or
 * `typia.validate()` for that.
 *
 * Related functions:
 *
 * - {@link createCoerce} — Create reusable coercer function
 * - {@link parse} — Parse and coerce raw JSON strings
 * - {@link parameters} — Generate parameters schema from type
 *
 * @template Parameters Target parameters type (object with static properties)
 * @template Config LLM schema configuration
 * @param input Parsed arguments object from LLM (with potentially wrong types)
 * @returns Coerced arguments with corrected types
 */
export function coerce<
  Parameters extends Record<string, any>,
  Config extends Partial<ILlmSchema.IConfig> = {},
>(input: Parameters): Parameters;

/** @internal */
export function coerce(): never {
  NoTransformConfigurationError("llm.coerce");
}

/**
 * Create reusable LLM JSON parser with type coercion.
 *
 * @danger You must configure the generic argument `Parameters`
 */
export function createParse(): never;

/**
 * Create reusable lenient JSON parser with schema-based type coercion.
 *
 * Returns a parser function that handles incomplete or malformed JSON commonly
 * produced by LLMs:
 *
 * - Unclosed brackets, strings, trailing commas
 * - JavaScript-style comments (`//` and multi-line)
 * - Unquoted object keys, incomplete keywords (`tru`, `fal`, `nul`)
 * - Markdown code block extraction, junk prefix skipping
 *
 * Also coerces double-stringified values based on the `Parameters` schema:
 *
 * - `"42"` → `42` (when schema expects number)
 * - `"true"` → `true` (when schema expects boolean)
 * - `"null"` → `null` (when schema expects null)
 * - `"{...}"` → `{...}` (when schema expects object)
 * - `"[...]"` → `[...]` (when schema expects array)
 *
 * Use this instead of {@link parse} when parsing multiple inputs to avoid
 * regenerating the schema each time.
 *
 * Type validation is NOT performed—use {@link ILlmFunction.validate} or
 * `typia.validate()` for that.
 *
 * Related functions:
 *
 * - {@link parse} — One-shot parsing (regenerates schema each call)
 * - {@link createCoerce} — Create reusable coercer function
 * - {@link parameters} — Generate parameters schema from type
 *
 * @template Parameters Target parameters type (object with static properties)
 * @template Config LLM schema configuration
 * @returns Reusable parser function
 */
export function createParse<
  Parameters extends Record<string, any>,
  Config extends Partial<ILlmSchema.IConfig> = {},
>(): (input: string) => IJsonParseResult<Parameters>;

/** @internal */
export function createParse(): never {
  NoTransformConfigurationError("llm.createParse");
}

/**
 * Create reusable LLM arguments coercer.
 *
 * @danger You must configure the generic argument `Parameters`
 */
export function createCoerce(): never;

/**
 * Create reusable coercer for LLM arguments.
 *
 * Returns a coercer function that fixes incorrect types commonly returned by
 * LLMs (e.g., numbers as strings). Coerces values based on the `Parameters`
 * schema:
 *
 * - `"42"` → `42` (when schema expects number)
 * - `"true"` → `true` (when schema expects boolean)
 * - `"null"` → `null` (when schema expects null)
 * - `"{...}"` → `{...}` (when schema expects object)
 * - `"[...]"` → `[...]` (when schema expects array)
 *
 * Use this instead of {@link coerce} when coercing multiple inputs to avoid
 * regenerating the schema each time.
 *
 * Type validation is NOT performed—use {@link ILlmFunction.validate} or
 * `typia.validate()` for that.
 *
 * Related functions:
 *
 * - {@link coerce} — One-shot coercion (regenerates schema each call)
 * - {@link createParse} — Create reusable parser function
 * - {@link parameters} — Generate parameters schema from type
 *
 * @template Parameters Target parameters type (object with static properties)
 * @template Config LLM schema configuration
 * @returns Reusable coercer function
 */
export function createCoerce<
  Parameters extends Record<string, any>,
  Config extends Partial<ILlmSchema.IConfig> = {},
>(): (input: Parameters) => Parameters;

/** @internal */
export function createCoerce(): never {
  NoTransformConfigurationError("llm.createCoerce");
}
