defmodule ExLLM.Types do
  @moduledoc """
  Shared type definitions used across ExLLM modules.

  This module contains struct definitions and types that are used by multiple
  modules, helping to avoid circular dependencies.
  """

  # Type definitions must come first
  @type provider ::
          :anthropic
          | :openai
          | :gemini
          | :groq
          | :ollama
          | :openrouter
          | :mistral
          | :perplexity
          | :lmstudio
          | :bumblebee
          | :bedrock
          | :mock
          | atom()

  @type message :: %{
          required(:role) => String.t(),
          required(:content) => String.t() | list(content_part()),
          optional(:timestamp) => DateTime.t(),
          optional(atom()) => any()
        }

  @type content_part :: text_content() | image_content()

  @type text_content :: %{
          type: :text,
          text: String.t()
        }

  @type image_content :: %{
          type: :image_url | :image,
          image_url: image_url() | nil,
          image: image_data() | nil
        }

  @type image_url :: %{
          url: String.t(),
          detail: :auto | :low | :high | nil
        }

  @type image_data :: %{
          # Base64 encoded image
          data: String.t(),
          # e.g., "image/jpeg", "image/png"
          media_type: String.t()
        }

  @type token_usage :: %{
          input_tokens: non_neg_integer(),
          output_tokens: non_neg_integer()
        }

  @type pricing :: %{
          input_cost_per_token: float() | nil,
          output_cost_per_token: float() | nil,
          currency: String.t()
        }

  @type provider_options :: keyword()

  @type stream :: Enumerable.t()

  @type cost_result :: %{
          provider: String.t(),
          model: String.t(),
          input_tokens: non_neg_integer(),
          output_tokens: non_neg_integer(),
          total_tokens: non_neg_integer(),
          input_cost: float(),
          output_cost: float(),
          total_cost: float(),
          currency: String.t(),
          pricing: %{input: float(), output: float()}
        }

  defmodule LLMResponse do
    @moduledoc """
    Standard response format from LLM providers with integrated cost calculation.
    """
    defstruct [
      :content,
      :model,
      :usage,
      :finish_reason,
      :id,
      :cost,
      :function_call,
      :tool_calls,
      :refusal,
      :logprobs,
      :metadata
    ]

    @type t :: %__MODULE__{
            content: String.t() | nil,
            model: String.t() | nil,
            usage: ExLLM.Types.token_usage() | nil,
            finish_reason: String.t() | nil,
            id: String.t() | nil,
            cost: float() | nil,
            function_call: map() | nil,
            tool_calls: list(map()) | nil,
            refusal: String.t() | nil,
            logprobs: map() | nil,
            metadata: map() | nil
          }
  end

  defmodule StreamChunk do
    @moduledoc """
    Represents a chunk from a streaming LLM response.
    """
    defstruct [:content, :finish_reason, :model, :id, :metadata]

    @type t :: %__MODULE__{
            content: String.t() | nil,
            finish_reason: String.t() | nil,
            model: String.t() | nil,
            id: String.t() | nil,
            metadata: map() | nil
          }
  end

  defmodule Model do
    @moduledoc """
    Represents an available LLM model.
    """
    defstruct [
      :id,
      :name,
      :description,
      :context_window,
      :pricing,
      :capabilities,
      :max_output_tokens
    ]

    @type t :: %__MODULE__{
            id: String.t(),
            name: String.t() | nil,
            description: String.t() | nil,
            context_window: non_neg_integer() | nil,
            pricing: ExLLM.Types.pricing() | nil,
            capabilities: map() | nil,
            max_output_tokens: non_neg_integer() | nil
          }
  end

  defmodule EmbeddingResponse do
    @moduledoc """
    Represents an embedding response from an LLM provider.
    """
    defstruct [:embeddings, :model, :usage, :cost, :metadata]

    @type t :: %__MODULE__{
            embeddings: list(list(float())),
            model: String.t(),
            usage: ExLLM.Types.token_usage() | nil,
            cost: float() | nil,
            metadata: map() | nil
          }
  end

  defmodule EmbeddingModel do
    @moduledoc """
    Represents an available embedding model.
    """
    defstruct [:name, :dimensions, :max_inputs, :provider, :description, :pricing]

    @type t :: %__MODULE__{
            name: String.t(),
            dimensions: non_neg_integer(),
            max_inputs: non_neg_integer() | nil,
            provider: atom(),
            description: String.t() | nil,
            pricing: ExLLM.Types.pricing() | nil
          }
  end

  defmodule Session do
    @moduledoc """
    Represents a conversation session with message history and metadata.
    """

    @enforce_keys [:id, :created_at, :updated_at]
    defstruct [
      :id,
      :llm_backend,
      :messages,
      :context,
      :created_at,
      :updated_at,
      :token_usage,
      :name
    ]

    @type t :: %__MODULE__{
            id: String.t(),
            llm_backend: String.t() | nil,
            messages: [ExLLM.Types.message()],
            context: %{optional(atom()) => any()},
            created_at: DateTime.t(),
            updated_at: DateTime.t(),
            token_usage: ExLLM.Types.token_usage() | nil,
            name: String.t() | nil
          }
  end
end
