#[cfg(not(coverage))]
use crate::video_support::{
    h264_decoder_launch_fragment, h264_decoder_preference_order, require_h264_decoder,
    software_video_fallback_allowed,
};
#[cfg(not(coverage))]
use anyhow::{Context, Result};
#[cfg(not(coverage))]
use gstreamer as gst;
#[cfg(not(coverage))]
use gstreamer::prelude::{Cast, ElementExt, GstBinExt, GstObjectExt, PadExt};
#[cfg(not(coverage))]
use gstreamer_app as gst_app;
#[cfg(not(coverage))]
use gtk::prelude::WidgetExt;
#[cfg(not(coverage))]
use gtk::{gdk, glib};
use lesavka_common::{
    eye_source::eye_source_mode_for_request,
    lesavka::{MonitorRequest, VideoPacket, relay_client::RelayClient},
};
#[cfg(not(coverage))]
use std::collections::VecDeque;
#[cfg(not(coverage))]
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
#[cfg(not(coverage))]
use std::sync::{Arc, Mutex};
#[cfg(not(coverage))]
use std::time::{Duration, Instant};
#[cfg(not(coverage))]
use tonic::Request;
#[cfg(not(coverage))]
use tracing::{debug, warn};
#[cfg(not(coverage))]
const PREVIEW_WIDTH: i32 = 960;
#[cfg(not(coverage))]
const PREVIEW_HEIGHT: i32 = 540;
#[cfg(not(coverage))]
const INLINE_PREVIEW_REQUEST_WIDTH: i32 = 1280;
#[cfg(not(coverage))]
const INLINE_PREVIEW_REQUEST_HEIGHT: i32 = 720;
#[cfg(not(coverage))]
const INLINE_PREVIEW_REQUEST_FPS: u32 = 30;
#[cfg(not(coverage))]
const INLINE_PREVIEW_MAX_KBIT: u32 = 6_000;
#[cfg(not(coverage))]
const DEFAULT_EYE_SOURCE_WIDTH: i32 = 1920;
#[cfg(not(coverage))]
const DEFAULT_EYE_SOURCE_HEIGHT: i32 = 1080;
#[cfg(not(coverage))]
const DEFAULT_EYE_SOURCE_FPS: u32 = 60;
#[cfg(not(coverage))]
const DEFAULT_EYE_SOURCE_MAX_KBIT: u32 = 18_000;
#[cfg(not(coverage))]
const PREVIEW_IDLE_STATUS: &str = "Connect relay to preview.";
#[cfg(not(coverage))]
const TELEMETRY_WINDOW: Duration = Duration::from_secs(5);

#[cfg(not(coverage))]
pub struct LauncherPreview {
    server_addr: Arc<Mutex<String>>,
    log_sink: Arc<Mutex<Option<std::sync::mpsc::Sender<String>>>>,
    inline_feeds: Arc<Mutex<[PreviewFeed; 2]>>,
    window_feeds: Arc<Mutex<[PreviewFeed; 2]>>,
}

#[cfg(not(coverage))]
#[derive(Clone)]
pub struct PreviewBinding {
    enabled: Arc<AtomicBool>,
    alive: Arc<AtomicBool>,
    active_bindings: Arc<AtomicUsize>,
}

#[cfg(not(coverage))]
pub(crate) struct PreviewRecordingTap {
    rx: std::sync::mpsc::Receiver<PreviewFrameSnapshot>,
    active_bindings: Arc<AtomicUsize>,
}

#[cfg(not(coverage))]
impl PreviewRecordingTap {
    fn new(
        rx: std::sync::mpsc::Receiver<PreviewFrameSnapshot>,
        active_bindings: Arc<AtomicUsize>,
    ) -> Self {
        active_bindings.fetch_add(1, Ordering::AcqRel);
        Self {
            rx,
            active_bindings,
        }
    }

    pub(crate) fn recv_timeout(
        &self,
        timeout: Duration,
    ) -> Result<PreviewFrameSnapshot, std::sync::mpsc::RecvTimeoutError> {
        self.rx.recv_timeout(timeout)
    }
}

#[cfg(not(coverage))]
impl Drop for PreviewRecordingTap {
    fn drop(&mut self) {
        self.active_bindings.fetch_sub(1, Ordering::AcqRel);
    }
}

#[cfg(not(coverage))]
#[derive(Clone, Copy, Debug)]
pub enum PreviewSurface {
    Inline,
    Window,
}

#[cfg(not(coverage))]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct PreviewMetricsSnapshot {
    pub receive_fps: f32,
    pub present_fps: f32,
    pub server_fps: f32,
    pub server_process_cpu_pct: f32,
    pub stream_spread_ms: f32,
    pub packet_loss_pct: f32,
    pub dropped_frames: u64,
    pub queue_depth: u32,
    pub queue_depth_peak: u32,
    pub packet_gap_peak_ms: f32,
    pub present_gap_peak_ms: f32,
    pub server_source_gap_peak_ms: f32,
    pub server_send_gap_peak_ms: f32,
    pub server_queue_peak: u32,
    pub server_encoder_label: String,
    pub decoder_label: String,
    pub stream_caps_label: String,
    pub decoded_caps_label: String,
    pub rendered_caps_label: String,
}

#[cfg(not(coverage))]
#[derive(Clone, Copy, Debug)]
struct PreviewProfile {
    source_monitor_id: u32,
    display_width: i32,
    display_height: i32,
    requested_width: i32,
    requested_height: i32,
    requested_fps: u32,
    max_bitrate_kbit: u32,
}

#[cfg(not(coverage))]
impl PreviewSurface {
    fn profile(self) -> PreviewProfile {
        match self {
            Self::Inline => PreviewProfile {
                source_monitor_id: 0,
                display_width: preview_dimension("LESAVKA_PREVIEW_WIDTH", PREVIEW_WIDTH),
                display_height: preview_dimension("LESAVKA_PREVIEW_HEIGHT", PREVIEW_HEIGHT),
                requested_width: preview_dimension(
                    "LESAVKA_PREVIEW_REQUEST_WIDTH",
                    INLINE_PREVIEW_REQUEST_WIDTH,
                ),
                requested_height: preview_dimension(
                    "LESAVKA_PREVIEW_REQUEST_HEIGHT",
                    INLINE_PREVIEW_REQUEST_HEIGHT,
                ),
                requested_fps: preview_bitrate(
                    "LESAVKA_PREVIEW_REQUEST_FPS",
                    INLINE_PREVIEW_REQUEST_FPS,
                ),
                max_bitrate_kbit: preview_bitrate(
                    "LESAVKA_PREVIEW_MAX_KBIT",
                    INLINE_PREVIEW_MAX_KBIT,
                ),
            },
            Self::Window => PreviewProfile {
                source_monitor_id: 0,
                display_width: preview_dimension("LESAVKA_BREAKOUT_PREVIEW_WIDTH", 1280),
                display_height: preview_dimension("LESAVKA_BREAKOUT_PREVIEW_HEIGHT", 720),
                requested_width: preview_dimension(
                    "LESAVKA_BREAKOUT_REQUEST_WIDTH",
                    DEFAULT_EYE_SOURCE_WIDTH,
                ),
                requested_height: preview_dimension(
                    "LESAVKA_BREAKOUT_REQUEST_HEIGHT",
                    DEFAULT_EYE_SOURCE_HEIGHT,
                ),
                requested_fps: preview_bitrate(
                    "LESAVKA_BREAKOUT_REQUEST_FPS",
                    DEFAULT_EYE_SOURCE_FPS,
                ),
                max_bitrate_kbit: preview_bitrate(
                    "LESAVKA_BREAKOUT_PREVIEW_MAX_KBIT",
                    DEFAULT_EYE_SOURCE_MAX_KBIT,
                ),
            },
        }
    }
}
