#![forbid(unsafe_code)]

use std::time::Duration;

use crate::handshake::PeerCaps;
use crate::input::camera::{CameraCodec, CameraConfig};

pub const DEFAULT_SERVER_ADDR: &str = "https://38.28.125.112:50051";

#[must_use]
/// Resolve the server address from `--server`, positional args, env, or default.
pub fn resolve_server_addr(args: &[String], env_addr: Option<&str>) -> String {
    args.windows(2)
        .find_map(|pair| {
            (pair[0] == "--server" && !pair[1].starts_with("--")).then(|| pair[1].clone())
        })
        .or_else(|| args.iter().find(|arg| !arg.starts_with("--")).cloned())
        .or_else(|| env_addr.map(ToOwned::to_owned))
        .unwrap_or_else(|| DEFAULT_SERVER_ADDR.to_string())
}

#[must_use]
/// Build local camera capture settings from negotiated peer capabilities.
pub fn camera_config_from_caps(caps: &PeerCaps) -> Option<CameraConfig> {
    let negotiated_codec = parse_camera_codec(caps.camera_codec.as_deref()?)?;
    let codec = if env_flag_enabled("LESAVKA_CAM_CODEC_FORCE") {
        std::env::var("LESAVKA_CAM_CODEC")
            .ok()
            .and_then(|value| parse_camera_codec(&value))
            .unwrap_or(negotiated_codec)
    } else {
        negotiated_codec
    };
    Some(CameraConfig {
        codec,
        width: caps.camera_width?,
        height: caps.camera_height?,
        fps: caps.camera_fps?,
    })
}

#[must_use]
/// Clamp queue depth so stale video cannot backlog behind live input.
pub fn sanitize_video_queue(queue: Option<usize>) -> usize {
    queue.unwrap_or(8).max(4)
}

#[must_use]
/// Exponential reconnect delay capped at 30 seconds.
pub fn next_delay(current: Duration) -> Duration {
    match current.as_secs() {
        1..=15 => current * 2,
        _ => Duration::from_secs(30),
    }
}

/// Parse the camera codec string used by launcher and server negotiation.
///
/// Inputs: operator/env codec text. Output: the supported transport codec when
/// recognized. Why: the client must not silently fall back to a differently
/// calibrated upstream path when the UI asks for HEVC or MJPEG.
pub(crate) fn parse_camera_codec(raw: &str) -> Option<CameraCodec> {
    match raw.trim().to_ascii_lowercase().as_str() {
        "mjpeg" | "mjpg" | "jpeg" => Some(CameraCodec::Mjpeg),
        "h264" => Some(CameraCodec::H264),
        "hevc" | "h265" | "h.265" => Some(CameraCodec::Hevc),
        _ => None,
    }
}

fn env_flag_enabled(name: &str) -> bool {
    std::env::var(name)
        .ok()
        .map(|value| value.trim().to_ascii_lowercase())
        .is_some_and(|value| matches!(value.as_str(), "1" | "true" | "yes" | "on"))
}
