import type { JSONObject } from "@ai-sdk/provider";

import type {
  ContentExpectations,
  KeyedExpectations,
  RawContentExpectations,
  RawKeyedExpectations,
  RawValueExpectation,
  ValueExpectation,
} from "../../expect/types";
import type { ResolvedTargetBase } from "../../types";

export interface LlmCheckObservation {
  finishReason: null | string;
  http: LlmHttpMetadata | null;
  mode: LlmMode;
  model: string;
  outputText: null | string;
  provider: LlmProvider;
  rawFinishReason: null | string;
  stream: LlmStreamObservation | null;
  usage: LlmUsageObservation | null;
  warnings: string[];
}

export interface LlmDefaultsConfig {
  headers?: Record<string, string>;
  ignoreSSL?: boolean;
  mode?: LlmMode;
  options?: LlmOptions;
  providerOptions?: Record<string, JSONObject>;
}

export interface LlmHttpMetadata {
  headers: Record<string, string>;
  status: number;
  statusText: string;
}

export interface LlmPersistedHttpMetadata {
  [key: string]: unknown;
  headers: Record<string, string>;
  status: number;
  statusText: string;
}

export interface LlmPersistedObservation {
  [key: string]: unknown;
  finishReason: null | string;
  http: LlmPersistedHttpMetadata | null;
  mode: LlmMode;
  model: string;
  outputLength: null | number;
  outputPreview: null | string;
  provider: LlmProvider;
  rawFinishReason: null | string;
  stream: LlmStreamObservation | null;
  usage: LlmUsageObservation | null;
  warnings: string[];
}

export const LLM_HEADERS_MAX = 20;
export const LLM_OUTPUT_PREVIEW_MAX = 512;

export type LlmMode = "http" | "stream";

export interface LlmOptions {
  frequencyPenalty?: number;
  maxOutputTokens?: number;
  presencePenalty?: number;
  seed?: number;
  stopSequences?: string[];
  temperature?: number;
  topK?: number;
  topP?: number;
}

export type LlmProvider = "anthropic" | "openai" | "openai-responses";

export interface LlmStreamObservation {
  completed: boolean;
  firstTokenMs: null | number;
}

export interface LlmTargetConfig {
  authToken?: string;
  headers?: Record<string, string>;
  ignoreSSL?: boolean;
  key?: string;
  mode?: LlmMode;
  model: string;
  options?: LlmOptions;
  prompt: string;
  provider: LlmProvider;
  providerOptions?: Record<string, JSONObject>;
  url: string;
}

export interface LlmUsageObservation {
  inputTokens: number;
  outputTokens: number;
  totalTokens: number;
}

export interface RawLlmExpectConfig {
  durationMs?: RawValueExpectation;
  finishReason?: RawValueExpectation;
  headers?: RawKeyedExpectations;
  output?: RawContentExpectations;
  rawFinishReason?: RawValueExpectation;
  status?: Array<number | string>;
  stream?: RawLlmStreamExpect;
  usage?: RawLlmUsageExpect;
}

export interface RawLlmStreamExpect {
  completed?: boolean;
  firstTokenMs?: RawValueExpectation;
}

export interface RawLlmUsageExpect {
  inputTokens?: RawValueExpectation;
  outputTokens?: RawValueExpectation;
  totalTokens?: RawValueExpectation;
}

export interface ResolvedLlmConfig {
  authToken?: string;
  headers: Record<string, string>;
  ignoreSSL: boolean;
  key: string;
  mode: LlmMode;
  model: string;
  options: LlmOptions;
  prompt: string;
  provider: LlmProvider;
  providerOptions: Record<string, JSONObject>;
  url: string;
}

export interface ResolvedLlmExpectConfig {
  durationMs?: ValueExpectation;
  finishReason?: ValueExpectation;
  headers?: KeyedExpectations;
  output?: ContentExpectations;
  rawFinishReason?: ValueExpectation;
  status: Array<number | string>;
  stream?: ResolvedLlmStreamExpect;
  usage?: ResolvedLlmUsageExpect;
}

export interface ResolvedLlmStreamExpect {
  completed: boolean;
  firstTokenMs?: ValueExpectation;
}

export interface ResolvedLlmTarget extends ResolvedTargetBase {
  expect?: ResolvedLlmExpectConfig;
  group: string;
  intervalMs: number;
  llm: ResolvedLlmConfig;
  name: null | string;
  rawExpect?: RawLlmExpectConfig;
  timeoutMs: number;
  type: "llm";
}

export interface ResolvedLlmUsageExpect {
  inputTokens?: ValueExpectation;
  outputTokens?: ValueExpectation;
  totalTokens?: ValueExpectation;
}
