//! Context Builder for Policy Evaluation
//!
//! Builds evaluation context from environment variables and request data.

use std::collections::HashMap;

use chrono::{DateTime, Datelike, Local, Timelike, Utc};
use serde_json::{Map, Value};

use crate::error::{policy, ControlCenterError, Result};

/// Context builder for policy evaluation
pub struct ContextBuilder {
    // Future: Could include context enrichment sources
}

impl ContextBuilder {
    /// Create new context builder
    pub fn new() -> Self {
        Self {}
    }

    /// Build context map from environment variables
    pub async fn build_context(
        &self,
        environment: &HashMap<String, serde_json::Value>,
    ) -> Result<Map<String, Value>> {
        let mut context_map = Map::new();

        // Add base environment variables
        for (key, value) in environment {
            context_map.insert(key.clone(), value.clone());
        }

        // Add standard context variables
        self.add_time_context(&mut context_map);
        self.add_system_context(&mut context_map).await?;
        self.add_security_context(&mut context_map);

        Ok(context_map)
    }

    /// Add time-based context variables
    fn add_time_context(&self, context: &mut Map<String, Value>) {
        let now = Utc::now();
        let local_now = Local::now();

        context.insert(
            "time".to_string(),
            Value::Object({
                let mut time_map = Map::new();
                time_map.insert("utc".to_string(), Value::String(now.to_rfc3339()));
                time_map.insert("local".to_string(), Value::String(local_now.to_rfc3339()));
                time_map.insert(
                    "timestamp".to_string(),
                    Value::Number(now.timestamp().into()),
                );
                time_map.insert("hour".to_string(), Value::Number(now.hour().into()));
                time_map.insert(
                    "day_of_week".to_string(),
                    Value::Number(now.weekday().num_days_from_monday().into()),
                );
                time_map.insert("day_of_month".to_string(), Value::Number(now.day().into()));
                time_map.insert("month".to_string(), Value::Number(now.month().into()));
                time_map.insert("year".to_string(), Value::Number(now.year().into()));
                time_map
            }),
        );
    }

    /// Add system context variables
    async fn add_system_context(&self, context: &mut Map<String, Value>) -> Result<()> {
        context.insert(
            "system".to_string(),
            Value::Object({
                let mut system_map = Map::new();

                // Add hostname
                if let Ok(hostname) = std::env::var("HOSTNAME") {
                    system_map.insert("hostname".to_string(), Value::String(hostname));
                }

                // Add environment type
                if let Ok(env_type) = std::env::var("ENVIRONMENT") {
                    system_map.insert("environment".to_string(), Value::String(env_type));
                } else {
                    system_map.insert(
                        "environment".to_string(),
                        Value::String("development".to_string()),
                    );
                }

                // Add deployment context
                if let Ok(deployment_id) = std::env::var("DEPLOYMENT_ID") {
                    system_map.insert("deployment_id".to_string(), Value::String(deployment_id));
                }

                // Add service information
                system_map.insert(
                    "service".to_string(),
                    Value::String("control-center".to_string()),
                );
                system_map.insert(
                    "version".to_string(),
                    Value::String(env!("CARGO_PKG_VERSION").to_string()),
                );

                system_map
            }),
        );

        Ok(())
    }

    /// Add security context variables
    fn add_security_context(&self, context: &mut Map<String, Value>) {
        context.insert(
            "security".to_string(),
            Value::Object({
                let mut security_map = Map::new();

                // Add security level based on environment
                let security_level =
                    if std::env::var("ENVIRONMENT").unwrap_or_default() == "production" {
                        "high"
                    } else {
                        "standard"
                    };
                security_map.insert(
                    "level".to_string(),
                    Value::String(security_level.to_string()),
                );

                // Add compliance requirements
                let mut compliance_array = Vec::new();
                if std::env::var("REQUIRE_SOC2").unwrap_or_default() == "true" {
                    compliance_array.push(Value::String("soc2".to_string()));
                }
                if std::env::var("REQUIRE_HIPAA").unwrap_or_default() == "true" {
                    compliance_array.push(Value::String("hipaa".to_string()));
                }
                security_map.insert(
                    "compliance_requirements".to_string(),
                    Value::Array(compliance_array),
                );

                // Add maintenance window information
                if let Ok(maintenance_start) = std::env::var("MAINTENANCE_WINDOW_START") {
                    security_map.insert(
                        "maintenance_window_start".to_string(),
                        Value::String(maintenance_start),
                    );
                }
                if let Ok(maintenance_end) = std::env::var("MAINTENANCE_WINDOW_END") {
                    security_map.insert(
                        "maintenance_window_end".to_string(),
                        Value::String(maintenance_end),
                    );
                }

                security_map
            }),
        );
    }

    /// Build context from IP address and geolocation
    pub async fn build_geo_context(&self, ip_address: &str) -> Result<Map<String, Value>> {
        let mut context = Map::new();

        // In a real implementation, this would call a geolocation service
        // For now, we'll add placeholder data
        context.insert(
            "geo".to_string(),
            Value::Object({
                let mut geo_map = Map::new();
                geo_map.insert("ip".to_string(), Value::String(ip_address.to_string()));

                // Detect local/private IPs
                if ip_address.starts_with("192.168.")
                    || ip_address.starts_with("10.")
                    || ip_address.starts_with("172.")
                    || ip_address == "127.0.0.1"
                    || ip_address == "::1"
                {
                    geo_map.insert("location".to_string(), Value::String("private".to_string()));
                    geo_map.insert("country".to_string(), Value::String("local".to_string()));
                } else {
                    // Would integrate with actual geolocation service
                    geo_map.insert("location".to_string(), Value::String("unknown".to_string()));
                    geo_map.insert("country".to_string(), Value::String("unknown".to_string()));
                }

                geo_map
            }),
        );

        Ok(context)
    }

    /// Build context from user authentication information
    pub fn build_auth_context(
        &self,
        user_id: &str,
        roles: &[String],
        mfa_enabled: bool,
    ) -> Map<String, Value> {
        let mut context = Map::new();

        context.insert(
            "auth".to_string(),
            Value::Object({
                let mut auth_map = Map::new();
                auth_map.insert("user_id".to_string(), Value::String(user_id.to_string()));
                auth_map.insert(
                    "roles".to_string(),
                    Value::Array(
                        roles
                            .iter()
                            .map(|role| Value::String(role.clone()))
                            .collect(),
                    ),
                );
                auth_map.insert("mfa_enabled".to_string(), Value::Bool(mfa_enabled));

                // Add session information
                auth_map.insert(
                    "session_created".to_string(),
                    Value::String(Utc::now().to_rfc3339()),
                );

                auth_map
            }),
        );

        context
    }

    /// Build context from request metadata
    pub fn build_request_context(
        &self,
        method: &str,
        path: &str,
        user_agent: &str,
    ) -> Map<String, Value> {
        let mut context = Map::new();

        context.insert(
            "request".to_string(),
            Value::Object({
                let mut request_map = Map::new();
                request_map.insert("method".to_string(), Value::String(method.to_string()));
                request_map.insert("path".to_string(), Value::String(path.to_string()));
                request_map.insert(
                    "user_agent".to_string(),
                    Value::String(user_agent.to_string()),
                );

                // Parse path for additional context
                let path_segments: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
                request_map.insert(
                    "path_segments".to_string(),
                    Value::Array(
                        path_segments
                            .iter()
                            .map(|segment| Value::String(segment.to_string()))
                            .collect(),
                    ),
                );

                // Detect API vs UI requests
                let is_api = path.starts_with("/api/")
                    || user_agent.contains("curl")
                    || user_agent.contains("PostmanRuntime");
                request_map.insert("is_api".to_string(), Value::Bool(is_api));

                request_map
            }),
        );

        context
    }

    /// Merge multiple context maps
    pub fn merge_contexts(&self, contexts: Vec<Map<String, Value>>) -> Map<String, Value> {
        let mut merged = Map::new();

        for context in contexts {
            for (key, value) in context {
                merged.insert(key, value);
            }
        }

        merged
    }

    /// Validate context for required fields
    pub fn validate_context(
        &self,
        context: &Map<String, Value>,
        required_fields: &[String],
    ) -> Result<()> {
        for field in required_fields {
            if !context.contains_key(field) {
                return Err(ControlCenterError::Policy(policy::PolicyError::Evaluation(
                    format!("Required context field missing: {}", field),
                )));
            }
        }

        Ok(())
    }
}

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