//! Rollout monitor — checks crash rate, advances or halts rollout.

use crate::{config::RolloutConfig, PublishError, PublishResult};

/// Current rollout state.
#[derive(Debug, Clone)]
pub struct RolloutState {
    pub track: String,
    pub current_percent: u8,
    pub crash_rate: f64,
    pub is_halted: bool,
    pub hours_elapsed: u64,
}

impl RolloutState {
    pub fn new(track: impl Into<String>, initial_percent: u8) -> Self {
        Self {
            track: track.into(),
            current_percent: initial_percent,
            crash_rate: 0.0,
            is_halted: false,
            hours_elapsed: 0,
        }
    }
}

/// Monitors a staged rollout and decides whether to advance or halt.
pub struct RolloutMonitor {
    pub config: RolloutConfig,
}

impl RolloutMonitor {
    pub fn new(config: RolloutConfig) -> Self {
        Self { config }
    }

    /// Evaluate whether to advance, halt, or maintain the current rollout.
    pub fn evaluate(&self, state: &RolloutState) -> RolloutDecision {
        if state.is_halted {
            return RolloutDecision::Halted {
                reason: "rollout was previously halted".into(),
            };
        }

        // Check crash rate
        if state.crash_rate > self.config.max_crash_rate {
            return RolloutDecision::Halt {
                reason: format!(
                    "crash rate {:.2}% exceeds threshold {:.2}%",
                    state.crash_rate * 100.0,
                    self.config.max_crash_rate * 100.0
                ),
            };
        }

        // Check if we should advance
        if self.config.auto_advance
            && state.hours_elapsed >= self.config.advance_after_hours
            && state.current_percent < 100
        {
            let next_percent = advance_percent(state.current_percent);
            return RolloutDecision::Advance { to_percent: next_percent };
        }

        RolloutDecision::Maintain
    }

    /// Apply a rollout decision — returns the new rollout state.
    pub fn apply(
        &self,
        mut state: RolloutState,
        decision: &RolloutDecision,
    ) -> PublishResult<RolloutState> {
        match decision {
            RolloutDecision::Advance { to_percent } => {
                state.current_percent = *to_percent;
                state.hours_elapsed = 0;
            }
            RolloutDecision::Halt { reason } => {
                state.is_halted = true;
                let _ = reason;
            }
            RolloutDecision::Halted { .. } => {
                return Err(PublishError::Config(
                    "cannot apply decision to halted rollout".into(),
                ));
            }
            RolloutDecision::Maintain => {}
        }
        Ok(state)
    }

    /// Run a complete rollout cycle: fetch metrics, evaluate, apply.
    ///
    /// In production: fetch crash rate from Firebase Crashlytics or App Store
    /// Connect Analytics API.
    pub fn tick(&self, mut state: RolloutState) -> PublishResult<(RolloutState, RolloutDecision)> {
        // TODO: fetch real crash rate from analytics API
        // let crash_rate = analytics_client.crash_rate(&state.track, &config.build).await?;
        // state.crash_rate = crash_rate;
        state.hours_elapsed += 1;

        let decision = self.evaluate(&state);
        let new_state = self.apply(state, &decision)?;
        Ok((new_state, decision))
    }
}

/// The outcome of a rollout evaluation.
#[derive(Debug, Clone)]
pub enum RolloutDecision {
    /// Advance to a higher rollout percentage.
    Advance { to_percent: u8 },
    /// Halt the rollout due to high crash rate.
    Halt { reason: String },
    /// Rollout was already halted.
    Halted { reason: String },
    /// No change needed.
    Maintain,
}

/// Determine the next rollout percentage.
/// Uses the standard staged-rollout progression: 10 → 25 → 50 → 100.
fn advance_percent(current: u8) -> u8 {
    match current {
        0..=9 => 10,
        10..=24 => 25,
        25..=49 => 50,
        _ => 100,
    }
}
