pub const FACTORY_MJPEG_AUDIO_OFFSET_US: i64 = 0;
pub const FACTORY_MJPEG_VIDEO_OFFSET_1280X720_20_US: i64 = 162_659;
pub const FACTORY_MJPEG_VIDEO_OFFSET_1280X720_30_US: i64 = 135_090;
pub const FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_20_US: i64 = 160_045;
pub const FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_30_US: i64 = 127_952;
pub const FACTORY_MJPEG_OPUS_AUDIO_OFFSET_US: i64 = -34_199;
pub const FACTORY_MJPEG_OPUS_VIDEO_OFFSET_1280X720_20_US: i64 = 541_419;
pub const FACTORY_MJPEG_OPUS_VIDEO_OFFSET_1280X720_30_US: i64 = 513_850;
pub const FACTORY_MJPEG_OPUS_VIDEO_OFFSET_1920X1080_20_US: i64 = 538_805;
pub const FACTORY_MJPEG_OPUS_VIDEO_OFFSET_1920X1080_30_US: i64 = 506_712;
pub const FACTORY_MJPEG_AUDIO_MODE_OFFSETS_US: &str =
    "1280x720@20=0,1280x720@30=0,1920x1080@20=0,1920x1080@30=0";
pub const FACTORY_MJPEG_VIDEO_MODE_OFFSETS_US: &str =
    "1280x720@20=162659,1280x720@30=135090,1920x1080@20=160045,1920x1080@30=127952";
pub const FACTORY_MJPEG_OPUS_AUDIO_MODE_OFFSETS_US: &str =
    "1280x720@20=-34199,1280x720@30=-34199,1920x1080@20=-34199,1920x1080@30=-34199";
// 1280x720@30 was measured through the blind client->server->RCT Opus path.
// The sibling MJPEG modes carry the same Opus-vs-PCM video delta until the next
// full mode matrix replaces them with direct hardware measurements.
pub const FACTORY_MJPEG_OPUS_VIDEO_MODE_OFFSETS_US: &str =
    "1280x720@20=541419,1280x720@30=513850,1920x1080@20=538805,1920x1080@30=506712";
pub const FACTORY_HEVC_AUDIO_OFFSET_US: i64 = 0;
pub const FACTORY_HEVC_VIDEO_OFFSET_1280X720_20_US: i64 = 173_852;
pub const FACTORY_HEVC_VIDEO_OFFSET_1280X720_30_US: i64 = 110_000;
pub const FACTORY_HEVC_VIDEO_OFFSET_1920X1080_20_US: i64 = 160_045;
pub const FACTORY_HEVC_VIDEO_OFFSET_1920X1080_30_US: i64 = 127_952;
pub const FACTORY_HEVC_AUDIO_MODE_OFFSETS_US: &str =
    "1280x720@20=0,1280x720@30=0,1920x1080@20=0,1920x1080@30=0";
pub const FACTORY_HEVC_VIDEO_MODE_OFFSETS_US: &str =
    "1280x720@20=173852,1280x720@30=110000,1920x1080@20=160045,1920x1080@30=127952";
pub const FACTORY_MJPEG_PCM_AUDIO_MODE_OFFSETS_US: &str = FACTORY_MJPEG_AUDIO_MODE_OFFSETS_US;
pub const FACTORY_HEVC_PCM_AUDIO_MODE_OFFSETS_US: &str = FACTORY_HEVC_AUDIO_MODE_OFFSETS_US;
pub const FACTORY_HEVC_OPUS_AUDIO_MODE_OFFSETS_US: &str = FACTORY_HEVC_AUDIO_MODE_OFFSETS_US;
pub const FACTORY_MJPEG_PCM_VIDEO_MODE_OFFSETS_US: &str = FACTORY_MJPEG_VIDEO_MODE_OFFSETS_US;
pub const FACTORY_HEVC_PCM_VIDEO_MODE_OFFSETS_US: &str = FACTORY_HEVC_VIDEO_MODE_OFFSETS_US;
pub const FACTORY_HEVC_OPUS_VIDEO_MODE_OFFSETS_US: &str = FACTORY_HEVC_VIDEO_MODE_OFFSETS_US;
pub const FACTORY_MJPEG_PCM_AUDIO_OFFSET_US: i64 = FACTORY_MJPEG_AUDIO_OFFSET_US;
pub const FACTORY_HEVC_PCM_AUDIO_OFFSET_US: i64 = FACTORY_HEVC_AUDIO_OFFSET_US;
pub const FACTORY_HEVC_OPUS_AUDIO_OFFSET_US: i64 = FACTORY_HEVC_AUDIO_OFFSET_US;
pub const FACTORY_MJPEG_PCM_VIDEO_OFFSET_US: i64 = FACTORY_MJPEG_VIDEO_OFFSET_US;
pub const FACTORY_HEVC_PCM_VIDEO_OFFSET_US: i64 = FACTORY_HEVC_VIDEO_OFFSET_US;
pub const FACTORY_MJPEG_OPUS_VIDEO_OFFSET_US: i64 = FACTORY_MJPEG_OPUS_VIDEO_OFFSET_1280X720_30_US;
pub const FACTORY_HEVC_OPUS_VIDEO_OFFSET_US: i64 = FACTORY_HEVC_VIDEO_OFFSET_US;
// Direct UVC/UAC output-delay probes against the lab RC target showed a
// per-mode sync center for MJPEG/UVC video. This is output-path compensation,
// not a freshness buffer. The scalar fallback follows the default UVC mode.
pub const FACTORY_MJPEG_VIDEO_OFFSET_US: i64 = FACTORY_MJPEG_VIDEO_OFFSET_1280X720_30_US;
pub const FACTORY_HEVC_VIDEO_OFFSET_US: i64 = FACTORY_HEVC_VIDEO_OFFSET_1280X720_30_US;

const MJPEG_PROFILE: &str = "mjpeg";
const HEVC_PROFILE: &str = "hevc";
const PCM_AUDIO_PROFILE: &str = "pcm";
const OPUS_AUDIO_PROFILE: &str = "opus";

use super::mode_env::{env_i64, env_mode_offset_us};

/// Read a profile-specific playout offset with generic env fallback.
///
/// Inputs: normalized profile (`mjpeg` or `hevc`), media kind (`AUDIO` or
/// `VIDEO`), optional UVC mode, and stale-scalar predicate. Output: the selected
/// offset in microseconds. Why: HEVC decode adds a different server-to-RCT path,
/// so profile-specific maps must override legacy generic MJPEG-era knobs while
/// preserving compatibility for existing deployments.
pub(super) fn configured_profile_offset_us(
    profile: &str,
    media: &str,
    mode: Option<&str>,
    is_stale_scalar: impl Fn(i64) -> bool,
) -> Option<i64> {
    let camera_profile = camera_profile_from_calibration_profile(profile);
    let audio_profile = audio_profile_from_calibration_profile(profile);
    let combined_prefix = format!(
        "{}_{}",
        camera_profile.to_ascii_uppercase(),
        audio_profile.to_ascii_uppercase()
    );
    let camera_prefix = camera_profile.to_ascii_uppercase();
    let combined_mode_map_name =
        format!("LESAVKA_UPSTREAM_{combined_prefix}_{media}_PLAYOUT_MODE_OFFSETS_US");
    let combined_scalar_name =
        format!("LESAVKA_UPSTREAM_{combined_prefix}_{media}_PLAYOUT_OFFSET_US");
    let profile_mode_map_name =
        format!("LESAVKA_UPSTREAM_{camera_prefix}_{media}_PLAYOUT_MODE_OFFSETS_US");
    let profile_scalar_name = format!("LESAVKA_UPSTREAM_{camera_prefix}_{media}_PLAYOUT_OFFSET_US");
    let generic_mode_map_name = format!("LESAVKA_UPSTREAM_{media}_PLAYOUT_MODE_OFFSETS_US");
    let generic_scalar_name = format!("LESAVKA_UPSTREAM_{media}_PLAYOUT_OFFSET_US");
    let combined_offset = mode
        .and_then(|mode| env_mode_offset_us(&combined_mode_map_name, mode))
        .or_else(|| env_i64(&combined_scalar_name).filter(|offset| !is_stale_scalar(*offset)));
    if combined_offset.is_some() {
        return combined_offset;
    }
    let camera_offset = mode
        .and_then(|mode| env_mode_offset_us(&profile_mode_map_name, mode))
        .or_else(|| env_i64(&profile_scalar_name).filter(|offset| !is_stale_scalar(*offset)));
    if camera_offset.is_some() {
        return camera_offset;
    }

    // Generic playout variables predate ingress profiles and were written by
    // MJPEG/PCM installs. Do not let those stale maps silently override HEVC or
    // Opus factory offsets; new deployments can still opt in with profile knobs.
    if camera_profile != MJPEG_PROFILE || audio_profile != PCM_AUDIO_PROFILE {
        return None;
    }

    mode.and_then(|mode| env_mode_offset_us(&generic_mode_map_name, mode))
        .or_else(|| env_i64(&generic_scalar_name).filter(|offset| !is_stale_scalar(*offset)))
}

/// Resolve the active calibration profile from explicit and codec env hints.
///
/// Inputs: process environment. Output: normalized `camera+audio` profile
/// string. Why: persisted calibration must follow both the video ingress codec
/// and the microphone transport, because HEVC and Opus can each add timing cost
/// before media reaches the RCT.
pub(super) fn current_profile() -> String {
    let camera_profile = current_camera_profile();
    let audio_profile = current_audio_profile();
    format!("{camera_profile}+{audio_profile}")
}

/// Normalize a persisted calibration profile into the current `camera+audio`
/// shape. Why: pre-Opus snapshots stored only `mjpeg` or `hevc`, and those
/// should compare as the PCM profiles they always represented.
pub(super) fn normalize_calibration_profile(value: &str) -> String {
    let camera_profile = camera_profile_from_calibration_profile(value);
    let audio_profile = audio_profile_from_calibration_profile(value);
    format!("{camera_profile}+{audio_profile}")
}

/// Resolve the camera side of the active calibration profile.
///
/// Inputs: process environment. Output: `mjpeg` or `hevc`. Why: the camera
/// ingress codec and microphone transport can now fork independently while
/// still sharing the old profile spellings for compatibility.
fn current_camera_profile() -> String {
    std::env::var("LESAVKA_CALIBRATION_PROFILE")
        .ok()
        .and_then(|value| normalize_camera_profile(&value))
        .or_else(|| {
            std::env::var("LESAVKA_UPLINK_CAMERA_CODEC")
                .ok()
                .and_then(|value| normalize_camera_profile(&value))
        })
        .or_else(|| {
            std::env::var("LESAVKA_CAM_CODEC")
                .ok()
                .and_then(|value| normalize_camera_profile(&value))
        })
        .unwrap_or_else(|| MJPEG_PROFILE.to_string())
}

/// Resolve the microphone side of the active calibration profile.
///
/// Inputs: process environment. Output: `pcm` or `opus`. Why: Opus adds codec
/// delay before UAC, so it needs its own calibration namespace without erasing
/// the already measured PCM factory values.
fn current_audio_profile() -> String {
    std::env::var("LESAVKA_CALIBRATION_PROFILE")
        .ok()
        .and_then(|value| normalize_audio_profile(&value))
        .or_else(|| {
            std::env::var("LESAVKA_CALIBRATION_AUDIO_CODEC")
                .ok()
                .and_then(|value| normalize_audio_profile(&value))
        })
        .or_else(|| {
            std::env::var("LESAVKA_UPLINK_AUDIO_CODEC")
                .ok()
                .and_then(|value| normalize_audio_profile(&value))
        })
        .or_else(|| {
            std::env::var("LESAVKA_AUDIO_CODEC")
                .ok()
                .and_then(|value| normalize_audio_profile(&value))
        })
        .unwrap_or_else(|| PCM_AUDIO_PROFILE.to_string())
}

/// Normalize user-facing codec spellings into calibration profile names.
///
/// Inputs: a codec/profile string. Output: `Some(profile)` for known camera
/// ingress profiles. Why: operators and install scripts use `h265`, `h.265`,
/// `mjpg`, and `jpeg` spellings interchangeably, but the stored calibration
/// profile must remain stable.
fn normalize_camera_profile(value: &str) -> Option<String> {
    let normalized = value.trim().to_ascii_lowercase();
    for token in normalized.split(|ch: char| !ch.is_ascii_alphanumeric() && ch != '.') {
        match token {
            "hevc" | "h265" | "h.265" => return Some(HEVC_PROFILE.to_string()),
            "mjpeg" | "mjpg" | "jpeg" => return Some(MJPEG_PROFILE.to_string()),
            _ => {}
        }
    }
    match normalized.as_str() {
        "hevc" | "h265" | "h.265" => Some(HEVC_PROFILE.to_string()),
        "mjpeg" | "mjpg" | "jpeg" => Some(MJPEG_PROFILE.to_string()),
        _ => None,
    }
}

/// Normalize user-facing microphone transport spellings into calibration names.
///
/// Inputs: a codec/profile string. Output: `Some(profile)` for known upstream
/// audio transports. Why: Opus can be selected in the client UI while PCM
/// remains the known-good fallback, and each side needs a stable profile key.
fn normalize_audio_profile(value: &str) -> Option<String> {
    let normalized = value.trim().to_ascii_lowercase();
    for token in normalized.split(|ch: char| !ch.is_ascii_alphanumeric()) {
        match token {
            "opus" | "compressed" | "voice" => return Some(OPUS_AUDIO_PROFILE.to_string()),
            "pcm" | "raw" | "s16le" | "uncompressed" => {
                return Some(PCM_AUDIO_PROFILE.to_string());
            }
            _ => {}
        }
    }
    match normalized.as_str() {
        "opus" | "compressed" | "voice" => Some(OPUS_AUDIO_PROFILE.to_string()),
        "pcm" | "raw" | "s16le" | "uncompressed" => Some(PCM_AUDIO_PROFILE.to_string()),
        _ => None,
    }
}

fn camera_profile_from_calibration_profile(profile: &str) -> String {
    normalize_camera_profile(profile).unwrap_or_else(|| MJPEG_PROFILE.to_string())
}

fn audio_profile_from_calibration_profile(profile: &str) -> String {
    normalize_audio_profile(profile).unwrap_or_else(|| PCM_AUDIO_PROFILE.to_string())
}

/// Return the factory per-mode audio offset map for a calibration profile.
///
/// Inputs: normalized profile. Output: comma-separated mode map. Why: keeping
/// audio maps profile-aware lets future decode paths diverge without changing
/// the persisted calibration schema again.
pub(super) fn factory_audio_mode_offsets_us(profile: &str) -> &'static str {
    let camera = camera_profile_from_calibration_profile(profile);
    let audio = audio_profile_from_calibration_profile(profile);
    match (camera.as_str(), audio.as_str()) {
        (HEVC_PROFILE, OPUS_AUDIO_PROFILE) => FACTORY_HEVC_OPUS_AUDIO_MODE_OFFSETS_US,
        (HEVC_PROFILE, _) => FACTORY_HEVC_PCM_AUDIO_MODE_OFFSETS_US,
        (MJPEG_PROFILE, OPUS_AUDIO_PROFILE) => FACTORY_MJPEG_OPUS_AUDIO_MODE_OFFSETS_US,
        _ => FACTORY_MJPEG_PCM_AUDIO_MODE_OFFSETS_US,
    }
}

/// Return the factory per-mode video offset map for a calibration profile.
///
/// Inputs: normalized profile. Output: comma-separated mode map. Why: video is
/// where HEVC decode and MJPEG re-emission can shift sync, so each ingress
/// profile needs its own baked server-to-RCT center points.
pub(super) fn factory_video_mode_offsets_us(profile: &str) -> &'static str {
    let camera = camera_profile_from_calibration_profile(profile);
    let audio = audio_profile_from_calibration_profile(profile);
    match (camera.as_str(), audio.as_str()) {
        (HEVC_PROFILE, OPUS_AUDIO_PROFILE) => FACTORY_HEVC_OPUS_VIDEO_MODE_OFFSETS_US,
        (HEVC_PROFILE, _) => FACTORY_HEVC_PCM_VIDEO_MODE_OFFSETS_US,
        (MJPEG_PROFILE, OPUS_AUDIO_PROFILE) => FACTORY_MJPEG_OPUS_VIDEO_MODE_OFFSETS_US,
        _ => FACTORY_MJPEG_PCM_VIDEO_MODE_OFFSETS_US,
    }
}

/// Return the scalar factory audio fallback for a profile.
///
/// Inputs: normalized profile. Output: audio offset in microseconds. Why:
/// callers still need a safe default when no UVC mode is visible during early
/// process startup.
pub(super) fn factory_audio_scalar_offset_us(profile: &str) -> i64 {
    let camera = camera_profile_from_calibration_profile(profile);
    let audio = audio_profile_from_calibration_profile(profile);
    match (camera.as_str(), audio.as_str()) {
        (HEVC_PROFILE, OPUS_AUDIO_PROFILE) => FACTORY_HEVC_OPUS_AUDIO_OFFSET_US,
        (HEVC_PROFILE, _) => FACTORY_HEVC_PCM_AUDIO_OFFSET_US,
        (MJPEG_PROFILE, OPUS_AUDIO_PROFILE) => FACTORY_MJPEG_OPUS_AUDIO_OFFSET_US,
        _ => FACTORY_MJPEG_PCM_AUDIO_OFFSET_US,
    }
}

/// Return the scalar factory video fallback for a profile.
///
/// Inputs: normalized profile. Output: video offset in microseconds. Why:
/// profile-specific scalar defaults keep unknown-mode startup aligned with the
/// most common calibrated profile instead of silently borrowing stale MJPEG
/// values for HEVC.
pub(super) fn factory_video_scalar_offset_us(profile: &str) -> i64 {
    let camera = camera_profile_from_calibration_profile(profile);
    let audio = audio_profile_from_calibration_profile(profile);
    match (camera.as_str(), audio.as_str()) {
        (HEVC_PROFILE, OPUS_AUDIO_PROFILE) => FACTORY_HEVC_OPUS_VIDEO_OFFSET_US,
        (HEVC_PROFILE, _) => FACTORY_HEVC_PCM_VIDEO_OFFSET_US,
        (MJPEG_PROFILE, OPUS_AUDIO_PROFILE) => FACTORY_MJPEG_OPUS_VIDEO_OFFSET_US,
        _ => FACTORY_MJPEG_PCM_VIDEO_OFFSET_US,
    }
}
