use std::time::Duration;

pub struct RetryConfig {
    pub max_retries: u32,
    pub initial_delay_ms: u64,
    pub max_delay_ms: u64,
    pub backoff_factor: f64,
}

impl RetryConfig {
    pub fn new(max_retries: u32) -> Self {
        Self {
            max_retries,
            initial_delay_ms: 1000,
            max_delay_ms: 30000,
            backoff_factor: 2.0,
        }
    }

    pub fn with_initial_delay(mut self, ms: u64) -> Self {
        self.initial_delay_ms = ms;
        self
    }

    pub fn with_max_delay(mut self, ms: u64) -> Self {
        self.max_delay_ms = ms;
        self
    }

    pub fn with_backoff_factor(mut self, factor: f64) -> Self {
        self.backoff_factor = factor;
        self
    }

    pub fn calculate_delay(&self, attempt: u32) -> Duration {
        let delay =
            (self.initial_delay_ms as f64 * self.backoff_factor.powi(attempt as i32)) as u64;
        let delay = delay.min(self.max_delay_ms);
        Duration::from_millis(delay)
    }
}

impl Default for RetryConfig {
    fn default() -> Self {
        Self::new(5)
    }
}

pub enum RetryAction {
    Retry(Duration),
    Stop,
}

pub struct RetryManager {
    config: RetryConfig,
    attempt: u32,
}

impl RetryManager {
    pub fn new(config: RetryConfig) -> Self {
        Self { config, attempt: 0 }
    }

    pub fn next(&mut self) -> RetryAction {
        if self.attempt >= self.config.max_retries {
            return RetryAction::Stop;
        }
        let delay = self.config.calculate_delay(self.attempt);
        self.attempt += 1;
        RetryAction::Retry(delay)
    }

    pub fn attempt(&self) -> u32 {
        self.attempt
    }

    pub fn reset(&mut self) {
        self.attempt = 0;
    }

    pub fn should_retry(&self) -> bool {
        self.attempt < self.config.max_retries
    }
}
