use std::sync::{Mutex, OnceLock};
use std::time::{Duration, Instant};

fn capture_clock_origin() -> &'static Instant {
    static ORIGIN: OnceLock<Instant> = OnceLock::new();
    ORIGIN.get_or_init(Instant::now)
}

pub fn capture_pts_us() -> u64 {
    capture_clock_origin().elapsed().as_micros() as u64
}

pub fn upstream_timing_trace_enabled() -> bool {
    std::env::var("LESAVKA_UPSTREAM_TIMING_TRACE")
        .ok()
        .map(|value| {
            let trimmed = value.trim();
            !(trimmed.eq_ignore_ascii_case("0")
                || trimmed.eq_ignore_ascii_case("false")
                || trimmed.eq_ignore_ascii_case("no")
                || trimmed.eq_ignore_ascii_case("off"))
        })
        .unwrap_or(false)
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct RebasedSourcePts {
    pub packet_pts_us: u64,
    pub capture_now_us: u64,
    pub source_pts_us: Option<u64>,
    pub source_base_us: Option<u64>,
    pub capture_base_us: Option<u64>,
    pub used_source_pts: bool,
}

#[derive(Debug, Default)]
struct SourcePtsRebaserState {
    source_base_us: Option<u64>,
    capture_base_us: Option<u64>,
    last_packet_pts_us: Option<u64>,
}

#[derive(Debug, Default)]
pub struct SourcePtsRebaser {
    state: Mutex<SourcePtsRebaserState>,
}

impl SourcePtsRebaser {
    pub fn rebase_or_now(&self, source_pts_us: Option<u64>, min_step_us: u64) -> RebasedSourcePts {
        let capture_now_us = capture_pts_us();
        let mut state = self
            .state
            .lock()
            .expect("source pts rebaser mutex poisoned");
        let mut packet_pts_us = capture_now_us;
        let mut used_source_pts = false;

        if let Some(source_pts_us) = source_pts_us {
            let source_base_us = *state.source_base_us.get_or_insert(source_pts_us);
            let capture_base_us = *state.capture_base_us.get_or_insert(capture_now_us);
            packet_pts_us =
                capture_base_us.saturating_add(source_pts_us.saturating_sub(source_base_us));
            used_source_pts = true;
        }

        if let Some(last_packet_pts_us) = state.last_packet_pts_us
            && packet_pts_us <= last_packet_pts_us
        {
            packet_pts_us = last_packet_pts_us.saturating_add(min_step_us.max(1));
        }

        state.last_packet_pts_us = Some(packet_pts_us);
        RebasedSourcePts {
            packet_pts_us,
            capture_now_us,
            source_pts_us,
            source_base_us: state.source_base_us,
            capture_base_us: state.capture_base_us,
            used_source_pts,
        }
    }
}
