// Integration smoke coverage for client runtime constructors.
//
// Scope: execute public client startup/media constructors through stable APIs
// without requiring hardware-specific assertions.
// Targets: `client/src/app.rs`, `client/src/input/camera.rs`,
// `client/src/input/microphone.rs`, and `client/src/output/audio.rs`.
// Why: these paths are operationally important but often depend on runtime
// host capabilities; smoke contracts keep them exercised in CI.

use lesavka_client::LesavkaClientApp;
use lesavka_client::input::camera::{CameraCapture, CameraCodec, CameraConfig};
use lesavka_client::input::inputs::InputAggregator;
use lesavka_client::input::microphone::MicrophoneCapture;
use lesavka_client::output::audio::AudioOut;
use lesavka_client::output::video::MonitorWindow;
use lesavka_common::lesavka::AudioPacket;
use lesavka_common::lesavka::{KeyboardReport, MouseReport, VideoPacket};
use serial_test::serial;
use temp_env::with_var;
use tokio::sync::{broadcast, mpsc};

#[test]
#[serial]
fn client_app_new_supports_headless_mode() {
    with_var("LESAVKA_HEADLESS", Some("1"), || {
        with_var(
            "LESAVKA_SERVER_ADDR",
            Some("http://127.0.0.1:50051"),
            || {
                let app = LesavkaClientApp::new();
                assert!(app.is_ok(), "expected headless app construction to succeed");
            },
        );
    });
}

#[test]
fn camera_capture_stub_pull_returns_none() {
    gstreamer::init().ok();
    let cam = CameraCapture::new_stub();
    assert!(
        cam.pull().is_none(),
        "stub capture should not produce real frames"
    );
}

#[test]
#[serial]
fn camera_capture_test_pattern_constructor_is_stable() {
    with_var("LESAVKA_CAM_TEST_PATTERN", Some("ball"), || {
        let result = CameraCapture::new(
            Some("test"),
            Some(CameraConfig {
                codec: CameraCodec::Mjpeg,
                width: 320,
                height: 240,
                fps: 5,
            }),
        );

        match result {
            Ok(_cam) => {}
            Err(err) => {
                assert!(
                    !err.to_string().trim().is_empty(),
                    "camera constructor returned an empty error"
                );
            }
        }
    });
}

#[test]
#[serial]
fn microphone_capture_constructor_is_stable_with_missing_source_hint() {
    with_var(
        "LESAVKA_MIC_SOURCE",
        Some("definitely-missing-source"),
        || {
            let result = MicrophoneCapture::new();
            match result {
                Ok(_mic) => {}
                Err(err) => {
                    assert!(
                        !err.to_string().trim().is_empty(),
                        "microphone constructor returned an empty error"
                    );
                }
            }
        },
    );
}

#[test]
#[serial]
fn audio_out_constructor_and_push_are_stable() {
    with_var("LESAVKA_AUDIO_SINK", Some("autoaudiosink"), || {
        with_var("LESAVKA_TAP_AUDIO", Some("0"), || match AudioOut::new() {
            Ok(out) => {
                out.push(AudioPacket {
                    id: 0,
                    pts: 0,
                    data: Vec::new(),
                    ..AudioPacket::default()
                });
            }
            Err(err) => {
                assert!(
                    !err.to_string().trim().is_empty(),
                    "audio output constructor returned an empty error"
                );
            }
        });
    });
}

#[test]
fn input_aggregator_constructor_is_stable() {
    let (kbd_tx, _) = broadcast::channel::<KeyboardReport>(8);
    let (mou_tx, _) = broadcast::channel::<MouseReport>(8);
    let (paste_tx, _paste_rx) = mpsc::unbounded_channel::<String>();
    let _agg = InputAggregator::new(false, kbd_tx, mou_tx, Some(paste_tx));
}

#[test]
#[serial]
fn monitor_window_constructor_and_push_are_stable() {
    match MonitorWindow::new(0) {
        Ok(win) => {
            win.push_packet(VideoPacket {
                id: 0,
                pts: 0,
                data: vec![0, 0, 0, 1, 0x65],
                ..Default::default()
            });
        }
        Err(err) => {
            assert!(
                !err.to_string().trim().is_empty(),
                "monitor window constructor returned an empty error"
            );
        }
    }
}
