//! Tests for the inspection accessors on `PgSagaRunner`:
//!  - `status`, `is_terminal`, `is_succeeded`, `is_failed`
//!  - the `load`, `step_status`, `emitted` paths
//!
//! The point is users + tests can introspect without unwrapping
//! `runner.load(&id).await.unwrap().unwrap().status`.
//!
//! Gated on `DATABASE_URL`; skips silently when unset.

#![allow(dead_code)]

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

mod common;

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

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

fn make_request(id: &str) -> BookingEvent {
    BookingEvent::Requested {
        booking_id: BookingId::new(id),
        customer_id: CustomerId::new("alice"),
        flight_id: FlightId::new("LH-100"),
        hotel_id: HotelId::new("HILTON-OSL"),
        amount: Money::from_minor(150_000),
    }
}

fn build_runner(store: &mire::EventStore) -> PgSagaRunner<travel_booking::TravelBookingSaga> {
    let topology = travel_booking::TravelBookingSaga::build(SagaBuilder::<
        travel_booking::TravelBookingSaga,
    >::for_saga());
    PgSagaRunner::new(topology, store.clone())
}

#[tokio::test]
async fn status_for_unknown_instance_is_none() {
    let Some(store) = common::store().await else {
        return;
    };
    let runner = build_runner(&store);
    let nope = common::unique("nope");
    assert_eq!(runner.status(&nope).await.unwrap(), None);
    assert!(!runner.is_terminal(&nope).await.unwrap());
    assert!(!runner.is_succeeded(&nope).await.unwrap());
    assert!(!runner.is_failed(&nope).await.unwrap());
}

#[tokio::test]
async fn status_accessors_reflect_succeeded_terminal() {
    let Some(store) = common::store().await else {
        return;
    };
    let runner = build_runner(&store);
    let id = runner
        .trigger::<Booking>(&make_request(&common::unique("happy-inspect")))
        .await
        .unwrap()
        .unwrap();

    assert_eq!(
        runner.status(&id).await.unwrap(),
        Some(SagaInstanceStatus::SucceededTerminal)
    );
    assert!(runner.is_terminal(&id).await.unwrap());
    assert!(runner.is_succeeded(&id).await.unwrap());
    assert!(!runner.is_failed(&id).await.unwrap());
}

#[tokio::test]
async fn status_accessors_reflect_failed_terminal() {
    let Some(store) = common::store().await else {
        return;
    };
    let runner = build_runner(&store);
    // booking_id starts with "fraud-" makes the payment handler return Err.
    let req = BookingEvent::Requested {
        booking_id: BookingId::new(common::unique("fraud-inspect")),
        customer_id: CustomerId::new("alice"),
        flight_id: FlightId::new("LH-100"),
        hotel_id: HotelId::new("HILTON-OSL"),
        amount: Money::from_minor(150_000),
    };
    let id = runner.trigger::<Booking>(&req).await.unwrap().unwrap();

    assert_eq!(
        runner.status(&id).await.unwrap(),
        Some(SagaInstanceStatus::FailedTerminal)
    );
    assert!(runner.is_terminal(&id).await.unwrap());
    assert!(!runner.is_succeeded(&id).await.unwrap());
    assert!(runner.is_failed(&id).await.unwrap());
}

#[tokio::test]
async fn status_running_instance_is_not_terminal() {
    let Some(store) = common::store().await else {
        return;
    };
    // Strip handlers so payment stalls — saga stays Running. Far-future
    // payment deadline so a sibling test's tick can't expire it.
    let mut topology = travel_booking::TravelBookingSaga::build(SagaBuilder::<
        travel_booking::TravelBookingSaga,
    >::for_saga());
    topology.remove_request_handler("saga.payment.capture-requested");
    let topology = topology.dep(travel_booking::PaymentDeadline(
        std::time::Duration::from_secs(7 * 24 * 3600),
    ));
    let runner = PgSagaRunner::new(topology, store);

    let id = runner
        .trigger::<Booking>(&make_request(&common::unique("stalled")))
        .await
        .unwrap()
        .unwrap();

    assert_eq!(
        runner.status(&id).await.unwrap(),
        Some(SagaInstanceStatus::Running)
    );
    assert!(!runner.is_terminal(&id).await.unwrap());
    assert!(!runner.is_succeeded(&id).await.unwrap());
    assert!(!runner.is_failed(&id).await.unwrap());
}
