//! Tests for `PgSagaRunner::mermaid_instance` — instance-state DAG
//! coloured by step status.
//!
//! Gated on `DATABASE_URL`; skips silently when unset.

#![allow(dead_code)]

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

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 mermaid_instance_unknown_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
            .mermaid_instance(&common::unique("nope"))
            .await
            .unwrap()
            .is_none()
    );
}

#[tokio::test]
async fn mermaid_instance_renders_class_defs_and_nodes() {
    let Some(store) = common::store().await else {
        return;
    };
    // Stall mid-flight by stripping a handler.
    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("vis-1")))
        .await
        .unwrap()
        .unwrap();

    let out = runner
        .mermaid_instance(&id)
        .await
        .unwrap()
        .expect("instance exists");

    assert!(out.starts_with("flowchart TD\n"), "got:\n{out}");
    assert!(
        out.contains("classDef active"),
        "missing active style:\n{out}"
    );
    assert!(
        out.contains("classDef completed"),
        "missing completed style"
    );
    // The confirm step should be Active (awaiting carrier-ticket).
    assert!(
        out.contains("confirm (Active)") || out.contains("confirm (Active"),
        "got:\n{out}"
    );
    // Per-node class assignment exists.
    assert!(out.contains("class confirm active"), "got:\n{out}");
}

#[tokio::test]
async fn mermaid_instance_terminal_uses_round_shape() {
    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("vis-2")))
        .await
        .unwrap()
        .unwrap();
    let out = runner.mermaid_instance(&id).await.unwrap().unwrap();
    // Terminal step renders with the rounded ([..]) syntax.
    assert!(
        out.contains(r#"confirm(["confirm (Completed)"])"#),
        "got:\n{out}"
    );
    assert!(out.contains("class confirm completed"));
}
