use lesavka_common::lesavka::CapturePowerState;

#[cfg(not(coverage))]
use {
    anyhow::{Context, Result, anyhow},
    std::process::Command,
    std::sync::{
        Arc,
        atomic::{AtomicBool, Ordering},
    },
    tokio::{
        sync::Mutex,
        time::{Duration, Instant},
    },
    tracing::{info, warn},
};

#[cfg(not(coverage))]
#[derive(Debug, Default)]
struct CapturePowerInner {
    preview_leases: u32,
    session_leases: u32,
    manual_override: Option<bool>,
    session_grace_deadline: Option<Instant>,
    sync_generation: u64,
}

#[cfg(not(coverage))]
#[derive(Debug, Clone)]
pub struct CapturePowerManager {
    unit: Arc<str>,
    session_grace: Duration,
    inner: Arc<Mutex<CapturePowerInner>>,
}

#[cfg(not(coverage))]
#[derive(Clone)]
pub struct CapturePowerLease {
    manager: CapturePowerManager,
    kind: LeaseKind,
    released: Arc<AtomicBool>,
}

#[cfg(not(coverage))]
#[derive(Clone, Copy, Debug)]
enum LeaseKind {
    Preview,
    Session,
}

#[cfg(not(coverage))]
#[derive(Debug)]
struct UnitSnapshot {
    available: bool,
    enabled: bool,
    detail: String,
}

#[cfg(not(coverage))]
impl LeaseKind {
    fn as_str(self) -> &'static str {
        match self {
            Self::Preview => "preview",
            Self::Session => "session",
        }
    }
}

#[cfg(not(coverage))]
impl Default for CapturePowerManager {
    fn default() -> Self {
        Self::new()
    }
}

impl CapturePowerManager {
    pub fn new() -> Self {
        let unit = std::env::var("LESAVKA_CAPTURE_POWER_UNIT")
            .ok()
            .filter(|value| !value.trim().is_empty())
            .unwrap_or_else(|| "relay.service".to_string());
        Self {
            unit: Arc::<str>::from(unit),
            session_grace: capture_power_session_grace_from_env(),
            inner: Arc::new(Mutex::new(CapturePowerInner::default())),
        }
    }

    pub async fn acquire(&self) -> CapturePowerLease {
        self.acquire_kind(LeaseKind::Preview).await
    }

    pub async fn acquire_session(&self) -> CapturePowerLease {
        self.acquire_kind(LeaseKind::Session).await
    }

    async fn acquire_kind(&self, kind: LeaseKind) -> CapturePowerLease {
        let (desired, unit, leases, manual_override, grace_remaining) = {
            let mut inner = self.inner.lock().await;
            match kind {
                LeaseKind::Preview => {
                    inner.preview_leases = inner.preview_leases.saturating_add(1);
                }
                LeaseKind::Session => {
                    inner.session_leases = inner.session_leases.saturating_add(1);
                    if inner.session_grace_deadline.take().is_some() {
                        inner.sync_generation = inner.sync_generation.saturating_add(1);
                    }
                }
            }
            let (desired, grace_remaining) = desired_state_and_grace(&inner, Instant::now());
            (
                desired,
                self.unit.to_string(),
                active_leases(&inner),
                inner.manual_override,
                grace_remaining,
            )
        };

        if let Err(err) = sync_unit_state(unit.as_str(), desired).await {
            warn!(
                unit = %unit,
                kind = kind.as_str(),
                leases,
                desired,
                ?manual_override,
                ?grace_remaining,
                ?err,
                "capture power sync failed on acquire"
            );
        }

        CapturePowerLease {
            manager: self.clone(),
            kind,
            released: Arc::new(AtomicBool::new(false)),
        }
    }

    pub async fn set_manual(&self, enabled: bool) -> Result<CapturePowerState> {
        let unit = self.unit.to_string();
        {
            let mut inner = self.inner.lock().await;
            inner.manual_override = Some(enabled);
            inner.session_grace_deadline = None;
            inner.sync_generation = inner.sync_generation.saturating_add(1);
        }

        sync_unit_state(unit.as_str(), enabled).await?;
        self.snapshot().await
    }

    pub async fn set_auto(&self) -> Result<CapturePowerState> {
        let unit = self.unit.to_string();
        let desired = {
            let mut inner = self.inner.lock().await;
            inner.manual_override = None;
            desired_state_and_grace(&inner, Instant::now()).0
        };

        sync_unit_state(unit.as_str(), desired).await?;
        self.snapshot().await
    }

    pub async fn snapshot(&self) -> Result<CapturePowerState> {
        let (active_leases, manual_override, grace_remaining) = {
            let inner = self.inner.lock().await;
            (
                active_leases(&inner),
                inner.manual_override,
                desired_state_and_grace(&inner, Instant::now()).1,
            )
        };
        let unit = self.unit.to_string();
        let snapshot = inspect_unit(unit.as_str()).await?;
        let mut detail = snapshot.detail;
        if let Some(grace_remaining) = grace_remaining {
            detail = format!(
                "{detail} • disconnect grace {}s",
                grace_remaining.as_secs().max(1)
            );
        }
        Ok(CapturePowerState {
            available: snapshot.available,
            enabled: snapshot.enabled,
            unit,
            detail,
            active_leases,
            mode: match manual_override {
                Some(true) => "forced-on".to_string(),
                Some(false) => "forced-off".to_string(),
                None => "auto".to_string(),
            },
            detected_devices: 0,
        })
    }

    async fn release_one(&self, kind: LeaseKind) {
        let (desired, unit, leases, manual_override, grace_remaining, grace_sync) = {
            let mut inner = self.inner.lock().await;
            match kind {
                LeaseKind::Preview => {
                    inner.preview_leases = inner.preview_leases.saturating_sub(1);
                }
                LeaseKind::Session => {
                    inner.session_leases = inner.session_leases.saturating_sub(1);
                    if inner.session_leases == 0 {
                        let deadline = Instant::now() + self.session_grace;
                        inner.session_grace_deadline = Some(deadline);
                        inner.sync_generation = inner.sync_generation.saturating_add(1);
                    }
                }
            }
            let grace_sync = match kind {
                LeaseKind::Session if inner.session_leases == 0 => inner
                    .session_grace_deadline
                    .map(|deadline| (inner.sync_generation, deadline)),
                _ => None,
            };
            let (desired, grace_remaining) = desired_state_and_grace(&inner, Instant::now());
            (
                desired,
                self.unit.to_string(),
                active_leases(&inner),
                inner.manual_override,
                grace_remaining,
                grace_sync,
            )
        };

        if let Err(err) = sync_unit_state(unit.as_str(), desired).await {
            warn!(
                unit = %unit,
                kind = kind.as_str(),
                leases,
                desired,
                ?manual_override,
                ?grace_remaining,
                ?err,
                "capture power sync failed on release"
            );
        } else {
            info!(
                unit = %unit,
                kind = kind.as_str(),
                leases,
                desired,
                ?manual_override,
                ?grace_remaining,
                "capture power synced"
            );
        }

        if let Some((generation, deadline)) = grace_sync {
            self.schedule_grace_sync(generation, deadline);
        }
    }

    fn schedule_grace_sync(&self, generation: u64, deadline: Instant) {
        let manager = self.clone();
        tokio::spawn(async move {
            tokio::time::sleep_until(deadline).await;
            let (desired, unit, leases, manual_override, grace_remaining, current_generation) = {
                let inner = manager.inner.lock().await;
                let (desired, grace_remaining) = desired_state_and_grace(&inner, Instant::now());
                (
                    desired,
                    manager.unit.to_string(),
                    active_leases(&inner),
                    inner.manual_override,
                    grace_remaining,
                    inner.sync_generation,
                )
            };
            if current_generation != generation {
                return;
            }
            if let Err(err) = sync_unit_state(unit.as_str(), desired).await {
                warn!(
                    unit = %unit,
                    generation,
                    leases,
                    desired,
                    ?manual_override,
                    ?grace_remaining,
                    ?err,
                    "capture power sync failed after grace"
                );
            } else {
                info!(
                    unit = %unit,
                    generation,
                    leases,
                    desired,
                    ?manual_override,
                    ?grace_remaining,
                    "capture power synced after grace"
                );
            }
        });
    }
}

#[cfg(not(coverage))]
impl Drop for CapturePowerLease {
    fn drop(&mut self) {
        if self.released.swap(true, Ordering::AcqRel) {
            return;
        }
        let manager = self.manager.clone();
        let kind = self.kind;
        tokio::spawn(async move {
            manager.release_one(kind).await;
        });
    }
}
