defmodule AgentForge do
  @moduledoc ~S"""
  AgentForge is a lightweight, signal-driven workflow framework designed for personal projects.

  ## Key Features

  - Signal-based communication
  - Composable handler pipelines
  - Persistent state management
  - Debug tracing support

  ## Example

      # Define a simple handler
      message_handler = fn signal, state ->
        new_state = Map.put(state, :last_message, signal.data)
        {AgentForge.Signal.emit(:processed, "Got: #{signal.data}"), new_state}
      end

      # Create a flow with the handler
      flow = AgentForge.new_flow([message_handler], debug: true)

      # Execute the flow with a signal
      {:ok, result, _state} = flow.(AgentForge.Signal.new(:message, "Hello"))
      # result.data will be "Got: Hello"
  """

  alias AgentForge.{Runtime, Signal}

  @doc """
  Creates a new flow with the given handlers and options.
  Returns a function that can be used to execute the flow.

  ## Options

  All options from `AgentForge.Runtime.execute/3` are supported.

  ## Examples

      iex> handler = fn signal, state ->
      ...>   {AgentForge.Signal.emit(:echo, signal.data), state}
      ...> end
      iex> flow = AgentForge.new_flow([handler])
      iex> {:ok, result, _} = flow.(AgentForge.Signal.new(:test, "hello"))
      iex> result.data
      "hello"
  """
  def new_flow(handlers, opts \\ []) do
    Runtime.configure(handlers, opts)
  end

  @doc """
  Creates a new stateful flow that maintains state between executions.
  Similar to new_flow/2 but automatically stores and retrieves state.

  ## Examples

      iex> {:ok, _pid} = AgentForge.Store.start_link(name: :test_store)
      iex> counter = fn _signal, state ->
      ...>   count = Map.get(state, :count, 0) + 1
      ...>   {AgentForge.Signal.emit(:count, count), Map.put(state, :count, count)}
      ...> end
      iex> flow = AgentForge.new_stateful_flow([counter], store_name: :test_store)
      iex> {:ok, result1, _} = flow.(AgentForge.Signal.new(:inc, nil))
      iex> {:ok, result2, _} = flow.(AgentForge.Signal.new(:inc, nil))
      iex> result2.data > result1.data
      true
  """
  def new_stateful_flow(handlers, opts \\ []) do
    Runtime.configure_stateful(handlers, opts)
  end

  @doc """
  Processes a flow with execution limits.
  This can prevent long-running operations.

  ## Options

  * `:timeout_ms` - Maximum execution time in milliseconds (default: 30000)
  * `:collect_stats` - Whether to collect execution statistics (default: true)
  * `:return_stats` - Whether to return statistics in the result (default: false)

  ## Examples

      iex> handlers = [
      ...>   fn _signal, state -> {{:emit, AgentForge.Signal.new(:done, "Success")}, state} end
      ...> ]
      iex> {:ok, result, _} = AgentForge.process_with_limits(handlers, AgentForge.Signal.new(:test, "data"), %{})
      iex> result.data
      "Success"
  """
  @spec process_with_limits(
          # handler functions
          list(function()),
          # input signal
          Signal.t(),
          # initial state
          map(),
          # options
          keyword()
        ) ::
          {:ok, Signal.t() | term(), term()}
          | {:ok, Signal.t() | term(), term(), AgentForge.ExecutionStats.t()}
          | {:error, term(), term()}
          | {:error, term(), term(), AgentForge.ExecutionStats.t()}
  def process_with_limits(handlers, signal, initial_state, opts \\ []) do
    # Use Runtime.execute_with_limits instead of directly calling Flow.process_with_limits
    # This ensures proper state persistence between executions
    Runtime.execute_with_limits(
      handlers,
      signal,
      opts |> Keyword.put(:initial_state, initial_state)
    )
  end

  @doc """
  Gets statistics from the last flow execution.
  Returns nil if no flow has been executed yet or statistics collection was disabled.

  ## Examples

      iex> handlers = [fn signal, state -> {{:emit, signal}, state} end]
      iex> signal = AgentForge.Signal.new(:test, "data")
      iex> {:ok, _, _} = AgentForge.process_with_limits(handlers, signal, %{})
      iex> stats = AgentForge.get_last_execution_stats()
      iex> stats.steps
      1
  """
  def get_last_execution_stats do
    Runtime.get_last_execution_stats()
  end

  # Re-export commonly used functions from Signal module
  defdelegate new_signal(type, data, meta \\ %{}), to: Signal, as: :new
  defdelegate emit(type, data, meta \\ %{}), to: Signal
  defdelegate emit_many(signals), to: Signal
  defdelegate halt(value), to: Signal
end
