//! WASM Guest Rate Limit Example for sgl-model-gateway
//!
//! This example demonstrates rate limiting middleware
//! for sgl-model-gateway using the WebAssembly Component Model.
//!
//! Features:
//! - Rate limiting based on API Key or IP address
//! - Fixed time window (e.g., 60 requests per minute)
//! - Returns 429 Too Many Requests when limit exceeded
//!
//! Note: This is a simplified implementation. Since WASM components are stateless,
//! each instance maintains its own counters. For production use, consider
//! implementing rate limiting at the host/router level with shared state.

wit_bindgen::generate!({
    path: "../../../src/wasm/interface",
    world: "sgl-model-gateway",
});

use std::cell::RefCell;

use exports::sgl::model_gateway::{
    middleware_on_request::Guest as OnRequestGuest,
    middleware_on_response::Guest as OnResponseGuest,
};
use sgl::model_gateway::middleware_types::{Action, Request, Response};

/// Main middleware implementation
struct Middleware;

// Rate limit configuration
const RATE_LIMIT_REQUESTS: u64 = 60; // Maximum requests per window
const RATE_LIMIT_WINDOW_MS: u64 = 60_000; // Time window in milliseconds (1 minute)

// Simple in-memory counter (per WASM instance)
// In a real implementation, this would be shared across all instances
// This is a simplified example for demonstration purposes
struct RateLimitState {
    requests: Vec<(String, u64)>, // (identifier, timestamp_ms)
}

impl RateLimitState {
    fn new() -> Self {
        Self {
            requests: Vec::new(),
        }
    }

    // Clean up old entries outside the time window
    fn cleanup(&mut self, current_time_ms: u64) {
        let cutoff = current_time_ms.saturating_sub(RATE_LIMIT_WINDOW_MS);
        self.requests.retain(|(_, timestamp)| *timestamp > cutoff);
    }

    // Check if identifier has exceeded rate limit
    fn check_limit(&mut self, identifier: &str, current_time_ms: u64) -> bool {
        self.cleanup(current_time_ms);

        // Count requests in current window for this identifier
        let count = self
            .requests
            .iter()
            .filter(|(id, timestamp)| {
                id == identifier
                    && *timestamp > current_time_ms.saturating_sub(RATE_LIMIT_WINDOW_MS)
            })
            .count() as u64;

        if count >= RATE_LIMIT_REQUESTS {
            return false; // Limit exceeded
        }

        // Add new request
        self.requests
            .push((identifier.to_string(), current_time_ms));
        true // Within limit
    }
}

// Thread-local state (per WASM instance thread)
// Using thread_local! is safer than static mut as it avoids unsafe blocks
// and provides separate state for each thread automatically
thread_local! {
    static RATE_LIMIT_STATE: RefCell<RateLimitState> = RefCell::new(RateLimitState::new());
}

fn get_identifier(req: &Request) -> String {
    // Helper function to find header value
    let find_header_value =
        |headers: &[sgl::model_gateway::middleware_types::Header], name: &str| -> Option<String> {
            headers
                .iter()
                .find(|h| h.name.eq_ignore_ascii_case(name))
                .map(|h| h.value.clone())
        };

    // Prefer API Key as identifier (more stable than IP)
    if let Some(auth_header) = find_header_value(&req.headers, "authorization") {
        if auth_header.starts_with("Bearer ") {
            return format!("api_key:{}", &auth_header[7..]);
        } else if auth_header.starts_with("ApiKey ") {
            return format!("api_key:{}", &auth_header[7..]);
        }
    }

    if let Some(api_key) = find_header_value(&req.headers, "x-api-key") {
        return format!("api_key:{}", api_key);
    }

    // Fall back to IP address from forwarded headers
    if let Some(forwarded_for) = find_header_value(&req.headers, "x-forwarded-for") {
        // Take first IP from comma-separated list
        let ip = forwarded_for.split(',').next().unwrap_or("").trim();
        if !ip.is_empty() {
            return format!("ip:{}", ip);
        }
    }

    if let Some(real_ip) = find_header_value(&req.headers, "x-real-ip") {
        return format!("ip:{}", real_ip);
    }

    // Last resort: use request ID (not ideal, but better than nothing)
    format!("req_id:{}", req.request_id)
}

// Implement on-request interface
impl OnRequestGuest for Middleware {
    fn on_request(req: Request) -> Action {
        let identifier = get_identifier(&req);
        let current_time_ms = req.now_epoch_ms;

        // Access thread-local state safely without unsafe blocks
        // Each thread gets its own RateLimitState instance
        RATE_LIMIT_STATE.with(|state| {
            let mut state = state.borrow_mut();
            if !state.check_limit(&identifier, current_time_ms) {
                // Rate limit exceeded
                return Action::Reject(429);
            }
            // Within rate limit, continue processing
            Action::Continue
        })
    }
}

// Implement on-response interface (empty - not used for rate limiting)
impl OnResponseGuest for Middleware {
    fn on_response(_resp: Response) -> Action {
        Action::Continue
    }
}

// Export the component
export!(Middleware);
