//! End-to-end release smoke tests.
//!
//! These string the individual per-endpoint tests together into the two
//! user-visible journeys we want to always work after a release:
//!
//!   1. A brand-new wallet can sign in, hit the faucet, and see that its
//!      session behaves like an authenticated user (profile, balances).
//!   2. A funded wallet can place → inspect → cancel an order using nothing
//!      more than the types published in the SDK.
//!
//! The goal is to catch the kinds of regressions that only show up when
//! the whole flow runs against a real backend — auth envelope changes,
//! renamed fields, moved routes — not unit-level correctness.

mod common;

use common::{authed_client, authenticate_and_faucet, authenticate_funded, first_pair_id};
use uuid::Uuid;

/// Fresh wallet journey: challenge → verify → profile → faucet → fees sim.
#[tokio::test]
#[ignore = "requires running API server"]
async fn fresh_wallet_onboarding_journey() {
    let (token, address, _signer, mint) = authenticate_and_faucet().await;
    let authed = authed_client(&token);

    // 1. The freshly-minted session identifies the wallet correctly.
    let profile = authed.get_user_profile().await.unwrap().into_inner();
    assert_eq!(
        profile.address.unwrap().to_lowercase(),
        address.to_lowercase(),
        "profile address should match the authenticated wallet"
    );

    // 2. The faucet response must carry at least one mint with a tx hash.
    let minted = mint.minted.expect("faucet should mint at least one asset");
    assert!(!minted.is_empty(), "faucet returned an empty mint list");

    // 3. Fee simulation works for this session against the default pair.
    let pair_id = first_pair_id().await;
    let pair_uuid: Uuid = pair_id.parse().unwrap();
    let fees = authed
        .simulate_fees(
            None,
            &"100.0".parse().unwrap(),
            &"1.0".parse().unwrap(),
            monaco_sdk::types::SimulateFeesSide::Buy,
            None,
            &pair_uuid,
        )
        .await
        .unwrap()
        .into_inner();
    assert!(
        fees.notional.is_some(),
        "simulate_fees should return a notional, got {fees:?}"
    );
}

/// Funded wallet journey: auth → create → fetch → cancel.
#[tokio::test]
#[ignore = "requires running API server and funded PRIVATE_KEY"]
async fn funded_wallet_order_journey() {
    let (token, _address, _signer) = authenticate_funded().await;
    let authed = authed_client(&token);
    let pair_id = first_pair_id().await;
    let pair_uuid: Uuid = pair_id.parse().unwrap();

    let created = authed
        .create_order(&monaco_sdk::types::CreateOrderRequest {
            trading_pair_id: pair_uuid,
            order_type: "LIMIT".into(),
            side: "BUY".into(),
            price: Some("0.01".into()),
            quantity: "1".into(),
            trading_mode: Some("SPOT".into()),
            time_in_force: Some("GTC".into()),
            expiration_date: None,
            slippage_tolerance_bps: None,
            use_master_balance: None,
        })
        .await
        .unwrap()
        .into_inner();
    let order_id = created.order_id.expect("created order should have an id");

    // Read-side propagation is eventually consistent — the matching engine
    // accepts the order before the read cache sees it. Retry briefly before
    // giving up.
    let fetched = {
        let mut last_err = None;
        let mut fetched = None;
        for attempt in 0..5 {
            match authed.get_order_by_id(&order_id.to_string()).await {
                Ok(ok) => {
                    fetched = Some(ok.into_inner());
                    break;
                }
                Err(e) => {
                    last_err = Some(e);
                    tokio::time::sleep(std::time::Duration::from_millis(200 * (attempt + 1))).await;
                }
            }
        }
        fetched
            .unwrap_or_else(|| panic!("get_order_by_id({order_id}) never succeeded: {last_err:?}"))
    };
    assert_eq!(fetched.id.unwrap(), order_id);

    let cancelled = authed
        .cancel_order(&monaco_sdk::types::CancelOrderRequest { order_id })
        .await
        .unwrap()
        .into_inner();
    assert_eq!(
        cancelled.status.as_deref(),
        Some("SUCCESS"),
        "cancel should report SUCCESS, got {cancelled:?}"
    );
}
