use governor::{
    clock::DefaultClock,
    state::{InMemoryState, NotKeyed},
    Quota, RateLimiter as GovernorRateLimiter,
};
use std::net::IpAddr;
use std::sync::Arc;
use std::collections::HashMap;
use std::num::NonZeroU32;
use tokio::sync::RwLock;

/// Thread-safe rate limiter for IP-based request throttling.
///
/// This struct provides rate limiting functionality using the `governor` crate,
/// allowing up to 100 requests per IP address per hour. Each IP address has its
/// own independent rate limiter state, ensuring that one client cannot affect
/// the rate limiting of another.
///
/// # Architecture
///
/// The rate limiter uses a thread-safe `Arc<RwLock<HashMap<IpAddr, Arc<GovernorRateLimiter>>>>`
/// to store per-IP rate limiters. This design allows:
///
/// - Concurrent reads from multiple threads without blocking
/// - Exclusive writes when creating new limiters
/// - Zero-copy access to existing limiters via `Arc`
///
/// # Example
///
/// ```no_run
/// use std::net::IpAddr;
/// use tokio::net::TcpListener;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let limiter = RateLimiter::new();
/// let ip: IpAddr = "127.0.0.1".parse().unwrap();
///
/// match limiter.check(&ip).await {
///     Ok(_) => println!("Request allowed"),
///     Err(e) => println!("Rate limited: {}", e),
/// }
/// # Ok(())
/// # }
/// ```
#[derive(Clone)]
pub struct RateLimiter {
    /// Map of IP addresses to their respective rate limiters.
    ///
    /// Each IP gets its own `GovernorRateLimiter` instance to ensure
    /// independent rate limiting per client. The inner `Arc` allows
    /// multiple tasks to check the same limiter concurrently without
    /// cloning the limiter state.
    limiters: Arc<RwLock<HashMap<IpAddr, Arc<GovernorRateLimiter<NotKeyed, InMemoryState, DefaultClock>>>>>,
}

/// Error type for rate limiting operations.
///
/// This error is returned when a rate limit check fails, indicating that
/// the client has exceeded the allowed request rate.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RateLimitError {
    /// The IP address that triggered the rate limit.
    pub ip: IpAddr,
}

impl std::fmt::Display for RateLimitError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Rate limit exceeded for IP: {}", self.ip)
    }
}

impl std::error::Error for RateLimitError {}

impl RateLimiter {
    /// Creates a new `RateLimiter` instance.
    ///
    /// The rate limiter is configured to allow 100 requests per IP address per hour.
    /// Each IP address maintains its own independent rate limiter state.
    ///
    /// # Example
    ///
    /// ```
    /// # use indieweb::ratelimiter::RateLimiter;
    /// let limiter = RateLimiter::new();
    /// ```
    pub fn new() -> Self {
        Self {
            limiters: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Checks if a request from the given IP address should be rate-limited.
    ///
    /// This method implements the core rate limiting logic:
    ///
    /// 1. Retrieves or creates a rate limiter for the IP address
    /// 2. Checks if the request would exceed the rate limit
    /// 3. Returns `Ok(())` if the request is allowed, `Err(RateLimitError)` if not
    ///
    /// # Arguments
    ///
    /// * `ip` - The IP address of the client making the request
    ///
    /// # Returns
    ///
    /// * `Ok(())` - The request is allowed (within rate limits)
    /// * `Err(RateLimitError)` - The request is blocked (rate limit exceeded)
    ///
    /// # Thread Safety
    ///
    /// This method is thread-safe and can be called concurrently from multiple
    /// tasks. It uses a read-write lock to allow concurrent reads while
    /// ensuring exclusive access when creating new limiters.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use indieweb::ratelimiter::RateLimiter;
    /// # use std::net::IpAddr;
    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let limiter = RateLimiter::new();
    /// let ip: IpAddr = "192.168.1.1".parse().unwrap();
    ///
    /// if let Err(e) = limiter.check(&ip).await {
    ///     eprintln!("Client rate limited: {}", e);
    ///     // Return 429 Too Many Requests
    /// }
    /// # Ok(())
    /// # }
    /// ```
    pub async fn check(&self, ip: &IpAddr) -> Result<(), RateLimitError> {
        // Try to read the limiter for this IP
        {
            let limiters = self.limiters.read().await;
            if let Some(limiter) = limiters.get(ip) {
                return limiter.check().map_err(|_| RateLimitError { ip: *ip });
            }
        }

        // Limiter doesn't exist, create it in a write lock
        let mut limiters = self.limiters.write().await;
        
        // Double-check: another task might have created it while we were waiting for the write lock
        if let Some(limiter) = limiters.get(ip) {
            return limiter.check().map_err(|_| RateLimitError { ip: *ip });
        }

        // Create a new rate limiter: 100 requests per hour
        let quota = Quota::per_hour(NonZeroU32::new(100).unwrap());
        let limiter = GovernorRateLimiter::direct(quota);
        
        // Perform the first check immediately
        limiter.check().map_err(|_| RateLimitError { ip: *ip })?;
        
        // Store the limiter for future requests
        limiters.insert(*ip, Arc::new(limiter));
        
        Ok(())
    }

    /// Returns the number of IP addresses currently being tracked.
    ///
    /// This is primarily useful for debugging and monitoring purposes.
    ///
    /// # Example
    ///
    /// ```
    /// # use indieweb::ratelimiter::RateLimiter;
    /// # use std::net::IpAddr;
    /// # async fn example() {
    /// let limiter = RateLimiter::new();
    /// let count = limiter.tracked_ip_count().await;
    /// assert_eq!(count, 0);
    /// # }
    /// ```
    pub async fn tracked_ip_count(&self) -> usize {
        self.limiters.read().await.len()
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    use std::net::{Ipv4Addr, Ipv6Addr};
    use std::thread;
    use std::time::Duration;
    use tokio::time::sleep;

    /// Test that the rate limiter enforces the threshold correctly.
    #[tokio::test]
    async fn test_rate_limiting_threshold() {
        let limiter = RateLimiter::new();
        let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));

        // Should allow requests within the limit
        for i in 0..100 {
            assert!(
                limiter.check(&ip).await.is_ok(),
                "Request {} should be allowed",
                i + 1
            );
        }

        // The 101st request should be rate limited
        assert!(
            limiter.check(&ip).await.is_err(),
            "101st request should be rate limited"
        );
    }

    /// Test that different IP addresses have independent rate limits.
    #[tokio::test]
    async fn test_ip_based_isolation() {
        let limiter = RateLimiter::new();
        let ip1 = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1));
        let ip2 = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2));

        // Exhaust the rate limit for ip1
        for _ in 0..100 {
            limiter.check(&ip1).await.unwrap();
        }

        // ip1 should now be rate limited
        assert!(limiter.check(&ip1).await.is_err());

        // But ip2 should still be able to make requests
        assert!(limiter.check(&ip2).await.is_ok());
        for _ in 1..10 {
            assert!(limiter.check(&ip2).await.is_ok());
        }
    }

    /// Test that IPv4 and IPv6 addresses are tracked independently.
    #[tokio::test]
    async fn test_ipv4_ipv6_isolation() {
        let limiter = RateLimiter::new();
        let ipv4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
        let ipv6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));

        // Exhaust IPv4 rate limit
        for _ in 0..100 {
            limiter.check(&ipv4).await.unwrap();
        }
        assert!(limiter.check(&ipv4).await.is_err());

        // IPv6 should still work
        assert!(limiter.check(&ipv6).await.is_ok());
        for _ in 1..100 {
            assert!(limiter.check(&ipv6).await.is_ok());
        }
        assert!(limiter.check(&ipv6).await.is_err());
    }

    /// Test that the rate limiter correctly tracks IP addresses.
    #[tokio::test]
    async fn test_ip_tracking() {
        let limiter = RateLimiter::new();

        // Initially, no IPs are tracked
        assert_eq!(limiter.tracked_ip_count().await, 0);

        // After first request from IP1, 1 IP should be tracked
        let ip1 = IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4));
        limiter.check(&ip1).await.unwrap();
        assert_eq!(limiter.tracked_ip_count().await, 1);

        // After first request from IP2, 2 IPs should be tracked
        let ip2 = IpAddr::V4(Ipv4Addr::new(5, 6, 7, 8));
        limiter.check(&ip2).await.unwrap();
        assert_eq!(limiter.tracked_ip_count().await, 2);

        // Additional requests from the same IPs don't increase the count
        for _ in 0..10 {
            limiter.check(&ip1).await.unwrap();
        }
        assert_eq!(limiter.tracked_ip_count().await, 2);
    }

    /// Test concurrent access from multiple tasks.
    #[tokio::test]
    async fn test_concurrent_access() {
        let limiter = Arc::new(RateLimiter::new());
        let ip1 = IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1));
        let ip2 = IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2));

        let mut handles = vec![];

        // Spawn 50 tasks for ip1
        for _ in 0..50 {
            let limiter_clone = Arc::clone(&limiter);
            let handle = tokio::spawn(async move {
                limiter_clone.check(&ip1).await
            });
            handles.push(handle);
        }

        // Spawn 50 tasks for ip2
        for _ in 0..50 {
            let limiter_clone = Arc::clone(&limiter);
            let handle = tokio::spawn(async move {
                limiter_clone.check(&ip2).await
            });
            handles.push(handle);
        }

        // All 100 requests should succeed
        let mut success_count = 0;
        for handle in handles {
            assert!(handle.await.unwrap().is_ok());
            success_count += 1;
        }
        assert_eq!(success_count, 100);
    }

    /// Test that RateLimitError displays correctly.
    #[test]
    fn test_rate_limit_error_display() {
        let ip = IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4));
        let error = RateLimitError { ip };
        let display = format!("{}", error);
        assert!(display.contains("Rate limit exceeded"));
        assert!(display.contains("1.2.3.4"));
    }

    /// Test Default trait implementation.
    #[tokio::test]
    async fn test_default() {
        let limiter = RateLimiter::default();
        // Just ensure it creates successfully
        assert_eq!(limiter.tracked_ip_count().await, 0);
    }

    /// Test that the rate limiter handles localhost correctly.
    #[tokio::test]
    async fn test_localhost_handling() {
        let limiter = RateLimiter::new();
        let localhost_v4 = IpAddr::V4(Ipv4Addr::LOCALHOST);
        let localhost_v6 = IpAddr::V6(Ipv6Addr::LOCALHOST);

        // Both localhost variants should work independently
        assert!(limiter.check(&localhost_v4).await.is_ok());
        assert!(limiter.check(&localhost_v6).await.is_ok());

        // Exhaust IPv4 localhost
        for _ in 1..100 {
            limiter.check(&localhost_v4).await.unwrap();
        }
        assert!(limiter.check(&localhost_v4).await.is_err());

        // IPv6 localhost should still work
        assert!(limiter.check(&localhost_v6).await.is_ok());
    }
}
