use anyhow::Result;
use gtk::{gdk, glib, prelude::*};
use std::{
    cell::RefCell,
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
    process::{Child, Command, Stdio},
    rc::Rc,
    sync::mpsc::Sender,
    time::{SystemTime, UNIX_EPOCH},
};

use super::{
    LAUNCHER_CLIPBOARD_CONTROL_ENV, LAUNCHER_FOCUS_SIGNAL_ENV,
    device_test::{DeviceTestController, DeviceTestKind},
    diagnostics::{SnapshotReport, quality_probe_command},
    launcher_clipboard_control_path, launcher_focus_signal_path,
    preview::{LauncherPreview, PreviewSurface},
    runtime_env_vars,
    state::{BreakoutSizeChoice, CapturePowerStatus, DisplaySurface, InputRouting, LauncherState},
    ui_components::{
        ConsoleLogLevel, DisplayPaneWidgets, LauncherWidgets, PopoutWindowHandle,
        set_rail_button_label,
    },
};

pub const INPUT_CONTROL_ENV: &str = "LESAVKA_LAUNCHER_INPUT_CONTROL";
pub const INPUT_STATE_ENV: &str = "LESAVKA_LAUNCHER_INPUT_STATE";
pub const TOGGLE_KEY_CONTROL_ENV: &str = "LESAVKA_LAUNCHER_TOGGLE_KEY_CONTROL";
pub const WAKE_CONTROL_ENV: &str = "LESAVKA_LAUNCHER_WAKE_CONTROL";
pub const AUDIO_GAIN_CONTROL_ENV: &str = "LESAVKA_AUDIO_GAIN_CONTROL";
pub const MIC_GAIN_CONTROL_ENV: &str = "LESAVKA_MIC_GAIN_CONTROL";
pub const MEDIA_CONTROL_ENV: &str = crate::live_media_control::MEDIA_CONTROL_ENV;
pub const UPLINK_CAMERA_PREVIEW_ENV: &str = "LESAVKA_UPLINK_CAMERA_PREVIEW";
pub const UPLINK_MIC_LEVEL_ENV: &str = "LESAVKA_UPLINK_MIC_LEVEL";
pub use crate::uplink_telemetry::{DEFAULT_UPLINK_TELEMETRY_PATH, UPLINK_TELEMETRY_ENV};
pub const DEFAULT_INPUT_CONTROL_PATH: &str = "/tmp/lesavka-launcher-input.control";
pub const DEFAULT_INPUT_STATE_PATH: &str = "/tmp/lesavka-launcher-input.state";
pub const DEFAULT_TOGGLE_KEY_CONTROL_PATH: &str = "/tmp/lesavka-launcher-toggle-key.control";
pub const DEFAULT_WAKE_CONTROL_PATH: &str = "/tmp/lesavka-launcher-wake.control";
pub const DEFAULT_AUDIO_GAIN_CONTROL_PATH: &str = "/tmp/lesavka-audio-gain.control";
pub const DEFAULT_MIC_GAIN_CONTROL_PATH: &str = "/tmp/lesavka-mic-gain.control";
pub const DEFAULT_MEDIA_CONTROL_PATH: &str = crate::live_media_control::DEFAULT_MEDIA_CONTROL_PATH;
pub const DEFAULT_UPLINK_CAMERA_PREVIEW_PATH: &str = "/tmp/lesavka-uplink-camera-preview.rgba";
pub const DEFAULT_UPLINK_MIC_LEVEL_PATH: &str = "/tmp/lesavka-uplink-mic-level.value";

pub type RelayChild = Child;

pub fn refresh_launcher_ui(widgets: &LauncherWidgets, state: &LauncherState, child_running: bool) {
    let relay_live = child_running || state.remote_active;
    let latest_sample = widgets.diagnostics_log.borrow().latest().cloned();
    let server_label = server_version_label(state);
    set_status_light(
        &widgets.summary.relay_light,
        server_light_state(state, relay_live),
    );
    widgets.summary.relay_value.set_text(&server_label);
    widgets
        .summary
        .relay_value
        .set_tooltip_text(Some(&server_label));
    set_status_light(
        &widgets.summary.routing_light,
        StatusLightState::from_active(matches!(state.routing, InputRouting::Remote)),
    );
    widgets
        .summary
        .routing_value
        .set_text(&capitalize(routing_name(state.routing)));
    let gpio_label = if state.server_available {
        gpio_power_label(&state.capture_power)
    } else {
        "Off".to_string()
    };
    set_status_light(
        &widgets.summary.gpio_light,
        if state.server_available {
            gpio_light_state(&state.capture_power)
        } else {
            StatusLightState::Idle
        },
    );
    widgets.summary.gpio_value.set_text(&gpio_label);
    widgets
        .summary
        .gpio_value
        .set_tooltip_text(Some(&capture_power_detail(&state.capture_power)));
    widgets
        .summary
        .shortcut_value
        .set_text(&toggle_key_label(&state.swap_key));

    let (left_eye_state, left_eye_value, left_eye_tooltip) =
        eye_stream_health(latest_sample.as_ref(), EyeChipSide::Left, relay_live);
    set_status_light(&widgets.summary.left_eye_light, left_eye_state);
    widgets.summary.left_eye_value.set_text(&left_eye_value);
    widgets
        .summary
        .left_eye_value
        .set_tooltip_text(Some(&left_eye_tooltip));
    let (right_eye_state, right_eye_value, right_eye_tooltip) =
        eye_stream_health(latest_sample.as_ref(), EyeChipSide::Right, relay_live);
    set_status_light(&widgets.summary.right_eye_light, right_eye_state);
    widgets.summary.right_eye_value.set_text(&right_eye_value);
    widgets
        .summary
        .right_eye_value
        .set_tooltip_text(Some(&right_eye_tooltip));

    let (usb_state, usb_value) = recovery_usb_health(state, relay_live);
    set_status_light(&widgets.summary.usb_light, usb_state);
    widgets.summary.usb_value.set_text(&usb_value);
    widgets.summary.usb_value.set_tooltip_text(Some(&usb_value));
    let (uac_state, uac_value) = recovery_uac_health(
        state,
        relay_live,
        latest_sample
            .as_ref()
            .map(|sample| &sample.upstream_microphone),
    );
    set_status_light(&widgets.summary.uac_light, uac_state);
    widgets.summary.uac_value.set_text(&uac_value);
    widgets.summary.uac_value.set_tooltip_text(Some(&format!(
        "Upstream microphone transport: {}. {}",
        state.upstream_audio_transport.label(),
        if state.mic_noise_suppression {
            "Noise suppression is enabled before transport."
        } else {
            "Raw microphone audio is sent before transport."
        }
    )));
    let (uvc_state, uvc_value) = recovery_uvc_health(
        state,
        relay_live,
        latest_sample
            .as_ref()
            .map(|sample| &sample.upstream_camera),
    );
    set_status_light(&widgets.summary.uvc_light, uvc_state);
    widgets.summary.uvc_value.set_text(&uvc_value);
    widgets.summary.uvc_value.set_tooltip_text(Some(&format!(
        "Upstream webcam transport: {}. UVC payload contract: {} via {}, cap {} bytes. {} Integrity: {}. {}",
        state.effective_webcam_transport().label(),
        state.upstream_sync.uvc_contract_status,
        state.upstream_sync.uvc_transport,
        state
            .upstream_sync
            .uvc_enforced_frame_cap_bytes
            .map(|value| value.to_string())
            .unwrap_or_else(|| "unavailable".to_string()),
        state.upstream_sync.uvc_contract_detail,
        state.upstream_sync.uvc_integrity_status,
        state.upstream_sync.uvc_integrity_detail,
    )));
    let (lag_state, lag_value, lag_tooltip) =
        upstream_lag_health(&state.upstream_sync, relay_live);
    set_status_light(&widgets.summary.upstream_lag_light, lag_state);
    widgets.summary.upstream_lag_value.set_text(&lag_value);
    widgets
        .summary
        .upstream_lag_value
        .set_tooltip_text(Some(&lag_tooltip));

    let power_detail = if state.server_available {
        capture_power_detail(&state.capture_power)
    } else {
        "relay host is offline; GPIO power state is unavailable".to_string()
    };
    widgets.power_detail.set_text(&power_detail);
    if (widgets.audio_gain_scale.value() - state.audio_gain_percent as f64).abs() > f64::EPSILON {
        widgets
            .audio_gain_scale
            .set_value(state.audio_gain_percent as f64);
    }
    widgets.audio_gain_value.set_text(&state.audio_gain_label());
    if (widgets.mic_gain_scale.value() - state.mic_gain_percent as f64).abs() > f64::EPSILON {
        widgets
            .mic_gain_scale
            .set_value(state.mic_gain_percent as f64);
    }
    widgets.mic_gain_value.set_text(&state.mic_gain_label());
    if widgets.camera_channel_toggle.is_active() != state.channels.camera {
        widgets
            .camera_channel_toggle
            .set_active(state.channels.camera);
    }
    if widgets.microphone_channel_toggle.is_active() != state.channels.microphone {
        widgets
            .microphone_channel_toggle
            .set_active(state.channels.microphone);
    }
    if widgets.noise_suppression_toggle.is_active() != state.mic_noise_suppression {
        widgets
            .noise_suppression_toggle
            .set_active(state.mic_noise_suppression);
    }
    if widgets.audio_channel_toggle.is_active() != state.channels.audio {
        widgets
            .audio_channel_toggle
            .set_active(state.channels.audio);
    }
    set_rail_button_label(
        &widgets.start_button,
        if relay_live { "Disconnect" } else { "Connect" },
    );
    if relay_live {
        widgets.start_button.remove_css_class("suggested-action");
        widgets.start_button.add_css_class("destructive-action");
    } else {
        widgets.start_button.remove_css_class("destructive-action");
        widgets.start_button.add_css_class("suggested-action");
    }
    widgets.start_button.set_sensitive(true);
    widgets.server_entry.set_sensitive(!relay_live);
    widgets.start_button.set_tooltip_text(Some(if relay_live {
        "Disconnect the relay session."
    } else {
        "Start relay and previews."
    }));
    widgets.clipboard_button.set_sensitive(relay_live);
    widgets
        .usb_recover_button
        .set_sensitive(state.server_available);
    widgets
        .uac_recover_button
        .set_sensitive(state.server_available);
    widgets
        .uvc_recover_button
        .set_sensitive(state.server_available);
    widgets.device_refresh_button.set_sensitive(true);
    widgets
        .camera_combo
        .set_sensitive(state.channels.camera);
    widgets.camera_quality_combo.set_sensitive(
        state.channels.camera
            && state.devices.camera.is_some()
            && state.camera_quality.is_some(),
    );
    widgets
        .microphone_combo
        .set_sensitive(state.channels.microphone);
    widgets
        .speaker_combo
        .set_sensitive(state.channels.audio);
    widgets
        .audio_gain_scale
        .set_sensitive(state.channels.audio);
    widgets.keyboard_combo.set_sensitive(!relay_live);
    widgets.mouse_combo.set_sensitive(!relay_live);
    widgets
        .camera_channel_toggle
        .set_sensitive(!relay_live || state.devices.camera.is_some());
    widgets
        .microphone_channel_toggle
        .set_sensitive(!relay_live || state.devices.microphone.is_some());
    widgets
        .noise_suppression_toggle
        .set_sensitive(state.channels.microphone);
    widgets
        .audio_channel_toggle
        .set_sensitive(!relay_live || state.devices.speaker.is_some());
    let media_toggle_tooltip = if relay_live {
        "Soft-pause or resume this feed in the running relay without resetting USB. Use Recover for hard device resets."
    } else {
        "Choose which media feeds are enabled when the next relay session starts."
    };
    widgets
        .camera_channel_toggle
        .set_tooltip_text(Some(media_toggle_tooltip));
    widgets
        .microphone_channel_toggle
        .set_tooltip_text(Some(media_toggle_tooltip));
    widgets
        .noise_suppression_toggle
        .set_tooltip_text(Some(if state.mic_noise_suppression {
            "Noise cancellation is on. Toggle to send raw microphone audio aside from gain."
        } else {
            "Noise cancellation is off. Toggle to suppress steady room, street, and fan noise."
        }));
    widgets
        .audio_channel_toggle
        .set_tooltip_text(Some(media_toggle_tooltip));
    widgets
        .camera_test_button
        .set_sensitive(!relay_live && state.channels.camera);
    widgets
        .microphone_test_button
        .set_sensitive(!relay_live && state.channels.microphone);
    widgets
        .mic_gain_scale
        .set_sensitive(state.channels.microphone);
    widgets
        .speaker_test_button
        .set_sensitive(!relay_live && state.channels.audio);
    if widgets.webcam_transport_combo.active_id().as_deref()
        != Some(state.effective_webcam_transport().as_id())
    {
        widgets.webcam_transport_syncing.set(true);
        widgets
            .webcam_transport_combo
            .set_active_id(Some(state.effective_webcam_transport().as_id()));
        widgets.webcam_transport_syncing.set(false);
    }
    widgets
        .webcam_transport_combo
        .set_sensitive(state.channels.camera);
    widgets
        .webcam_transport_combo
        .set_tooltip_text(Some(if relay_live {
            "Changing upstream webcam transport restarts the live camera path; the picture may pause briefly."
        } else {
            "Choose upstream webcam transport for the next relay launch; MJPEG is the safe calibrated default."
        }));
    if widgets
        .upstream_audio_transport_combo
        .active_id()
        .as_deref()
        != Some(state.upstream_audio_transport.as_id())
    {
        widgets.upstream_audio_transport_syncing.set(true);
        widgets
            .upstream_audio_transport_combo
            .set_active_id(Some(state.upstream_audio_transport.as_id()));
        widgets.upstream_audio_transport_syncing.set(false);
    }
    widgets
        .upstream_audio_transport_combo
        .set_sensitive(state.channels.microphone);
    widgets
        .upstream_audio_transport_combo
        .set_tooltip_text(Some(if relay_live {
            "Changing upstream audio transport restarts the microphone path; PCM is stable, Opus is compressed and experimental."
        } else {
            "Choose PCM for the known-good route or Opus for compressed upstream audio experiments."
        }));
    widgets.input_toggle_button.set_label(match state.routing {
        InputRouting::Remote => "Route Local",
        InputRouting::Local => "Route Remote",
    });
    widgets
        .input_toggle_button
        .set_tooltip_text(Some(match state.routing {
            InputRouting::Remote => "Route inputs back local.",
            InputRouting::Local => "Route inputs to remote.",
        }));
    widgets.swap_key_button.set_label("Set Swap Key");
    widgets
        .swap_key_button
        .set_tooltip_text(Some(if state.swap_key_binding {
            "Press the new swap key."
        } else {
            "Set the swap shortcut."
        }));
    let power_available = state.capture_power.available;
    widgets
        .power_auto_button
        .set_sensitive(power_available && !matches!(state.capture_power.mode.as_str(), "auto"));
    widgets.power_on_button.set_sensitive(
        power_available && !matches!(state.capture_power.mode.as_str(), "forced-on"),
    );
    widgets.power_off_button.set_sensitive(
        power_available && !matches!(state.capture_power.mode.as_str(), "forced-off"),
    );
    sync_power_mode_button_styles(widgets, state.capture_power.mode.as_str());

    for monitor_id in 0..2 {
        refresh_display_pane(
            &widgets.display_panes[monitor_id],
            state.display_surface(monitor_id),
        );
    }
    refresh_diagnostics_report(widgets, state, child_running);
}

pub fn refresh_test_buttons(widgets: &LauncherWidgets, tests: &mut DeviceTestController) {
    let camera_running = tests.is_running(DeviceTestKind::Camera);
    let microphone_running = tests.is_running(DeviceTestKind::Microphone);
    let microphone_replay_running = tests.is_running(DeviceTestKind::MicrophoneReplay);
    let speaker_running = tests.is_running(DeviceTestKind::Speaker);

    widgets.camera_test_button.set_label(if camera_running {
        "Stop Preview"
    } else {
        "Start Preview"
    });
    widgets
        .camera_preview_stack
        .set_visible_child_name(if camera_running { "live" } else { "idle" });
    let camera_mirrored = tests.camera_preview_mirrored();
    if widgets.camera_mirror_button.is_active() != camera_mirrored {
        widgets.camera_mirror_button.set_active(camera_mirrored);
    }
    widgets.camera_mirror_button.set_visible(camera_running);
    widgets.camera_mirror_button.set_sensitive(camera_running);
    if !camera_running {
        widgets.camera_mirror_revealer.set_reveal_child(false);
    }
    widgets
        .microphone_test_button
        .set_label(if microphone_running {
            "Stop Monitor"
        } else {
            "Monitor Mic"
        });
    widgets
        .microphone_replay_button
        .set_label(if microphone_replay_running {
            "Stop"
        } else {
            "Replay"
        });
    widgets
        .microphone_replay_button
        .set_sensitive(microphone_replay_running || tests.microphone_replay_ready());
    widgets.speaker_test_button.set_label(if speaker_running {
        "Stop Tone"
    } else {
        "Play Tone"
    });
    widgets.audio_check_detail.set_text(&local_test_detail(
        camera_running,
        microphone_running,
        speaker_running,
        microphone_replay_running,
    ));
    if microphone_running {
        let level = tests.microphone_level_fraction().clamp(0.0, 1.0);
        widgets.audio_check_meter.set_fraction(level);
        widgets
            .audio_check_meter
            .set_text(Some(&format!("Mic {:>3}%", (level * 100.0).round() as u32)));
    } else if speaker_running || microphone_replay_running {
        widgets.audio_check_meter.set_text(Some("Playback"));
        widgets.audio_check_meter.pulse();
    } else {
        widgets.audio_check_meter.set_fraction(0.0);
        widgets.audio_check_meter.set_text(Some("Idle"));
    }
}

pub fn update_test_action_result(
    widgets: &LauncherWidgets,
    tests: &mut DeviceTestController,
    result: Result<bool>,
    start_msg: &str,
    stop_msg: &str,
) {
    match result {
        Ok(true) => widgets.status_label.set_text(start_msg),
        Ok(false) => widgets.status_label.set_text(stop_msg),
        Err(err) => widgets
            .status_label
            .set_text(&format!("Device test failed: {err}")),
    }
    refresh_test_buttons(widgets, tests);
}
