//! Polling configuration for credential requests.

#[cfg(feature = "json-schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::error::LastIDError;

/// Configuration for credential request polling behavior.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[serde(default)]
pub struct PollingConfig {
    /// Initial polling interval (milliseconds)
    #[serde(default = "default_initial_interval")]
    pub initial_interval_ms: u64,

    /// Maximum polling interval (milliseconds)
    #[serde(default = "default_max_interval")]
    pub max_interval_ms: u64,

    /// Backoff multiplier (1.0 = no backoff)
    #[serde(default = "default_backoff")]
    pub backoff_multiplier: f64,

    /// Maximum polling duration (seconds)
    #[serde(default = "default_max_duration")]
    pub max_duration_seconds: u64,
}

const fn default_initial_interval() -> u64 {
    2000
}
const fn default_max_interval() -> u64 {
    30000
}
const fn default_backoff() -> f64 {
    1.5
}
const fn default_max_duration() -> u64 {
    300
}

impl Default for PollingConfig {
    fn default() -> Self {
        Self {
            initial_interval_ms: default_initial_interval(),
            max_interval_ms: default_max_interval(),
            backoff_multiplier: default_backoff(),
            max_duration_seconds: default_max_duration(),
        }
    }
}

impl PollingConfig {
    /// Validate the polling configuration.
    pub fn validate(&self) -> Result<(), LastIDError> {
        if self.initial_interval_ms == 0 {
            return Err(LastIDError::config("initial_interval_ms must be > 0"));
        }

        if self.max_interval_ms < self.initial_interval_ms {
            return Err(LastIDError::config(
                "max_interval_ms must be >= initial_interval_ms",
            ));
        }

        if self.backoff_multiplier < 1.0 {
            return Err(LastIDError::config("backoff_multiplier must be >= 1.0"));
        }

        if self.max_duration_seconds == 0 {
            return Err(LastIDError::config("max_duration_seconds must be > 0"));
        }

        Ok(())
    }

    /// Calculate the next polling interval with backoff.
    #[must_use]
    #[allow(
        clippy::cast_precision_loss,
        clippy::cast_possible_truncation,
        clippy::cast_sign_loss
    )]
    pub fn next_interval(&self, current_ms: u64) -> u64 {
        // Precision loss is acceptable for backoff calculations.
        // Truncation is safe because multiplier >= 1.0 and we cap at max_interval.
        // Sign loss impossible because both values are positive.
        let next = (current_ms as f64 * self.backoff_multiplier) as u64;
        next.min(self.max_interval_ms)
    }

    /// Merge another configuration into this one.
    pub fn merge(&mut self, other: &Self) {
        if other.initial_interval_ms != default_initial_interval() {
            self.initial_interval_ms = other.initial_interval_ms;
        }
        if other.max_interval_ms != default_max_interval() {
            self.max_interval_ms = other.max_interval_ms;
        }
        if (other.backoff_multiplier - default_backoff()).abs() > f64::EPSILON {
            self.backoff_multiplier = other.backoff_multiplier;
        }
        if other.max_duration_seconds != default_max_duration() {
            self.max_duration_seconds = other.max_duration_seconds;
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_default_values() {
        let config = PollingConfig::default();
        assert_eq!(config.initial_interval_ms, 2000);
        assert_eq!(config.max_interval_ms, 30000);
        assert!((config.backoff_multiplier - 1.5).abs() < f64::EPSILON);
        assert_eq!(config.max_duration_seconds, 300);
    }

    #[test]
    fn test_next_interval() {
        let config = PollingConfig::default();

        // First backoff: 2000 * 1.5 = 3000
        assert_eq!(config.next_interval(2000), 3000);

        // Second backoff: 3000 * 1.5 = 4500
        assert_eq!(config.next_interval(3000), 4500);

        // Capped at max: 30000 * 1.5 = 45000 -> capped to 30000
        assert_eq!(config.next_interval(30000), 30000);
    }

    #[test]
    fn test_validate_invalid() {
        let mut config = PollingConfig::default();

        config.initial_interval_ms = 0;
        assert!(config.validate().is_err());

        config.initial_interval_ms = 2000;
        config.max_interval_ms = 1000; // Less than initial
        assert!(config.validate().is_err());

        config.max_interval_ms = 30000;
        config.backoff_multiplier = 0.5; // Less than 1.0
        assert!(config.validate().is_err());

        config.backoff_multiplier = 1.5;
        config.max_duration_seconds = 0;
        assert!(config.validate().is_err());
    }

    #[test]
    fn test_validate_valid() {
        let config = PollingConfig::default();
        assert!(config.validate().is_ok());
    }
}
