import { ITool } from './tool';

/**
 * Represents a tool call generated by an LLM.
 */
export interface ToolCall {
  /** Unique identifier for the tool call. */
  id: string;
  /** The type of the call, currently only 'function' is supported. */
  type: 'function';
  /** The specific function being called and its arguments. */
  function: {
    /** The name of the tool to execute. */
    name: string;
    /** JSON-formatted string of arguments for the tool. */
    arguments: string;
  };
}

/**
 * Valid roles for messages in a conversation.
 */
export enum MessageRole {
  /** Input from the human user. */
  USER = 'user',
  /** Response from the AI agent. */
  ASSISTANT = 'assistant',
  /** System-level instructions or constraints. */
  SYSTEM = 'system',
  /** Output from a tool execution. */
  TOOL = 'tool',
  /** Developer-level metadata or instructions. */
  DEVELOPER = 'developer',
}

/**
 * Types of attachments that can be included in messages.
 */
export enum AttachmentType {
  /** Image attachment (photos, screenshots, diagrams). */
  IMAGE = 'image',
  /** File attachment (documents, code files, data). */
  FILE = 'file',
}

/**
 * Shared attachment shape used across messages and events.
 * Enforces that at least one payload source is present.
 */
export type Attachment = {
  type: AttachmentType;
  name?: string;
  mimeType?: string;
} & ({ url: string; base64?: string } | { base64: string; url?: string });

/**
 * Type guard to check if an unknown object is a valid Attachment.
 */
export const isValidAttachment = (rawAtt: unknown): rawAtt is Attachment => {
  if (!rawAtt || typeof rawAtt !== 'object') return false;
  const a = rawAtt as Record<string, unknown>;
  if (typeof a.url === 'string' && a.url.length > 0) return true;
  if (typeof a.base64 === 'string' && a.base64.length > 0) return true;
  return false;
};

/**
 * UI button/option types for interactive agent signals.
 */
export enum ButtonType {
  /** Primary action button (most important action). */
  PRIMARY = 'primary',
  /** Secondary action button (alternative action). */
  SECONDARY = 'secondary',
  /** Danger/destructive action button (requires caution). */
  DANGER = 'danger',
}

/**
 * A single message in a conversation thread.
 * Fields are made required to improve contract enforcement and eliminate fallback cascades.
 */
export interface Message {
  /** The role of the message sender. */
  role: MessageRole;
  /** The textual content of the message. */
  content: string;
  /** Intermediate reasoning content (e.g. <thought> tags). */
  thought?: string;
  /** Tool calls requested by the assistant. */
  tool_calls?: ToolCall[];
  /** The ID of the tool call this message is responding to (if role is TOOL). */
  tool_call_id?: string;
  /** The name of the tool or agent this message is responding from. */
  name?: string;
  /** Human-readable name of the agent that generated this message. */
  agentName?: string;
  /** Required link to the isolated mechanical monologue for this message */
  traceId: string;
  /** Required unique identifier for the message. */
  messageId: string;
  /**
   * Workspace identifier for multi-tenant isolation.
   */
  workspaceId?: string;
  /**
   * Attachments (images, files) associated with the message.
   */
  attachments?: Attachment[];
  /**
   * Optional usage statistics (tokens) for the message (usually only for ASSISTANT).
   */
  usage?: {
    prompt_tokens: number;
    completion_tokens: number;
    total_tokens?: number;
    tool_calls?: number;
  };
  /**
   * Optional name of the model that generated this message.
   */
  modelName?: string;
  /**
   * UI options (buttons) associated with the message.
   */
  options?: Array<{
    label: string;
    value: string;
    type?: ButtonType;
  }>;
  /**
   * Optional page context attached by the user (URL, title, page data, etc.)
   */
  pageContext?: {
    url: string;
    title?: string;
    data?: Record<string, unknown>;
    traceId?: string;
    sessionId?: string;
    agentId?: string;
  };
  /**
   * Dynamic UI blocks rendered by the assistant.
   */
  ui_blocks?: Array<{
    id: string;
    componentType: string;
    props: Record<string, unknown>;
    actions?: Array<{
      id: string;
      label: string;
      type: 'primary' | 'secondary' | 'danger';
      payload?: Record<string, unknown>;
    }>;
  }>;
}

/**
 * Factory function to create a Message with sensible defaults for optional fields.
 */
export function createMessage(params: {
  role: MessageRole;
  content: string;
  traceId: string;
  messageId: string;
  thought?: string;
  tool_calls?: ToolCall[];
  tool_call_id?: string;
  name?: string;
  agentName?: string;
  attachments?: Attachment[];
  usage?: {
    prompt_tokens: number;
    completion_tokens: number;
    total_tokens?: number;
    tool_calls?: number;
  };
  modelName?: string;
  options?: Array<{
    label: string;
    value: string;
    type?: ButtonType;
  }>;
  pageContext?: {
    url: string;
    title?: string;
    data?: Record<string, unknown>;
    traceId?: string;
    sessionId?: string;
    agentId?: string;
  };
  ui_blocks?: Array<{
    id: string;
    componentType: string;
    props: Record<string, unknown>;
    actions?: Array<{
      id: string;
      label: string;
      type: 'primary' | 'secondary' | 'danger';
      payload?: Record<string, unknown>;
    }>;
  }>;
  workspaceId?: string;
}): Message {
  return {
    role: params.role,
    content: params.content,
    thought: params.thought ?? '',
    tool_calls: params.tool_calls ?? [],
    tool_call_id: params.tool_call_id,
    name: params.name,
    agentName: params.agentName ?? 'SYSTEM',
    traceId: params.traceId,
    messageId: params.messageId,
    attachments: params.attachments ?? [],
    usage: params.usage,
    modelName: params.modelName,
    options: params.options ?? [],
    pageContext: params.pageContext,
    ui_blocks: params.ui_blocks ?? [],
    workspaceId: params.workspaceId ?? 'default',
  };
}

/**
 * Profiles that define the computational intensity and 'thinking' depth of the LLM.
 */
export enum ReasoningProfile {
  /** Prioritizes speed and cost for simple tasks. */
  FAST = 'fast',
  /** Balanced profile for standard agent interactions. */
  STANDARD = 'standard',
  /** Enables intermediate reasoning steps for complex problems. */
  THINKING = 'thinking',
  /** Maximum reasoning depth for critical strategic tasks. */
  DEEP = 'deep',
}

/**
 * Supported LLM service providers.
 */
export enum LLMProvider {
  /** OpenAI (GPT models). */
  OPENAI = 'openai',
  /** AWS Bedrock (Claude, Llama models). */
  BEDROCK = 'bedrock',
  /** OpenRouter (Aggregator for multiple providers). */
  OPENROUTER = 'openrouter',
  /** MiniMax (Direct API via Anthropic SDK). */
  MINIMAX = 'minimax',
  /** Anthropic (Claude models). */
  ANTHROPIC = 'anthropic',
  /** DeepSeek (Direct API, OpenAI-compatible). */
  DEEPSEEK = 'deepseek',
  /** Mock provider for testing. */
  MOCK = 'mock',
}

/**
 * Specific model identifiers for DeepSeek.
 */
export enum DeepSeekModel {
  /** DeepSeek V4 Flash (fast, cost-efficient). */
  V4_FLASH = 'deepseek-v4-flash',
  /** DeepSeek V4 Pro (max reasoning capability). */
  V4_PRO = 'deepseek-v4-pro',
}

/**
 * Specific model identifiers for OpenAI.
 */
export enum OpenAIModel {
  /** High-tier reasoning model (GPT-5.4 family) */
  GPT_5_4 = 'gpt-5.4',
  /** Balanced standard reasoning model (GPT-5.4 family) */
  GPT_5_4_MINI = 'gpt-5.4-mini',
  /** Fast, cost-efficient reasoning model (GPT-5.4 family) */
  GPT_5_4_NANO = 'gpt-5.4-nano',
  /** Mini reasoning model (GPT-5 family) */
  GPT_5_MINI = 'gpt-5-mini',
}

/**
 * Specific model identifiers for AWS Bedrock.
 */
export enum BedrockModel {
  CLAUDE_4_6 = 'global.anthropic.claude-sonnet-4-6',
}

/**
 * Specific model identifiers for OpenRouter.
 */
export enum OpenRouterModel {
  GLM_5 = 'zhipu/glm-5',
  GEMINI_3_FLASH = 'google/gemini-3-flash-preview',
}

/**
 * Specific model identifiers for MiniMax (Direct API).
 */
export enum MiniMaxModel {
  /** M2.7 with interleaved thinking and advanced reasoning (60 tps). */
  M2_7 = 'MiniMax-M2.7',
  /** M2.7 highspeed variant with same performance but faster output (100 tps). */
  M2_7_HIGHSPEED = 'MiniMax-M2.7-highspeed',
}

/**
 * Describes the capabilities supported by a specific LLM model or provider.
 */
export interface ICapabilities {
  /** List of reasoning profiles supported by this configuration. */
  supportedReasoningProfiles: ReasoningProfile[];
  /** Optional cap for reasoning effort (e.g. 'high' for mini models). */
  maxReasoningEffort?: string;
  /** Whether the model supports structured JSON output (json_schema). */
  supportsStructuredOutput?: boolean;
  /** The maximum context window size (tokens) for this model. */
  contextWindow: number;
  /** Which attachment types this provider/model can process. */
  supportedAttachmentTypes?: AttachmentType[];
}

/**
 * Format specification for the LLM response, used to enforce structured output.
 */
export type ResponseFormat =
  | { type: 'text' }
  | { type: 'json_object' }
  | {
      type: 'json_schema';
      json_schema: {
        /** A descriptive name for the schema. */
        name: string;
        /** Optional description of what the response format is for. */
        description?: string;
        /** Whether to strictly enforce the schema (OpenAI specific). */
        strict: boolean;
        /** The actual JSON schema object. */
        schema: Record<string, unknown>;
      };
    };

/**
 * A partial chunk of a message during streaming.
 * Fields are made non-nullable where possible to eliminate fallback cascades.
 */
export interface MessageChunk {
  /** The specific role (usually assistant). */
  role?: MessageRole;
  /** Partial text content. */
  content?: string;
  /** Partial reasoning content. */
  thought?: string;
  /** Partial tool calls. */
  tool_calls?: ToolCall[];
  /** Optional UI options (buttons) for the user. */
  options?: Array<{
    label: string;
    value: string;
    type?: string;
  }>;
  /** Optional agent name for identifying the source of the chunk. */
  agentName?: string;
  /** Optional message ID for UI reconciliation. */
  messageId?: string;
  /** Optional type for EventBridge compatibility. */
  'detail-type'?: string;
  /** Optional usage stats (usually in the final chunk). */
  usage?: {
    prompt_tokens: number;
    completion_tokens: number;
    total_tokens?: number;
    tool_calls?: number;
  };
  /**
   * Optional attachments (images, files) during streaming.
   */
  attachments?: Attachment[];
  /**
   * Optional dynamic UI blocks during streaming.
   */
  ui_blocks?: Message['ui_blocks'];
  /** Optional model name for streaming chunks. */
  modelName?: string;
}

/**
 * Interface for LLM provider implementations.
 */
export interface IProvider {
  /**
   * Performs a completion call to the LLM.
   */
  call(
    messages: Message[],
    tools?: ITool[],
    profile?: ReasoningProfile,
    model?: string,
    provider?: string,
    responseFormat?: ResponseFormat,
    temperature?: number,
    maxTokens?: number,
    topP?: number,
    stopSequences?: string[]
  ): Promise<Message>;

  /**
   * Performs a streaming completion call to the LLM.
   */
  stream(
    messages: Message[],
    tools?: ITool[],
    profile?: ReasoningProfile,
    model?: string,
    provider?: string,
    responseFormat?: ResponseFormat,
    temperature?: number,
    maxTokens?: number,
    topP?: number,
    stopSequences?: string[]
  ): AsyncIterable<MessageChunk>;

  /**
   * Retrieves the capabilities of a specific model.
   */
  getCapabilities(model?: string): Promise<ICapabilities>;
}
