# `Journey.Tools`

This module contains utility functions for the Journey library.

# `abandon_computation`

Manually abandons a computation that is currently in the `:computing` state.

This is useful when you want to immediately abandon a stuck or problematic computation
without waiting for its deadline to expire. The behavior matches the automatic
abandonment sweeper: it will schedule a retry if the node hasn't exhausted its
`max_retries`, then mark the computation as `:abandoned`.

## Parameters
- `computation_id` - The ID of the computation to abandon (starts with "CMP")

## Returns
- `{:ok, computation}` - The updated computation struct
- `{:error, :not_found}` - Computation with the given ID doesn't exist
- `{:error, :invalid_state, state}` - Computation is not in `:computing` state

## Example

    # Abandon a stuck computation
    {:ok, computation} = Journey.Tools.abandon_computation("CMP123ABC")

    # The computation is now marked as :abandoned
    computation.state
    #=> :abandoned

## How It Works
1. Looks up the computation by ID
2. Validates the computation is in `:computing` state
3. Schedules a retry if `max_retries` hasn't been exhausted
4. Marks the computation as `:abandoned` with current timestamp
5. Kicks the execution to trigger downstream processing

# `computation_state`

Returns the current state of a computation node.

Returns the state of the most recent computation attempt for the given node.
If no computation has been attempted yet, returns `:not_set`.
For input nodes (non-compute nodes), returns `:not_compute_node`.

## Parameters
- `execution_id` - The ID of the execution to check
- `node_name` - The atom name of the node to check

## Returns
- `:not_set` - No computation has been attempted yet
- `:computing` - Currently computing
- `:success` - Computation completed successfully
- `:failed` - Computation failed
- `:abandoned` - Computation was abandoned
- `:cancelled` - Computation was cancelled
- `:not_compute_node` - The node is an input node, not a computation

## Examples

    iex> import Journey.Node
    iex> graph = Journey.new_graph("computation_state doctest graph", "v1.0.0", [
    ...>   input(:value),
    ...>   compute(:double, [:value], fn %{value: v} -> {:ok, v * 2} end)
    ...> ])
    iex> execution = Journey.start_execution(graph)
    iex> Journey.Tools.computation_state(execution.id, :double)
    :not_set
    iex> Journey.Tools.computation_state(execution.id, :value)
    :not_compute_node
    iex> execution = Journey.set(execution, :value, 5)
    iex> {:ok, %{value: _result}} = Journey.get_value(execution, :double, wait: :newer)
    iex> Journey.Tools.computation_state(execution.id, :double)
    :success

# `computation_state_to_text`

Converts a computation state atom to human-readable text with a visual symbol.

Returns a formatted string with an appropriate symbol and the state atom
for each computation state, following the pattern used in other Journey
text formatting functions.

## Parameters
- `state` - The computation state atom returned by `computation_state/2`

## Returns
A string with symbol and the state atom.

## State Representations
- `:not_set` - "⬜ :not_set (not yet attempted)"
- `:computing` - "⏳ :computing"
- `:success` - "✅ :success"
- `:failed` - "❌ :failed"
- `:abandoned` - "❓ :abandoned"
- `:cancelled` - "🛑 :cancelled"
- `:not_compute_node` - "📝 :not_compute_node"

## Examples

    iex> Journey.Tools.computation_state_to_text(:success)
    "✅ :success"

    iex> Journey.Tools.computation_state_to_text(:computing)
    "⏳ :computing"

    iex> Journey.Tools.computation_state_to_text(:not_set)
    "⬜ :not_set (not yet attempted)"

# `computation_status_as_text`

Shows the status and dependencies for a single computation node.

Provides a focused view of one specific computation node's status and dependencies,
similar to the computation sections in Journey.Tools.introspect/1 but for just one node.

## Parameters
- `execution_id` - The ID of the execution to analyze
- `node_name` - The atom name of the computation node to check

## Returns
A string showing the node's current status and dependencies.

For completed computations, shows the result with inputs used:

    :send_follow_up (CMPTA5MDJHVXRMG54150EGX): ✅ :success | :compute | rev 4
    inputs used:
       :user_applied (rev 0)
       :card_mailed (rev 0)

For outstanding computations, shows the dependency tree:

    :send_weekly_reminder (CMPTA5MDJHVXRMG54150EGX): ⬜ :not_set (not yet attempted) | :compute
         :and
          ├─ 🛑 :subscribe_weekly | &true?/1
          ├─ 🛑 :weekly_reminder_schedule | &provided?/1
          └─ ✅ :email_address | &provided?/1 | rev 2

For input nodes (non-compute nodes), returns an appropriate message.

## Examples

    iex> import Journey.Node
    iex> graph = Journey.new_graph("computation_status_as_text doctest", "v1.0.0", [
    ...>   input(:value),
    ...>   compute(:double, [:value], fn %{value: v} -> {:ok, v * 2} end)
    ...> ])
    iex> execution = Journey.start_execution(graph)
    iex> Journey.Tools.computation_status_as_text(execution.id, :double)
    ":double: ⬜ :not_set (not yet attempted) | :compute\n       ✅ :value | &is_set/1"

    iex> import Journey.Node
    iex> graph = Journey.new_graph("computation_status_as_text completed doctest", "v1.0.0", [
    ...>   input(:value),
    ...>   compute(:triple, [:value], fn %{value: v} -> {:ok, v * 3} end)
    ...> ])
    iex> execution = Journey.start_execution(graph)
    iex> execution = Journey.set(execution, :value, 5)
    iex> {:ok, %{}} = Journey.get_value(execution, :triple, wait: :newer)
    iex> result = Journey.Tools.computation_status_as_text(execution.id, :triple)
    iex> result =~ ":triple"
    true
    iex> result =~ "✅ :success"
    true
    iex> result =~ "inputs used"
    true

# `generate_mermaid_graph`

Generates a Mermaid diagram representation of a Journey graph.

Converts a graph into Mermaid syntax for visualization. By default returns only
the flow diagram without legend or timestamp.

## Quick Example

```elixir
# Just the flow
mermaid = Journey.Tools.generate_mermaid_graph(graph)

# Include legend and timestamp
mermaid = Journey.Tools.generate_mermaid_graph(graph,
  include_legend: true,
  include_timestamp: true
)
```

## Options
* `:include_legend` - Include node type legend (default: `false`)
* `:include_timestamp` - Include generation timestamp (default: `false`)

# `introspect`

Introspects an execution's current state with a human-readable text summary.

This is the primary debugging and introspection tool for Journey executions,
providing a comprehensive snapshot of values, computations, and dependencies.

## Example

    iex> Journey.Tools.introspect("EXEC07B2H0H7J1LTAE0VJDAL") |> IO.puts()
    Execution summary:
    - ID: 'EXEC07B2H0H7J1LTAE0VJDAL'
    - Graph: 'g1' | 'v1'
    ...
    :ok

## Parameters
- `execution_id` - The ID of the execution to analyze

## Returns
A formatted string with the complete execution state summary.

Use `summarize_as_data/1` to get execution summary as structured data.

# `retry_computation`

Retries a failed computation.

This function enables retrying computations that have exhausted their max_retries
by making their previous attempts "stale" through upstream revision changes, then
creating a new computation for the scheduler to pick up.

## Parameters
- `execution_id` - The ID of the execution containing the failed computation
- `computation_node_name` - The atom name of the computation node to retry

## Returns
The updated execution struct

## Example
    iex> Journey.Tools.retry_computation("EXEC123", :email_horoscope)
    %Journey.Persistence.Schema.Execution{...}

## How It Works
1. Finds upstream dependencies that are currently satisfied
3. Creates a new :not_set computation for the scheduler to pick up
4. Previous failed attempts become "stale" in the retry counting logic
5. The scheduler can now execute the new computation attempt

# `summarize`

> This function is deprecated. Use introspect/1 instead.

Generates a human-readable text summary of an execution's current state.

**This function is deprecated.** Use `introspect/1` instead.

## Parameters
- `execution_id` - The ID of the execution to analyze

## Returns
A formatted string with the complete execution state summary.

# `summarize_as_data`

Generates structured data about an execution's current state.

Returns a map containing:
- Execution metadata (ID, graph, timestamps, duration, revision, archived status)
- Values categorized as set/not_set with their details
- Computations categorized as completed/outstanding with dependency info

## Example

    iex> Journey.Tools.introspect("EXEC07B2H0H7J1LTAE0VJDAL")
    %{
      execution_id: "EXEC07B2H0H7J1LTAE0VJDAL",
      graph_name: "g1",
      graph_version: "v1",
      archived_at: nil,
      created_at: 1723656196,
      updated_at: 1723656210,
      duration_seconds: 14,
      revision: 7,
      values: %{
        set: [...],
        not_set: [...]
      },
      computations: %{
        completed: [...],
        outstanding: [...]
      }
    }

## Parameters
- `execution_id` - The ID of the execution to analyze

## Returns
A structured map with execution state data.

Use `introspect/1` to get execution summary as text.

# `summarize_as_text`

> This function is deprecated. Use introspect/1 instead.

Generates a human-readable text summary of an execution's current state.

**This function is deprecated.** Use `introspect/1` instead.

## Parameters
- `execution_id` - The ID of the execution to analyze

## Returns
A formatted string with the complete execution state summary.

Use `summarize_as_data/1` to get execution summary as data.

# `what_am_i_waiting_for`

Shows the status of upstream dependencies for a computation node.

Lists each dependency with a checkmark (✅) if satisfied or a stop sign (🛑) if not.
Useful for debugging to see which dependencies are met and which are still blocking.

## Parameters
- `execution_id` - The ID of the execution to analyze
- `computation_node_name` - The atom name of the computation node to check

## Returns
A string showing the readiness status with checkmarks for met conditions and
stop signs for unmet conditions.

## Example

    iex> import Journey.Node
    iex> graph = Journey.new_graph("what_am_i_waiting_for test graph Elixir.Journey.Tools", "v1.0.0", [
    ...>   input(:name),
    ...>   input(:title),
    ...>   compute(:greeting, [:name, :title], fn %{name: name, title: title} ->
    ...>     {:ok, "Hello, #{title} #{name}!"}
    ...>   end)
    ...> ])
    iex> {:ok, execution} = Journey.start_execution(graph)
    iex> Journey.Tools.what_am_i_waiting_for(execution.id, :greeting) |> IO.puts()
    🛑 :name | &is_set/1
    🛑 :title | &is_set/1
    :ok
    iex> {:ok, execution} = Journey.set(execution, :name, "Alice")
    iex> Journey.Tools.what_am_i_waiting_for(execution.id, :greeting) |> IO.puts()
    ✅ :name | &is_set/1 | rev 1
    🛑 :title | &is_set/1
    :ok
    iex> {:ok, execution} = Journey.set(execution, :title, "Dr.")
    iex> {:ok, %{value: _greeting_value}} = Journey.get_value(execution, :greeting, wait: :newer)
    iex> Journey.Tools.what_am_i_waiting_for(execution.id, :greeting) |> IO.puts()
    ✅ :name | &is_set/1 | rev 1
    ✅ :title | &is_set/1 | rev 2
    :ok

---

*Consult [api-reference.md](api-reference.md) for complete listing*
