use super::{
    MIC_REPLAY_MAX_BYTES, PreviewFrame, build_wav_bytes, camera_preview_mode,
    camera_preview_pipeline_desc, microphone_monitor_pipeline_desc, mirror_preview_frame,
    normalize_camera_selection, push_recent_audio, read_camera_preview_tap,
    read_microphone_level_tap, resolve_camera_device,
};
use crate::launcher::devices::CameraMode;
use std::sync::{Arc, Mutex};

#[test]
fn resolve_camera_device_accepts_explicit_paths_and_catalog_names() {
    assert_eq!(resolve_camera_device("/dev/video0"), "/dev/video0");
    assert_eq!(
        resolve_camera_device("usb-Logitech_C920-video-index0"),
        "/dev/v4l/by-id/usb-Logitech_C920-video-index0"
    );
}

#[test]
fn normalize_camera_selection_drops_auto_and_blank_values() {
    assert_eq!(normalize_camera_selection(None), None);
    assert_eq!(normalize_camera_selection(Some("")), None);
    assert_eq!(normalize_camera_selection(Some("auto")), None);
    assert_eq!(
        normalize_camera_selection(Some("usb-Logitech_C920-video-index0")),
        Some("usb-Logitech_C920-video-index0".to_string())
    );
}

#[test]
fn camera_preview_pipeline_scales_after_source_instead_of_pinning_raw_source_caps() {
    let desc = camera_preview_pipeline_desc("/dev/video0", None);
    assert!(desc.contains("v4l2src device=\"/dev/video0\""));
    assert!(desc.contains("videoconvert ! videoscale ! videorate !"));
    assert!(!desc.contains("v4l2src device=\"/dev/video0\" do-timestamp=true ! video/x-raw,"));
}

#[test]
fn camera_preview_pipeline_requests_selected_webcam_quality_before_scaling() {
    let desc = camera_preview_pipeline_desc("/dev/video0", Some(CameraMode::new(1920, 1080, 30)));
    assert!(desc.contains("video/x-raw,width=(int)1920,height=(int)1080,framerate=(fraction)30/1"));
    assert!(desc.contains("image/jpeg,width=(int)1920,height=(int)1080,framerate=(fraction)30/1"));
    assert!(desc.contains("decodebin ! videoconvert ! videoscale"));
    assert!(desc.contains("video/x-raw,format=RGBA,width=1920,height=1080,framerate=30/1"));
    assert!(!desc.contains("width=128,height=72"));
}

#[test]
fn camera_preview_mode_defaults_to_hd_and_tracks_selected_quality() {
    assert_eq!(camera_preview_mode(None), (1280, 720, 30));
    assert_eq!(
        camera_preview_mode(Some(CameraMode::new(1920, 1080, 30))),
        (1920, 1080, 30)
    );
}

#[test]
fn microphone_monitor_uses_pulse_for_launcher_catalog_source_names() {
    let desc = microphone_monitor_pipeline_desc(
        "alsa_input.usb-Neat_Microphones_Bumblebee_II_USB_Microphone-00.mono-fallback",
        None,
    );
    assert!(desc.contains("pulsesrc device=\"alsa_input.usb-Neat_Microphones"));
    assert!(!desc.contains("pipewiresrc target-object"));
}

#[test]
fn push_recent_audio_keeps_only_last_three_seconds() {
    let buffer = Arc::new(Mutex::new(Vec::new()));
    push_recent_audio(&buffer, &vec![1u8; MIC_REPLAY_MAX_BYTES / 2]);
    push_recent_audio(&buffer, &vec![2u8; MIC_REPLAY_MAX_BYTES]);
    let stored = buffer.lock().expect("buffer").clone();
    assert_eq!(stored.len(), MIC_REPLAY_MAX_BYTES);
    assert!(stored.contains(&2));
}

#[test]
fn build_wav_bytes_writes_a_valid_riff_header() {
    let audio = vec![0u8; 32];
    let wav = build_wav_bytes(&audio, 16_000, 1, 16);
    assert!(wav.starts_with(b"RIFF"));
    assert_eq!(&wav[8..12], b"WAVE");
    assert_eq!(&wav[36..40], b"data");
    assert_eq!(wav.len(), 44 + audio.len());
}

#[test]
fn relay_camera_preview_tap_round_trips_rgba_frame() {
    let path =
        std::env::temp_dir().join(format!("lesavka-camera-preview-tap-{}", std::process::id()));
    std::fs::write(
        &path,
        [b"LESAVKA_RGBA 2 2 8\n".as_slice(), &[1_u8; 16]].concat(),
    )
    .expect("write tap");

    let frame = read_camera_preview_tap(&path).expect("read tap");
    assert_eq!(frame.width, 2);
    assert_eq!(frame.height, 2);
    assert_eq!(frame.stride, 8);
    assert_eq!(frame.rgba.len(), 16);
    let _ = std::fs::remove_file(path);
}

#[test]
fn relay_microphone_level_tap_clamps_values() {
    let path = std::env::temp_dir().join(format!("lesavka-mic-level-tap-{}", std::process::id()));
    std::fs::write(&path, "1.25\n").expect("write high");
    assert_eq!(read_microphone_level_tap(&path), Some(1.0));

    std::fs::write(&path, "-0.5\n").expect("write low");
    assert_eq!(read_microphone_level_tap(&path), Some(0.0));

    std::fs::write(&path, "not-a-number\n").expect("write invalid");
    assert_eq!(read_microphone_level_tap(&path), None);
    let _ = std::fs::remove_file(path);
}

#[test]
fn mirror_preview_frame_flips_each_row_without_changing_channels() {
    let mut frame = PreviewFrame {
        width: 2,
        height: 1,
        stride: 8,
        rgba: vec![1, 2, 3, 4, 9, 10, 11, 12],
    };
    mirror_preview_frame(&mut frame);
    assert_eq!(frame.rgba, vec![9, 10, 11, 12, 1, 2, 3, 4]);
}
