//! `mire-sagas` — saga / process-manager layer on top of [`mire`].
//!
//! A saga is a sparse DAG of typed step handlers, declared on a builder
//! and driven by a runner. Each step is an `async fn` with axum-style
//! extractors that emits a typed request event ([`Emit`]); which response
//! event completes the step — and how it maps to the step's typed output
//! — is declared on the topology via `task(...).on_response(...)`.
//! In-process handlers can be registered on the *same* builder via
//! `.handle(handler)`; the runner auto-dispatches each emit through its
//! handler and delivers the typed response back to the awaiting step —
//! so a saga runs to terminal in a single `trigger()` call when every
//! handler is registered.
//!
//! ## The model
//!
//! A saga has:
//!
//! - **A trigger.** Some domain event from another aggregate starts a
//!   new instance. Registered via [`SagaBuilder::start_on`].
//! - **A DAG of steps.** Each step is a typed handler. Steps declare
//!   their dependencies (`after`, `after_any_of`), their await
//!   (`on_response`, `timeout`), their gating (`gated_by`), and their
//!   terminal status (`terminal_success`, `terminal_failure`). Bundled
//!   together via [`task`]. A handler returning [`Emit`] dispatches
//!   events; a handler returning a plain serialisable value completes
//!   immediately with it (compute/join steps).
//! - **Compensations.** Per-step undo handlers. The runner cascades
//!   them in reverse-completion order on failure.
//! - **Request handlers.** Typed `async fn(Req<R>, …)` registered via
//!   [`SagaBuilder::handle`]. The runner auto-invokes the right handler
//!   per emitted event_type and delivers its response back.
//! - **Signals.** External decisions/events (HTTP webhooks, admin
//!   actions). Registered via [`SagaBuilder::signal`] with policies
//!   `triggers_compensation` or `routes_to_step`.
//!
//! ## A minimal saga
//!
//! ```rust,no_run
//! use mire::{Aggregate, EventData};
//! use mire_sagas::{Emit, PgSagaRunner, Req, Saga, SagaBuilder, Start, State, task};
//! use serde::{Deserialize, Serialize};
//!
//! // 1. The trigger aggregate + event.
//! #[derive(Default, Clone)]
//! struct Booking;
//!
//! #[derive(Serialize, Deserialize, Clone, EventData)]
//! #[mire(event_type = "booking.requested")]
//! struct BookingRequested { id: String }
//!
//! impl Aggregate for Booking {
//!     type Event = BookingRequested;
//!     fn stream_category() -> &'static str { "booking" }
//!     fn apply(&mut self, _: &Self::Event) {}
//! }
//!
//! // 2. Per-saga state and per-step typed outputs.
//! #[derive(Default, Clone, Serialize, Deserialize)]
//! struct BookingState { id: String }
//!
//! #[derive(Debug, Clone, Serialize, Deserialize)]
//! struct FlightReserved { hold_id: String }
//!
//! // 3. A typed request the step emits + the response it awaits.
//! #[derive(Serialize, Deserialize, Clone, EventData)]
//! #[mire(event_type = "flight.hold-requested")]
//! struct RequestFlightHold { id: String }
//!
//! #[derive(Serialize, Deserialize, Clone, EventData)]
//! #[mire(event_type = "flight.held")]
//! struct FlightHeld { hold_id: String }
//!
//! // 4. The step handler — axum-style extractors; it just emits. The
//! //    await + response→output map live on the topology (step 6).
//! async fn reserve_flight(State(s): State<BookingState>) -> Emit {
//!     Emit::event(RequestFlightHold { id: s.id.clone() })
//! }
//!
//! // 5. An in-process handler the runner auto-invokes per emit.
//! async fn handle_flight_hold(Req(r): Req<RequestFlightHold>) -> FlightHeld {
//!     FlightHeld { hold_id: format!("fh-{}", r.id) }
//! }
//!
//! // 6. Wire the saga.
//! struct BookingSaga;
//! impl Saga for BookingSaga {
//!     type State = BookingState;
//!     fn name() -> &'static str { "booking-saga" }
//!     fn build(b: SagaBuilder<Self>) -> SagaBuilder<Self> {
//!         b.start_on::<Booking, _>(|e: &BookingRequested| Some(Start {
//!             instance_id: e.id.clone(),
//!             state: BookingState { id: e.id.clone() },
//!         }))
//!         .step("flight", task(reserve_flight)
//!             .on_response(|h: FlightHeld| FlightReserved { hold_id: h.hold_id })
//!             .terminal_success())
//!         .handle(handle_flight_hold)
//!     }
//! }
//!
//! // 7. Drive it — durable: every transition commits to Postgres, and it
//! //    runs to terminal in one call because the handler is in-process.
//! # async fn run(store: mire::EventStore) {
//! let topology = BookingSaga::build(SagaBuilder::<BookingSaga>::for_saga());
//! let runner = PgSagaRunner::new(topology, store);
//! let id = runner.trigger::<Booking>(&BookingRequested { id: "b1".into() })
//!     .await.unwrap().unwrap();
//! assert!(runner.is_succeeded(&id).await.unwrap());
//! # }
//! ```
//!
//! `task(handler).after(...)` keeps the step's handler and options
//! visually grouped inside the `.step(name, ...)` call — rustfmt won't
//! split the options onto top-level chain lines.
//!
//! ## What runs where
//!
//! The runner ([`PgSagaRunner`]) drives the lifecycle, reading and
//! writing through [`mire`]'s event store: every instance transition is
//! an OCC-checked commit to the saga's own event stream, so any replica
//! can act on any instance.
//!
//! Per-instance state lives in [`SagaEnvelope<S>`] — a `mire` aggregate
//! the framework provides. Users don't define an aggregate; they
//! provide the state struct (`type State`) and the framework carries
//! it through the envelope.
//!
//! ## Reliability features
//!
//! - **`Result<Ok, Err>` returns** for fallible handlers — the typed
//!   Err event is emitted into the log AND the awaiting step fails
//!   with [`StepError::Failed`], firing the compensation cascade.
//! - **No handler re-runs.** A suspended step resumes by applying the
//!   topology-registered `.on_response` map to the delivered payload —
//!   handlers are never re-invoked, so they carry no purity contract.
//! - **`.strict_handlers()`** opts into build-time-ish safety: any
//!   emit with no registered handler fails the step rather than
//!   stalling silently. Pair with `.allow_unhandled::<T>()` to mark
//!   intentional external emits.
//! - **`.default_step_timeout(d)`** caps any step's await with a
//!   saga-wide deadline. Task-level `.timeout(d)` overrides it;
//!   `Emit::timeout(d)` (computed at activation, e.g. from a `Dep`)
//!   overrides both.
//! - **Topology validator** catches missing `start_on`, duplicate
//!   step names, unknown `.after`/`.gated_by`/`.compensate`/signal
//!   references, missing terminal, forward/cyclic deps — surfaced
//!   via [`TopologyError`] at `.build()`-time.
//! - **Skip-cascade** on `gated_by` predicates and `after_any_of`
//!   merges with all-skipped predecessors.
//!
//! ## Worked example
//!
//! See `examples/travel_booking.rs` for a fully-runnable saga with
//! parallel reservation steps, a payment timeout, admin cancel via
//! signal, and a fallible payment handler.

#![allow(clippy::needless_lifetimes)]

pub mod builder;
pub(crate) mod decide;
pub mod envelope;
pub mod event_static;
pub mod extract;
pub(crate) mod guard;
pub mod handler;
pub mod inspect;
/// Per-instance processing leases (effects-vs-commit §11). Surfaced under
/// `#[doc(hidden)]` so concurrency tests can drive the primitive directly;
/// production code uses it via `PgSagaRunner`'s lease-guarded entry points.
#[doc(hidden)]
pub mod instance_lease;
pub mod migrate;
pub mod outcome;
pub mod pg_runner;
pub mod publisher;
pub mod result;
pub mod saga;
#[doc(hidden)]
pub mod shard_lease;
pub mod step;
pub mod worker;

pub use migrate::migrate;
pub use outcome::SignalOutcome;
pub use pg_runner::{PgRunnerError, PgSagaRunner};
pub use worker::SagaWorker;

pub use builder::{
    CompBuilder, NoResponse, SagaBuilder, SagaSignal, TaskBuilder, TopologyError, comp, task,
};
pub use envelope::{EmittedRequest, SagaEnvelope, SagaInstanceStatus, SagaStepStatus};
pub use event_static::EventTypeStatic;
pub use extract::{
    Cause, Dep, Extract, ExtractError, ExtractRequirement, Output, Req, State, Trigger,
};
pub use handler::{
    CompensationHandler, EmitStep, IntoResponse, RequestHandler, ResponseValue, StepHandler,
    StepKind, ValueStep,
};
pub use inspect::{AwaitDescription, EmittedEvent, InstanceDiagnostic};
pub use publisher::{EmitPublisher, OutboxMessage};
pub use result::{AwaitMapError, DispatchedAction, Emit, StepBuildError, StepError};
pub use saga::{Saga, Start};
pub use step::{IdempotencyKey, StepContext, StepName};
