//! Tests for `PgSagaRunner::describe()` — the one-call instance diagnostic.
//!
//! Gated on `DATABASE_URL`; skips silently when unset.

#![allow(dead_code)]

use mire_sagas::{PgSagaRunner, Saga, SagaBuilder, SagaInstanceStatus, SagaStepStatus};

mod common;

#[allow(unused)]
#[path = "../examples/travel_booking.rs"]
mod travel_booking;

use travel_booking::{
    Booking, BookingEvent, BookingId, CustomerId, FlightId, HotelId, Money, TravelBookingSaga,
};

fn req(id: &str) -> BookingEvent {
    BookingEvent::Requested {
        booking_id: BookingId::new(id),
        customer_id: CustomerId::new("alice"),
        flight_id: FlightId::new("F1"),
        hotel_id: HotelId::new("H1"),
        amount: Money::from_minor(100_000),
    }
}

#[tokio::test]
async fn describe_running_saga_shows_active_step_and_awaits() {
    // Strip the carrier-ticket handler so the saga stalls on the confirm
    // step's emit. Then describe() should show "confirm Active" awaiting
    // the carrier-ticket-issued event.
    let Some(store) = common::store().await else {
        return;
    };
    let mut topology = TravelBookingSaga::build(SagaBuilder::<TravelBookingSaga>::for_saga());
    topology.remove_request_handler("saga.carrier.ticket-requested");
    let runner = PgSagaRunner::new(topology, store);
    let id = runner
        .trigger::<Booking>(&req(&common::unique("desc-1")))
        .await
        .unwrap()
        .unwrap();

    let diag = runner
        .describe(&id)
        .await
        .unwrap()
        .expect("instance should exist");

    assert_eq!(diag.saga, "travel-booking-saga");
    assert_eq!(diag.instance_id, id);
    assert_eq!(diag.status, SagaInstanceStatus::Running);

    // The confirm step is Active, awaiting some event.
    let confirm_status = diag
        .steps
        .iter()
        .find(|(n, _)| *n == "confirm")
        .map(|(_, s)| s.clone())
        .unwrap();
    assert_eq!(confirm_status, SagaStepStatus::Active);

    assert!(
        diag.awaiting.iter().any(|a| a.step == "confirm"),
        "describe should report confirm as awaiting; got {:?}",
        diag.awaiting
    );

    // Display renders without panic.
    let rendered = format!("{diag}");
    assert!(rendered.contains("travel-booking-saga"));
    assert!(rendered.contains("Running"));
    assert!(rendered.contains("confirm"));
}

#[tokio::test]
async fn describe_unknown_instance_returns_none() {
    let Some(store) = common::store().await else {
        return;
    };
    let topology = TravelBookingSaga::build(SagaBuilder::<TravelBookingSaga>::for_saga());
    let runner = PgSagaRunner::new(topology, store);
    assert!(
        runner
            .describe(&common::unique("nope"))
            .await
            .unwrap()
            .is_none()
    );
}

#[tokio::test]
async fn describe_completed_saga_shows_no_awaits_and_full_emit_count() {
    // Full happy path: every handler registered → saga runs to terminal
    // in one trigger() call.
    let Some(store) = common::store().await else {
        return;
    };
    let topology = TravelBookingSaga::build(SagaBuilder::<TravelBookingSaga>::for_saga());
    let runner = PgSagaRunner::new(topology, store);
    let id = runner
        .trigger::<Booking>(&req(&common::unique("desc-2")))
        .await
        .unwrap()
        .unwrap();

    let diag = runner.describe(&id).await.unwrap().unwrap();
    assert_eq!(diag.status, SagaInstanceStatus::SucceededTerminal);
    assert!(
        diag.awaiting.is_empty(),
        "terminal saga shouldn't be awaiting anything"
    );
    assert!(diag.emit_count > 0, "should have recorded emits");
    assert!(diag.originating_error.is_none());
    assert!(diag.last_error.is_none());
}
