//! Acceptance tests for ADR-122 §2.2 availability topic + LWT integration.

#![cfg(feature = "std")]

use wifi_densepose_bfld::{
    availability_topic, offline_message, online_message, publish_availability_offline,
    publish_availability_online, render_discovery_payloads, CapturePublisher, PrivacyClass,
    PAYLOAD_AVAILABLE, PAYLOAD_NOT_AVAILABLE,
};

#[test]
fn availability_topic_format_matches_documented_path() {
    assert_eq!(
        availability_topic("seed-01"),
        "ruview/seed-01/bfld/availability",
    );
}

#[test]
fn online_message_is_retained_friendly_payload() {
    let msg = online_message("seed-99");
    assert_eq!(msg.topic, "ruview/seed-99/bfld/availability");
    assert_eq!(msg.payload, "online");
    assert_eq!(msg.payload, PAYLOAD_AVAILABLE);
}

#[test]
fn offline_message_is_retained_friendly_payload() {
    let msg = offline_message("seed-99");
    assert_eq!(msg.payload, "offline");
    assert_eq!(msg.payload, PAYLOAD_NOT_AVAILABLE);
}

#[test]
fn publish_online_lands_one_message() {
    let mut p = CapturePublisher::default();
    publish_availability_online(&mut p, "seed-01").unwrap();
    assert_eq!(p.published.len(), 1);
    assert_eq!(p.published[0].payload, "online");
}

#[test]
fn publish_offline_lands_one_message() {
    let mut p = CapturePublisher::default();
    publish_availability_offline(&mut p, "seed-01").unwrap();
    assert_eq!(p.published.len(), 1);
    assert_eq!(p.published[0].payload, "offline");
}

// --- discovery payload integration --------------------------------------

#[test]
fn discovery_payload_includes_availability_topic_field() {
    let msgs = render_discovery_payloads("seed-01", PrivacyClass::Anonymous);
    for msg in &msgs {
        assert!(
            msg.payload
                .contains("\"availability_topic\":\"ruview/seed-01/bfld/availability\""),
            "discovery payload must reference availability_topic, got: {}",
            msg.payload,
        );
    }
}

#[test]
fn discovery_payload_includes_payload_available_and_not_available_strings() {
    let msgs = render_discovery_payloads("seed-01", PrivacyClass::Anonymous);
    for msg in &msgs {
        assert!(
            msg.payload.contains("\"payload_available\":\"online\""),
            "discovery payload missing payload_available, got: {}",
            msg.payload,
        );
        assert!(
            msg.payload.contains("\"payload_not_available\":\"offline\""),
            "discovery payload missing payload_not_available, got: {}",
            msg.payload,
        );
    }
}

#[test]
fn restricted_class_discovery_still_carries_availability_fields() {
    // Availability isn't an identity field — class 3 retains it.
    let msgs = render_discovery_payloads("seed-01", PrivacyClass::Restricted);
    assert_eq!(msgs.len(), 5);
    for msg in &msgs {
        assert!(msg.payload.contains("\"availability_topic\":"));
    }
}

// --- bootstrap composition ----------------------------------------------

#[test]
fn bootstrap_sequence_online_then_discovery_lands_in_order() {
    let mut p = CapturePublisher::default();
    publish_availability_online(&mut p, "seed-01").expect("online");
    let count =
        wifi_densepose_bfld::publish_discovery(&mut p, "seed-01", PrivacyClass::Anonymous)
            .expect("discovery");
    assert_eq!(count, 6);
    assert_eq!(p.published.len(), 1 + 6);
    assert_eq!(p.published[0].payload, "online");
    for msg in p.published.iter().skip(1) {
        assert!(msg.topic.starts_with("homeassistant/"));
    }
}

#[test]
fn graceful_shutdown_sequence_publishes_offline_message_last() {
    let mut p = CapturePublisher::default();
    publish_availability_online(&mut p, "seed-01").unwrap();
    publish_availability_offline(&mut p, "seed-01").unwrap();
    assert_eq!(p.published.len(), 2);
    assert_eq!(p.published[0].payload, "online");
    assert_eq!(p.published[1].payload, "offline");
}
