//! Gateway HTTP Handler - Request processing
//!
//! Handles incoming HTTP requests and routes them to the appropriate
//! content resolution logic.

use crate::cid::ContentId;
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::net::IpAddr;
use std::sync::Arc;
use tokio::sync::RwLock;

/// HTTP request abstraction
#[derive(Debug, Clone)]
pub struct GatewayRequest {
    /// Request method
    pub method: Method,
    /// Request path (CID or CID/path)
    pub path: String,
    /// Query parameters
    pub query: HashMap<String, String>,
    /// Request headers
    pub headers: HashMap<String, String>,
    /// Client IP address
    pub client_ip: Option<IpAddr>,
    /// Request body (for POST)
    pub body: Option<Vec<u8>>,
}

/// HTTP methods
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Method {
    Get,
    Head,
    Post,
    Options,
}

/// HTTP response
#[derive(Debug, Clone)]
pub struct HttpResponse {
    /// Status code
    pub status: u16,
    /// Response headers
    pub headers: HashMap<String, String>,
    /// Response body
    pub body: Vec<u8>,
}

impl HttpResponse {
    /// Create a 200 OK response
    pub fn ok(body: Vec<u8>, content_type: &str) -> Self {
        let mut headers = HashMap::new();
        headers.insert("Content-Type".to_string(), content_type.to_string());
        headers.insert("Content-Length".to_string(), body.len().to_string());
        headers.insert("X-Content-Type-Options".to_string(), "nosniff".to_string());

        Self {
            status: 200,
            headers,
            body,
        }
    }

    /// Create a 404 Not Found response
    pub fn not_found(message: &str) -> Self {
        Self {
            status: 404,
            headers: HashMap::new(),
            body: message.as_bytes().to_vec(),
        }
    }

    /// Create a 400 Bad Request response
    pub fn bad_request(message: &str) -> Self {
        Self {
            status: 400,
            headers: HashMap::new(),
            body: message.as_bytes().to_vec(),
        }
    }

    /// Create a 500 Internal Server Error response
    pub fn internal_error(message: &str) -> Self {
        Self {
            status: 500,
            headers: HashMap::new(),
            body: message.as_bytes().to_vec(),
        }
    }

    /// Create a 429 Too Many Requests response
    pub fn rate_limited() -> Self {
        Self {
            status: 429,
            headers: HashMap::new(),
            body: b"Rate limit exceeded".to_vec(),
        }
    }

    /// Create a 413 Content Too Large response
    pub fn content_too_large(max_size: u64) -> Self {
        Self {
            status: 413,
            headers: HashMap::new(),
            body: format!("Content exceeds maximum size of {} bytes", max_size).into_bytes(),
        }
    }
}

/// Rate limiter for requests
pub struct RateLimiter {
    /// Requests per window per IP
    limit: u32,
    /// Window duration in seconds
    window_secs: u64,
    /// Request counts per IP
    counts: Arc<RwLock<HashMap<IpAddr, (u64, u32)>>>, // (window_start, count)
}

impl RateLimiter {
    /// Create a new rate limiter
    pub fn new(limit: u32, window_secs: u64) -> Self {
        Self {
            limit,
            window_secs,
            counts: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Check if request is allowed
    pub async fn check(&self, ip: IpAddr) -> bool {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();

        let mut counts = self.counts.write().await;

        let entry = counts.entry(ip).or_insert((now, 0));

        // Reset window if expired
        if now - entry.0 >= self.window_secs {
            entry.0 = now;
            entry.1 = 0;
        }

        if entry.1 >= self.limit {
            false
        } else {
            entry.1 += 1;
            true
        }
    }

    /// Clean up old entries
    pub async fn cleanup(&self) {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs();

        let mut counts = self.counts.write().await;
        counts.retain(|_, (window_start, _)| now - *window_start < self.window_secs * 2);
    }
}

/// Gateway request handler
pub struct GatewayHandler {
    /// Rate limiter
    rate_limiter: RateLimiter,
    /// Maximum content size
    max_content_size: u64,
    /// Upload enabled
    enable_upload: bool,
    /// CORS origins
    cors_origins: Vec<String>,
}

impl GatewayHandler {
    /// Create a new handler
    pub fn new(rate_limit: u32, max_content_size: u64, enable_upload: bool, cors_origins: Vec<String>) -> Self {
        Self {
            rate_limiter: RateLimiter::new(rate_limit, 60),
            max_content_size,
            enable_upload,
            cors_origins,
        }
    }

    /// Parse CID from request path
    pub fn parse_path(&self, path: &str) -> Result<(ContentId, Option<String>)> {
        let path = path.trim_start_matches('/');

        // Check for subpath
        let (cid_part, subpath) = if let Some(idx) = path.find('/') {
            (&path[..idx], Some(path[idx + 1..].to_string()))
        } else {
            (path, None)
        };

        let cid = ContentId::from_string(cid_part)
            .map_err(|e| Error::InvalidCid(e.to_string()))?;

        Ok((cid, subpath))
    }

    /// Check rate limit
    pub async fn check_rate_limit(&self, ip: Option<IpAddr>) -> bool {
        if let Some(ip) = ip {
            self.rate_limiter.check(ip).await
        } else {
            true // Allow if no IP (internal requests)
        }
    }

    /// Build CORS headers
    pub fn cors_headers(&self, origin: Option<&str>) -> HashMap<String, String> {
        let mut headers = HashMap::new();

        if self.cors_origins.contains(&"*".to_string()) {
            headers.insert("Access-Control-Allow-Origin".to_string(), "*".to_string());
        } else if let Some(origin) = origin {
            if self.cors_origins.iter().any(|o| o == origin) {
                headers.insert("Access-Control-Allow-Origin".to_string(), origin.to_string());
            }
        }

        headers.insert(
            "Access-Control-Allow-Methods".to_string(),
            "GET, HEAD, OPTIONS".to_string(),
        );
        headers.insert(
            "Access-Control-Allow-Headers".to_string(),
            "Content-Type".to_string(),
        );

        headers
    }

    /// Check if upload is allowed
    pub fn upload_allowed(&self) -> bool {
        self.enable_upload
    }

    /// Check content size limit
    pub fn check_size(&self, size: u64) -> bool {
        size <= self.max_content_size
    }
}

/// Upload response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UploadResponse {
    /// Content ID of uploaded content
    pub cid: String,
    /// Size in bytes
    pub size: u64,
    /// Gateway URL
    pub url: String,
}
