// Unit tests for shared audio transport metadata.
//
// Scope: exercise `common/src/audio_transport.rs` without GStreamer or network
// dependencies.
// Targets: `common/src/audio_transport.rs`.
// Why: Opus should be evaluated as a transport codec without weakening the
// known-good raw PCM microphone path.

use lesavka_common::audio_transport::{
    AudioTransportProfile, UpstreamAudioCodec, bundle_audio_profile, mark_bundle_audio_profile,
    mark_packet_opus, mark_packet_pcm_s16le, normalize_audio_encoding, packet_audio_profile,
    packet_is_raw_pcm_s16le, parse_upstream_audio_codec,
};
use lesavka_common::lesavka::{AudioEncoding, AudioPacket, UpstreamMediaBundle};

#[test]
fn opus_profile_is_low_bandwidth_without_changing_capture_clock() {
    let pcm = AudioTransportProfile::pcm_s16le();
    let opus = AudioTransportProfile::opus_voice();

    assert_eq!(pcm.sample_rate, 48_000);
    assert_eq!(opus.sample_rate, 48_000);
    assert_eq!(pcm.channels, 2);
    assert_eq!(opus.channels, 2);
    assert_eq!(pcm.frame_duration_us, 20_000);
    assert_eq!(opus.frame_duration_us, 20_000);
    assert_eq!(pcm.expected_payload_bytes(), 3_840);
    assert_eq!(opus.expected_payload_bytes(), 240);
}

#[test]
fn unstamped_legacy_audio_remains_pcm_for_backward_compatibility() {
    let packet = AudioPacket::default();

    assert_eq!(
        normalize_audio_encoding(packet.encoding),
        AudioEncoding::PcmS16le
    );
    assert_eq!(
        packet_audio_profile(&packet),
        AudioTransportProfile::pcm_s16le()
    );
    assert!(packet_is_raw_pcm_s16le(&packet));
}

#[test]
fn packet_and_bundle_metadata_can_select_opus_without_payload_guessing() {
    let packet = AudioPacket {
        encoding: AudioEncoding::Opus as i32,
        sample_rate: 48_000,
        channels: 2,
        frame_duration_us: 20_000,
        data: vec![0xaa; 240],
        ..AudioPacket::default()
    };
    let bundle = UpstreamMediaBundle {
        audio_encoding: AudioEncoding::Opus as i32,
        audio_sample_rate: 48_000,
        audio_channels: 2,
        audio: vec![packet.clone()],
        ..UpstreamMediaBundle::default()
    };

    assert_eq!(
        packet_audio_profile(&packet),
        AudioTransportProfile::opus_voice()
    );
    assert_eq!(
        bundle_audio_profile(&bundle),
        AudioTransportProfile::opus_voice()
    );
    assert!(!packet_is_raw_pcm_s16le(&packet));
}

#[test]
fn marking_helpers_keep_current_pcm_path_explicit() {
    let mut packet = AudioPacket {
        frame_duration_us: 10_000,
        ..AudioPacket::default()
    };
    mark_packet_pcm_s16le(&mut packet);
    assert_eq!(packet.encoding, AudioEncoding::PcmS16le as i32);
    assert_eq!(packet.sample_rate, 48_000);
    assert_eq!(packet.channels, 2);
    assert_eq!(
        packet.frame_duration_us, 10_000,
        "caller-supplied packet duration should be preserved"
    );

    let mut bundle = UpstreamMediaBundle::default();
    mark_bundle_audio_profile(&mut bundle, AudioTransportProfile::pcm_s16le());
    assert_eq!(bundle.audio_encoding, AudioEncoding::PcmS16le as i32);
    assert_eq!(bundle.audio_sample_rate, 48_000);
    assert_eq!(bundle.audio_channels, 2);
}

#[test]
fn upstream_audio_codec_parser_keeps_opus_and_pcm_names_stable() {
    assert_eq!(UpstreamAudioCodec::PcmS16le.as_id(), "pcm");
    assert_eq!(UpstreamAudioCodec::PcmS16le.label(), "PCM");
    assert_eq!(
        UpstreamAudioCodec::PcmS16le.profile(),
        AudioTransportProfile::pcm_s16le()
    );
    assert_eq!(UpstreamAudioCodec::Opus.as_id(), "opus");
    assert_eq!(UpstreamAudioCodec::Opus.label(), "Opus");
    assert_eq!(
        UpstreamAudioCodec::Opus.profile(),
        AudioTransportProfile::opus_voice()
    );
    assert_eq!(
        parse_upstream_audio_codec("opus"),
        Some(UpstreamAudioCodec::Opus)
    );
    assert_eq!(
        parse_upstream_audio_codec(" compressed "),
        Some(UpstreamAudioCodec::Opus)
    );
    assert_eq!(
        parse_upstream_audio_codec("voice"),
        Some(UpstreamAudioCodec::Opus)
    );
    assert_eq!(
        parse_upstream_audio_codec("raw"),
        Some(UpstreamAudioCodec::PcmS16le)
    );
    assert_eq!(
        parse_upstream_audio_codec("PCM_S16LE"),
        Some(UpstreamAudioCodec::PcmS16le)
    );
    assert_eq!(
        parse_upstream_audio_codec("uncompressed"),
        Some(UpstreamAudioCodec::PcmS16le)
    );
    assert_eq!(parse_upstream_audio_codec("aac"), None);

    let mut packet = AudioPacket {
        data: vec![0xaa; 240],
        ..AudioPacket::default()
    };
    mark_packet_opus(&mut packet);
    assert_eq!(packet.encoding, AudioEncoding::Opus as i32);
    assert_eq!(
        packet_audio_profile(&packet),
        AudioTransportProfile::opus_voice()
    );
}

#[test]
fn packet_profiles_preserve_explicit_nonzero_pcm_metadata() {
    let packet = AudioPacket {
        encoding: AudioEncoding::PcmS16le as i32,
        sample_rate: 44_100,
        channels: 1,
        frame_duration_us: 10_000,
        ..AudioPacket::default()
    };

    assert_eq!(
        packet_audio_profile(&packet),
        AudioTransportProfile {
            encoding: AudioEncoding::PcmS16le,
            sample_rate: 44_100,
            channels: 1,
            frame_duration_us: 10_000,
            target_bitrate_bps: None,
        }
    );
}

#[test]
fn bundle_profiles_fallback_from_envelope_to_first_packet() {
    let packet = AudioPacket {
        encoding: AudioEncoding::Opus as i32,
        frame_duration_us: 40_000,
        ..AudioPacket::default()
    };
    let bundle = UpstreamMediaBundle {
        audio: vec![packet],
        ..UpstreamMediaBundle::default()
    };

    assert_eq!(
        bundle_audio_profile(&bundle),
        AudioTransportProfile {
            encoding: AudioEncoding::Opus,
            sample_rate: 48_000,
            channels: 2,
            frame_duration_us: 40_000,
            target_bitrate_bps: Some(96_000),
        }
    );
    assert_eq!(
        bundle_audio_profile(&UpstreamMediaBundle::default()),
        AudioTransportProfile::pcm_s16le()
    );
}

#[test]
fn marking_helpers_fill_default_frame_duration_when_absent() {
    let mut pcm_packet = AudioPacket::default();
    mark_packet_pcm_s16le(&mut pcm_packet);
    assert_eq!(pcm_packet.frame_duration_us, 20_000);

    let mut opus_packet = AudioPacket::default();
    mark_packet_opus(&mut opus_packet);
    assert_eq!(opus_packet.frame_duration_us, 20_000);
}
