// Contract tests for launcher-owned relay process lifetime.
//
// Scope: static guardrails around launcher runtime process management.
// Targets: split launcher UI/runtime modules under `client/src/launcher/`.
// Why: the launcher is the owner of the live relay. If it crashes, audio,
// video, and input streams must not keep running as leaked child processes.

const UI_RUNTIME_SRC: &str = concat!(
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui_runtime.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui_runtime/control_paths.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui_runtime/process_logs.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui_runtime/status_refresh.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui_runtime/status_details.rs"
    )),
);
const UI_SRC: &str = concat!(
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/message_and_network_state.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/control_requests.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/activation_setup.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/device_refresh_binding.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/local_test_bindings.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/media_device_bindings.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/relay_input_bindings.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/runtime_poll.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/runtime_poll/runtime_monitor_tick.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/stage_device_bindings.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/eye_capture_bindings.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/eye_capture_bindings/recording_support.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/eye_capture_bindings/recording_worker.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/utility_button_bindings.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui/utility_button_bindings/pki_support.rs"
    )),
);
const DEVICE_TEST_SRC: &str = concat!(
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/device_test.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/device_test/controller.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/device_test/local_preview.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/device_test/pipeline_helpers.rs"
    )),
);
const CAMERA_SRC: &str = concat!(
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/input/camera.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/input/camera/capture_pipeline.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/input/camera/encoder_selection.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/input/camera/preview_tap.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/input/camera/source_description.rs"
    )),
);
const MICROPHONE_SRC: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/client/src/input/microphone.rs"
));
const LAUNCHER_MOD_SRC: &str = include_str!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/client/src/launcher/mod.rs"
));
const MAIN_SRC: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/client/src/main.rs"));
const UI_COMPONENTS_SRC: &str = concat!(
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui_components.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui_components/types.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui_components/assemble_view.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/ui_components/build_shell.rs"
    )),
);
const PREVIEW_SRC: &str = concat!(
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/preview.rs"
    )),
    include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/client/src/launcher/preview/status_pipeline.rs"
    )),
);

#[test]
fn relay_child_gets_parent_identity_from_launcher() {
    assert!(UI_RUNTIME_SRC.contains("\"LESAVKA_LAUNCHER_PARENT_PID\""));
    assert!(UI_RUNTIME_SRC.contains("std::process::id().to_string()"));
    assert!(UI_RUNTIME_SRC.contains("LESAVKA_LAUNCHER_PARENT_START_TICKS"));
    assert!(UI_RUNTIME_SRC.contains("launcher_parent_start_ticks()"));
}

#[test]
fn relay_child_starts_safe_parent_watchdog_on_boot() {
    assert!(MAIN_SRC.contains("launcher::start_launcher_child_parent_watchdog_from_env();"));
    assert!(LAUNCHER_MOD_SRC.contains("start_launcher_child_parent_watchdog_from_env"));
    assert!(LAUNCHER_MOD_SRC.contains("launcher-parent-watchdog"));
    assert!(LAUNCHER_MOD_SRC.contains("std::process::exit(0);"));
    assert!(LAUNCHER_MOD_SRC.contains("proc_stat_start_ticks"));
}

#[test]
fn relay_address_locks_but_media_staging_stays_available_while_relay_is_live() {
    assert!(UI_RUNTIME_SRC.contains("widgets.server_entry.set_sensitive(!relay_live);"));
    assert!(
        UI_RUNTIME_SRC.contains(".camera_combo\n        .set_sensitive(state.channels.camera);")
    );
    assert!(UI_RUNTIME_SRC.contains(".camera_quality_combo"));
    assert!(UI_RUNTIME_SRC.contains("widgets.camera_quality_combo.set_sensitive("));
    assert!(UI_RUNTIME_SRC.contains("state.devices.camera.is_some()"));
    assert!(UI_RUNTIME_SRC.contains("state.camera_quality.is_some()"));
    assert!(
        UI_RUNTIME_SRC
            .contains(".microphone_combo\n        .set_sensitive(state.channels.microphone);")
    );
    assert!(
        UI_RUNTIME_SRC.contains(".speaker_combo\n        .set_sensitive(state.channels.audio);")
    );
    assert!(UI_RUNTIME_SRC.contains(".audio_gain_scale"));
    assert!(UI_RUNTIME_SRC.contains(".set_sensitive(state.channels.audio);"));
    assert!(UI_RUNTIME_SRC.contains(".mic_gain_scale"));
    assert!(UI_RUNTIME_SRC.contains(".set_sensitive(state.channels.microphone);"));
    assert!(UI_RUNTIME_SRC.contains("widgets.keyboard_combo.set_sensitive(!relay_live);"));
    assert!(UI_RUNTIME_SRC.contains("widgets.mouse_combo.set_sensitive(!relay_live);"));
    assert!(
        UI_RUNTIME_SRC
            .contains(".camera_channel_toggle\n        .set_sensitive(!relay_live || state.devices.camera.is_some());")
    );
    assert!(
        UI_RUNTIME_SRC.contains(
            ".microphone_channel_toggle\n        .set_sensitive(!relay_live || state.devices.microphone.is_some());"
        )
    );
    assert!(
        UI_RUNTIME_SRC.contains(
            ".audio_channel_toggle\n        .set_sensitive(!relay_live || state.devices.speaker.is_some());"
        )
    );
    assert!(UI_RUNTIME_SRC.contains("Soft-pause or resume this feed in the running relay"));
    assert!(UI_SRC.contains("{feed_label} selection is staged for the next relay launch"));
    assert!(UI_SRC.contains("Camera quality"));
    assert!(UI_SRC.contains("Microphone"));
    assert!(UI_SRC.contains("Speaker"));
    assert!(UI_RUNTIME_SRC.contains("\"Connect\""));
    assert!(UI_RUNTIME_SRC.contains("\"Disconnect\""));
}

#[test]
fn audio_gain_slider_callback_never_panics_on_refresh_reentry() {
    assert!(UI_SRC.contains("fn apply_audio_gain_change("));
    assert!(UI_SRC.contains("fn apply_mic_gain_change("));
    assert!(UI_SRC.contains("state.try_borrow_mut()"));
    assert!(UI_SRC.contains("return false;"));
    assert!(UI_SRC.contains("glib::idle_add_local_once"));
    assert!(!UI_SRC.contains("let mut state = state.borrow_mut();\n                        if state.audio_gain_percent == percent"));
}

#[test]
fn live_power_probe_failures_do_not_flip_relay_state_red() {
    assert!(UI_SRC.contains("PowerMessage::Refresh(Err(err))"));
    assert!(UI_SRC.contains("let relay_live = child_proc.borrow().is_some()"));
    assert!(UI_SRC.contains("if relay_live"));
    assert!(UI_SRC.contains("state.set_server_available(true);"));
    assert!(UI_SRC.contains("if !state.capture_power.available"));
}

#[test]
fn active_relay_keeps_local_upstream_camera_and_microphone_evidence_visible() {
    assert!(UI_RUNTIME_SRC.contains("UPLINK_CAMERA_PREVIEW_ENV"));
    assert!(UI_RUNTIME_SRC.contains("UPLINK_MIC_LEVEL_ENV"));
    assert!(UI_RUNTIME_SRC.contains("command.env(UPLINK_CAMERA_PREVIEW_ENV"));
    assert!(UI_RUNTIME_SRC.contains("command.env(UPLINK_MIC_LEVEL_ENV"));
    assert!(UI_SRC.contains("stop_local_capture_for_relay"));
    assert!(UI_SRC.contains("sync_relay_uplink_probe"));

    assert!(DEVICE_TEST_SRC.contains("fn start_relay_file("));
    assert!(DEVICE_TEST_SRC.contains("run_camera_file_preview_feed"));
    assert!(DEVICE_TEST_SRC.contains("read_camera_preview_tap"));
    assert!(DEVICE_TEST_SRC.contains("LocalMicrophoneLevelProbe"));
    assert!(DEVICE_TEST_SRC.contains("read_microphone_level_tap"));

    assert!(CAMERA_SRC.contains("LESAVKA_UPLINK_CAMERA_PREVIEW"));
    assert!(CAMERA_SRC.contains("appsink name=preview_sink"));
    assert!(CAMERA_SRC.contains("spawn_camera_preview_tap"));
    assert!(MICROPHONE_SRC.contains("LESAVKA_UPLINK_MIC_LEVEL"));
    assert!(MICROPHONE_SRC.contains("appsink name=level_sink"));
    assert!(MICROPHONE_SRC.contains("spawn_mic_level_tap"));
}

#[test]
fn launcher_webcam_quality_selection_reaches_preview_and_relay_env() {
    assert!(UI_SRC.contains("selected_camera_quality(&camera_quality_combo"));
    assert!(UI_SRC.contains("selected_camera_quality(&camera_quality_combo_read"));
    assert!(UI_SRC.contains("sync_camera_quality_selection"));
    assert!(UI_SRC.contains("let camera_quality_syncing = Rc::new(Cell::new(false));"));
    assert!(UI_SRC.contains("camera_quality_syncing.set(true);"));
    assert!(UI_SRC.contains("if camera_quality_syncing.get()"));
    assert!(UI_COMPONENTS_SRC.contains("webcam_transport_syncing: Rc<Cell<bool>>"));
    assert!(UI_COMPONENTS_SRC.contains("upstream_audio_transport_syncing: Rc<Cell<bool>>"));
    assert!(UI_RUNTIME_SRC.contains("widgets.webcam_transport_syncing.set(true);"));
    assert!(UI_SRC.contains("if widgets.webcam_transport_syncing.get()"));
    assert!(UI_SRC.contains("state.try_borrow_mut()"));
    assert!(UI_SRC.contains("tests.set_camera_quality"));
    assert!(DEVICE_TEST_SRC.contains("pub fn set_camera_quality"));
    assert!(DEVICE_TEST_SRC.contains("build_camera_preview_pipeline(&device, mode)"));
    assert!(DEVICE_TEST_SRC.contains("capsfilter caps=\\\"video/x-raw"));
    assert!(CAMERA_SRC.contains("fn resolved_capture_profile"));
    assert!(CAMERA_SRC.contains("LESAVKA_CAM_LOCK_TO_SERVER_PROFILE"));
    assert!(CAMERA_SRC.contains("LESAVKA_CAM_EMIT_UI_PROFILE"));
    assert!(LAUNCHER_MOD_SRC.contains("\"LESAVKA_CAM_WIDTH\""));
    assert!(LAUNCHER_MOD_SRC.contains("\"LESAVKA_CAM_H264_KBIT\""));
}

#[test]
fn launcher_audio_transport_selection_reaches_relay_env_and_chips() {
    assert!(LAUNCHER_MOD_SRC.contains("\"LESAVKA_UPLINK_AUDIO_CODEC\""));
    assert!(LAUNCHER_MOD_SRC.contains("\"LESAVKA_MIC_NOISE_SUPPRESSION\""));
    assert!(UI_RUNTIME_SRC.contains("state.upstream_audio_transport.as_common_codec()"));
    assert!(UI_RUNTIME_SRC.contains("state.mic_noise_suppression"));
    assert!(UI_RUNTIME_SRC.contains("state.upstream_audio_transport.label()"));
    assert!(UI_RUNTIME_SRC.contains("Upstream microphone transport:"));
    assert!(UI_RUNTIME_SRC.contains("Noise suppression is enabled before transport."));
    assert!(
        UI_RUNTIME_SRC.contains("Changing upstream audio transport restarts the microphone path")
    );
    assert!(UI_RUNTIME_SRC.contains(
        "Choose PCM for the known-good route or Opus for compressed upstream audio experiments."
    ));
    assert!(UI_SRC.contains("audio_combo.connect_changed"));
    assert!(UI_SRC.contains("toggle.connect_toggled"));
}

#[test]
fn launcher_utility_buttons_still_bind_to_live_actions() {
    assert!(UI_SRC.contains("widgets.clipboard_button.connect_clicked"));
    assert!(UI_SRC.contains("certs_button.connect_clicked"));
    assert!(UI_SRC.contains("Choose Lesavka Client TLS Bundle"));
    assert!(UI_SRC.contains("install_client_pki_bundle(&bundle)"));
    assert!(UI_SRC.contains(".config/lesavka/pki"));
    assert!(UI_SRC.contains("send_clipboard_text_to_remote(&server_addr, &text)"));
    assert!(UI_SRC.contains("Start the relay before sending clipboard text."));
    assert!(!UI_SRC.contains("widgets.probe_button.connect_clicked"));
    assert!(!UI_SRC.contains("Quality probe command copied to the local clipboard."));
    assert!(UI_SRC.contains("pane.save_button.connect_clicked"));
    assert!(UI_SRC.contains("Choose Eye Capture Folder"));
    assert!(UI_SRC.contains("pane.clip_button.connect_clicked"));
    assert!(UI_SRC.contains("clip saved to"));
    assert!(UI_SRC.contains("pane.record_audio_button.connect_clicked"));
    assert!(UI_SRC.contains("enum EyeRecordAudioMode"));
    assert!(UI_SRC.contains("Self::NoAudio => \"🔇\""));
    assert!(UI_SRC.contains("Self::UpstreamOnly => \"👂↑\""));
    assert!(UI_SRC.contains("Self::DownstreamOnly => \"👂↓\""));
    assert!(UI_SRC.contains("Self::Both => \"👂↕\""));
    assert!(UI_SRC.contains("Audio mode is locked for the active recording."));
    assert!(UI_SRC.contains("LESAVKA_EYE_RECORD_UPSTREAM_AUDIO_SOURCE"));
    assert!(UI_SRC.contains("LESAVKA_EYE_RECORD_DOWNSTREAM_AUDIO_SOURCE"));
    assert!(UI_SRC.contains("[1:a][2:a]amix=inputs=2:duration=shortest:normalize=0[a]"));
    assert!(UI_SRC.contains("record_button.connect_clicked"));
    assert!(UI_SRC.contains("preview.start_recording_tap(monitor_id, recording_surface)"));
    assert!(UI_SRC.contains("spawn_raw_video_encoder("));
    assert!(UI_SRC.contains("\"-f\","));
    assert!(UI_SRC.contains("\"rawvideo\""));
    assert!(UI_SRC.contains("button.add_css_class(\"recording-active\");"));
    assert!(UI_SRC.contains("button.remove_css_class(\"recording-active\");"));
    assert!(UI_SRC.contains("recording saved to"));
    assert!(UI_SRC.contains("press the record button again to finish."));
    assert!(!UI_SRC.contains("queue_record_frame("));
    assert!(!UI_SRC.contains("frame-%06d.png"));
    assert!(UI_SRC.contains("fn default_eye_capture_root() -> PathBuf"));
    assert!(UI_SRC.contains(".join(\"Pictures\").join(\"lesavka\")"));
    assert!(UI_SRC.contains("fn capture_day_slug() -> String"));
    assert!(
        UI_SRC.contains("ensure_eye_capture_dir(borrowed.save_dir_override.as_deref(), \"clips\")")
    );
    assert!(
        UI_SRC.contains(
            "ensure_eye_capture_dir(borrowed.save_dir_override.as_deref(), \"recordings\")"
        )
    );
    assert!(UI_SRC.contains("widgets.usb_recover_button.connect_clicked"));
    assert!(UI_SRC.contains("widgets.wake_combo.connect_changed"));
    assert!(UI_SRC.contains("write_wake_control_request(&path, minutes)"));
    assert!(UI_RUNTIME_SRC.contains("WAKE_CONTROL_ENV"));
    assert!(UI_RUNTIME_SRC.contains("command.env(WAKE_CONTROL_ENV, wake_path);"));
    assert!(UI_SRC.contains("recover_usb_soft(&server_addr)"));
    assert!(UI_SRC.contains("recover_uac_soft(&server_addr)"));
    assert!(UI_SRC.contains("recover_uvc_soft(&server_addr)"));
    assert!(UI_SRC.contains("Recover HID 2/3: handles reopened."));
    assert!(UI_SRC.contains("Recover Audio 2/3: old epoch released."));
    assert!(UI_SRC.contains("Recover Video 2/3: webcam sink retired."));
}

#[test]
fn launcher_normalizes_bare_relay_addresses_to_https() {
    assert!(UI_RUNTIME_SRC.contains("pub fn normalize_server_addr(raw: &str) -> String"));
    assert!(UI_RUNTIME_SRC.contains("trimmed.contains(\"://\")"));
    assert!(UI_RUNTIME_SRC.contains("format!(\"https://{trimmed}\")"));
    assert!(UI_RUNTIME_SRC.contains("entry.set_text(&normalized);"));
}

#[test]
fn server_chip_distinguishes_reachable_from_connected() {
    assert!(UI_RUNTIME_SRC.contains("fn server_light_state("));
    assert!(UI_RUNTIME_SRC.contains("StatusLightState::Connected"));
    assert!(UI_RUNTIME_SRC.contains("server_versions_match(state)"));
    assert!(UI_RUNTIME_SRC.contains("} else if relay_live {"));
    assert!(UI_RUNTIME_SRC.contains("StatusLightState::Caution"));
    assert!(UI_RUNTIME_SRC.contains("fn server_version_label("));
    assert!(UI_RUNTIME_SRC.contains("return \"???\".to_string();"));
}

#[test]
fn relay_action_button_marks_disconnect_as_destructive() {
    assert!(UI_RUNTIME_SRC.contains("set_rail_button_label("));
    assert!(UI_RUNTIME_SRC.contains("widgets.start_button.add_css_class(\"destructive-action\")"));
    assert!(
        UI_RUNTIME_SRC.contains("widgets.start_button.remove_css_class(\"destructive-action\")")
    );
    assert!(UI_RUNTIME_SRC.contains("widgets.start_button.add_css_class(\"suggested-action\")"));
}

#[test]
fn launcher_brand_uses_readable_icon_size() {
    assert!(UI_COMPONENTS_SRC.contains("brand_icon.set_pixel_size(44);"));
    assert!(UI_COMPONENTS_SRC.contains("brand_icon.set_valign(gtk::Align::Center);"));
    assert!(UI_COMPONENTS_SRC.contains("heading.set_valign(gtk::Align::Center);"));
}

#[test]
fn preview_logs_are_actionable_with_short_mythic_flavor() {
    assert!(PREVIEW_SRC.contains("waking {eye} eye preview"));
    assert!(PREVIEW_SRC.contains("connecting {eye} eye feed"));
    assert!(PREVIEW_SRC.contains("waiting for first frame"));
    assert!(PREVIEW_SRC.contains("Preview stream error. See session log."));
    assert!(PREVIEW_SRC.contains("«У лукоморья дуб зелёный; златая цепь на дубе том…»"));
    assert!(PREVIEW_SRC.contains("«Там чудеса: там леший бродит, русалка на ветвях сидит…»"));
    assert!(PREVIEW_SRC.contains("«Подымите мне веки: не вижу!»"));
    assert!(PREVIEW_SRC.contains("«Идёт направо — песнь заводит, налево — сказку говорит…»"));
    assert!(
        PREVIEW_SRC
            .contains("«Там царь Кащей над златом чахнет; там русский дух… там Русью пахнет!»")
    );
    assert!(PREVIEW_SRC.contains("«Там на неведомых дорожках следы невиданных зверей…»"));
    assert!(PREVIEW_SRC.contains("«Избушка, избушка! Встань к лесу задом, ко мне передом.»"));
    assert!(PREVIEW_SRC.contains("«Фу-фу! Русским духом пахнет!»"));
    assert!(PREVIEW_SRC.contains("«Дела давно минувших дней, преданья старины глубокой…»"));
    assert!(!PREVIEW_SRC.contains("is waking the preview spell"));
    assert!(!PREVIEW_SRC.contains("opens the watchfire"));
    assert!(!PREVIEW_SRC.contains("log spellbook"));
}
