use std::task::Poll;
use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
use thiserror::Error;
use tower::Service;

use crate::models::generation::{ClipStatus, PollingStatus};
use crate::services::generation::{GenerationError, PollGenerationRequest};

/// Configuration for polling middleware
#[derive(Debug, Clone)]
pub struct PollingConfig {
    /// Initial backoff duration in milliseconds
    pub initial_backoff_ms: u64,
    /// Backoff multiplier (e.g., 1.5 for exponential backoff)
    pub backoff_multiplier: f64,
    /// Maximum backoff duration in milliseconds
    pub max_backoff_ms: u64,
    /// Maximum number of retry attempts
    pub max_retries: u32,
    /// Jitter percentage (0-100), applied as ±jitter% to backoff
    pub jitter_percent: u8,
}

impl Default for PollingConfig {
    fn default() -> Self {
        Self {
            initial_backoff_ms: 1000,      // Start at 1s
            backoff_multiplier: 1.5,       // Multiply by 1.5 each time
            max_backoff_ms: 30000,         // Cap at 30s
            max_retries: 60,               // 60 retries = ~5 min total
            jitter_percent: 10,            // ±10% jitter
        }
    }
}

impl PollingConfig {
    /// Create a config with custom values
    pub fn new(
        initial_backoff_ms: u64,
        backoff_multiplier: f64,
        max_backoff_ms: u64,
        max_retries: u32,
        jitter_percent: u8,
    ) -> Self {
        Self {
            initial_backoff_ms,
            backoff_multiplier,
            max_backoff_ms,
            max_retries,
            jitter_percent,
        }
    }

    /// Aggressive config for quick testing (shorter timeouts)
    pub fn aggressive() -> Self {
        Self {
            initial_backoff_ms: 500,
            backoff_multiplier: 1.5,
            max_backoff_ms: 5000,
            max_retries: 30,
            jitter_percent: 10,
        }
    }
}

#[derive(Debug, Error)]
pub enum PollingError {
    #[error("polling error: {0}")]
    GenerationError(#[from] GenerationError),
    #[error("max retries exceeded")]
    MaxRetriesExceeded,
    #[error("generation error: {0}")]
    GenerationFailed(String),
}

/// Calculate next backoff duration with jitter
/// Uses a simple deterministic jitter based on system time for unpredictability
fn calculate_backoff(
    current_backoff_ms: u64,
    config: &PollingConfig,
) -> u64 {
    // Calculate next backoff with multiplier
    let next_backoff = (current_backoff_ms as f64 * config.backoff_multiplier) as u64;
    let capped_backoff = next_backoff.min(config.max_backoff_ms);

    // Apply jitter: ±jitter%
    if config.jitter_percent == 0 {
        capped_backoff
    } else {
        let jitter_range = (capped_backoff as f64 * config.jitter_percent as f64) / 100.0;
        // Simple jitter: use nanoseconds from system time as pseudo-random
        let nanos = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .subsec_nanos();
        let jitter = ((nanos as f64 / 1_000_000_000.0) - 0.5) * 2.0 * jitter_range;
        ((capped_backoff as f64 + jitter).max(config.initial_backoff_ms as f64)) as u64
    }
}

/// Tower middleware for automatic polling with exponential backoff
/// Wraps a PollGeneration service and automatically retries until completion or error
pub struct PollingMiddleware<S> {
    inner: S,
    config: PollingConfig,
}

impl<S> PollingMiddleware<S> {
    pub fn new(inner: S, config: PollingConfig) -> Self {
        Self { inner, config }
    }

    pub fn with_default_config(inner: S) -> Self {
        Self::new(inner, PollingConfig::default())
    }

    pub fn with_aggressive_config(inner: S) -> Self {
        Self::new(inner, PollingConfig::aggressive())
    }
}

impl<S> Clone for PollingMiddleware<S>
where
    S: Clone,
{
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
            config: self.config.clone(),
        }
    }
}

/// Request for auto-polling generation (wait until complete or error)
#[derive(Clone, Debug)]
pub struct AutoPollGenerationRequest {
    pub base_url: String,
    pub clip_id: String,
    pub api_key: String,
}

impl AutoPollGenerationRequest {
    pub fn new(
        base_url: impl Into<String>,
        clip_id: impl Into<String>,
        api_key: impl Into<String>,
    ) -> Self {
        Self {
            base_url: base_url.into(),
            clip_id: clip_id.into(),
            api_key: api_key.into(),
        }
    }
}

/// A simple async sleep implementation that works without tokio
/// Note: This is intended for tests and examples. Production code should use tokio::time::sleep
struct AsyncSleep {
    deadline: std::time::Instant,
}

impl Future for AsyncSleep {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
        if std::time::Instant::now() >= self.deadline {
            Poll::Ready(())
        } else {
            // In real async runtime, this wakes via timer
            // For now, we'll return Pending - the runtime will wake us
            cx.waker().wake_by_ref();
            Poll::Pending
        }
    }
}

fn sleep_until(deadline: std::time::Instant) -> AsyncSleep {
    AsyncSleep { deadline }
}

impl<S> Service<AutoPollGenerationRequest> for PollingMiddleware<S>
where
    S: Service<PollGenerationRequest, Response = PollingStatus, Error = GenerationError>
        + Clone
        + Send
        + 'static,
    S::Future: Send + 'static,
{
    type Response = PollingStatus;
    type Error = PollingError;
    type Future = futures_util::future::BoxFuture<'static, Result<Self::Response, Self::Error>>;

    fn poll_ready(
        &mut self,
        cx: &mut std::task::Context<'_>,
    ) -> Poll<Result<(), Self::Error>> {
        self.inner
            .poll_ready(cx)
            .map_err(|e| PollingError::GenerationError(e))
    }

    fn call(&mut self, req: AutoPollGenerationRequest) -> Self::Future {
        let mut service = self.inner.clone();
        let config = self.config.clone();
        let base_url = req.base_url.clone();
        let clip_id = req.clip_id.clone();
        let api_key = req.api_key.clone();

        Box::pin(async move {
            let mut attempt = 0;
            let mut current_backoff_ms = config.initial_backoff_ms;

            loop {
                // Create poll request
                let poll_req = PollGenerationRequest::new(
                    base_url.clone(),
                    clip_id.clone(),
                    api_key.clone(),
                );

                // Call the polling service
                let result = service.call(poll_req).await?;

                // Check status
                match result.status {
                    ClipStatus::Complete => {
                        return Ok(result);
                    }
                    ClipStatus::Submitted | ClipStatus::Pending => {
                        // Continue polling (both submitted and pending are in-progress states)
                        attempt += 1;

                        if attempt >= config.max_retries {
                            return Err(PollingError::MaxRetriesExceeded);
                        }

                        // Sleep before next attempt
                        let sleep_ms = Duration::from_millis(current_backoff_ms);
                        let deadline = std::time::Instant::now() + sleep_ms;
                        sleep_until(deadline).await;

                        // Calculate next backoff
                        current_backoff_ms = calculate_backoff(current_backoff_ms, &config);
                    }
                    ClipStatus::Error => {
                        return Err(PollingError::GenerationFailed(
                            result
                                .error
                                .unwrap_or_else(|| "Unknown error".to_string()),
                        ));
                    }
                }
            }
        })
    }
}

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

    #[test]
    fn test_polling_config_default() {
        let config = PollingConfig::default();
        assert_eq!(config.initial_backoff_ms, 1000);
        assert_eq!(config.backoff_multiplier, 1.5);
        assert_eq!(config.max_backoff_ms, 30000);
        assert_eq!(config.max_retries, 60);
        assert_eq!(config.jitter_percent, 10);
    }

    #[test]
    fn test_polling_config_aggressive() {
        let config = PollingConfig::aggressive();
        assert_eq!(config.initial_backoff_ms, 500);
        assert_eq!(config.max_retries, 30);
    }

    #[test]
    fn test_calculate_backoff_no_jitter() {
        let config = PollingConfig {
            initial_backoff_ms: 1000,
            backoff_multiplier: 1.5,
            max_backoff_ms: 30000,
            max_retries: 60,
            jitter_percent: 0,
        };

        let backoff1 = calculate_backoff(1000, &config);
        assert_eq!(backoff1, 1500);

        let backoff2 = calculate_backoff(1500, &config);
        assert_eq!(backoff2, 2250);

        let backoff3 = calculate_backoff(20000, &config);
        assert_eq!(backoff3, 30000); // Capped at max
    }

    #[test]
    fn test_calculate_backoff_with_jitter() {
        let config = PollingConfig {
            initial_backoff_ms: 1000,
            backoff_multiplier: 1.5,
            max_backoff_ms: 30000,
            max_retries: 60,
            jitter_percent: 10,
        };

        // Run multiple times to ensure jitter is within bounds
        for _ in 0..100 {
            let backoff = calculate_backoff(1000, &config);
            // 1500 ±10% = 1350-1650
            assert!(backoff >= 1000); // At least initial backoff
            assert!(backoff <= 1650); // At most 1500 * 1.1
        }
    }

    #[test]
    fn test_backoff_sequence() {
        let config = PollingConfig {
            initial_backoff_ms: 1000,
            backoff_multiplier: 1.5,
            max_backoff_ms: 30000,
            max_retries: 60,
            jitter_percent: 0,
        };

        let mut current = config.initial_backoff_ms;
        let expected_sequence = vec![1000, 1500, 2250, 3375, 5062, 7593, 11389, 17083, 25624, 30000];

        for expected in expected_sequence {
            // Allow small rounding differences due to floating point arithmetic
            assert!(
                (current as i64 - expected as i64).abs() <= 1,
                "Expected {}, got {}",
                expected,
                current
            );
            current = calculate_backoff(current, &config);
        }
    }

    #[test]
    fn test_auto_poll_generation_request_creation() {
        let req = AutoPollGenerationRequest::new(
            "https://api.example.com",
            "clip-123",
            "test_key",
        );

        assert_eq!(req.base_url, "https://api.example.com");
        assert_eq!(req.clip_id, "clip-123");
        assert_eq!(req.api_key, "test_key");
    }
}
