use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;

pub mod endpoint;
pub mod server;
pub mod validation;

pub use endpoint::{
    EndpointDefinition, EndpointKind, ParameterDefinition, ParameterLocation, PeriodicMessage,
    RequestBodyDefinition, ResponseDefinition, ScenarioConditions, ScenarioDefinition,
    ScenarioResponse, ScenarioStrategy, SideEffect, StreamConfig,
};
pub use server::{CorsConfig, ServerConfig};
pub use validation::{ConfigLoader, LoadError, LoadErrorType, LoadResult, ValidationSummary};

// Import IoT types
use crate::iot::config::TwinDefinition;

fn default_db_path() -> PathBuf {
    PathBuf::from("apicentric.db")
}

/// Main simulator configuration
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SimulatorConfig {
    /// Whether the simulator is enabled
    pub enabled: bool,
    /// Directory containing service definition YAML files
    pub services_dir: PathBuf,
    /// Port range for automatic port assignment
    pub port_range: PortRange,
    /// Path to SQLite database for persistent storage
    #[serde(default = "default_db_path")]
    pub db_path: PathBuf,
    /// Port for the admin API server
    pub admin_port: Option<u16>,
    /// Global behavior settings
    #[serde(default)]
    pub global_behavior: Option<BehaviorConfig>,
}

impl SimulatorConfig {
    /// Create a new simulator configuration
    pub fn new(enabled: bool, services_dir: PathBuf, port_range: PortRange) -> Self {
        Self {
            enabled,
            services_dir,
            port_range,
            db_path: default_db_path(),
            admin_port: None,
            global_behavior: None,
        }
    }

    /// Create a default simulator configuration
    pub fn default_config() -> Self {
        Self {
            enabled: false,
            services_dir: PathBuf::from("services"),
            port_range: PortRange {
                start: 8000,
                end: 8999,
            },
            db_path: default_db_path(),
            admin_port: None,
            global_behavior: None,
        }
    }

    /// Check if the simulator is enabled
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }
}

impl Default for SimulatorConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            services_dir: PathBuf::from("services"),
            port_range: PortRange {
                start: 8000,
                end: 8999,
            },
            db_path: default_db_path(),
            admin_port: None,
            global_behavior: None,
        }
    }
}

/// Port range configuration for service assignment
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PortRange {
    pub start: u16,
    pub end: u16,
}

/// Service definition loaded from YAML
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ServiceDefinition {
    pub name: String,
    pub version: Option<String>,
    pub description: Option<String>,
    pub server: Option<ServerConfig>,
    pub models: Option<HashMap<String, serde_json::Value>>, // JSON Schema definitions
    pub fixtures: Option<HashMap<String, serde_json::Value>>,
    #[serde(default)]
    pub bucket: Option<HashMap<String, serde_json::Value>>,
    #[serde(default)]
    pub endpoints: Option<Vec<EndpointDefinition>>,
    #[serde(default)]
    pub graphql: Option<GraphQLConfig>,
    #[serde(default)]
    pub behavior: Option<BehaviorConfig>,
    // Digital Twin support
    #[serde(default)]
    pub twin: Option<TwinDefinition>,
}

/// Helper for untagged deserialization of service files
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum UnifiedConfig {
    /// Standard service with name at top level
    Service(Box<ServiceDefinition>),
    /// Digital twin with 'twin' key at top level
    Twin { twin: TwinDefinition },
}

impl From<UnifiedConfig> for ServiceDefinition {
    fn from(config: UnifiedConfig) -> Self {
        match config {
            UnifiedConfig::Service(def) => *def,
            UnifiedConfig::Twin { twin } => ServiceDefinition {
                name: twin.name.clone(),
                version: None,
                description: Some("Digital Twin".to_string()),
                server: None,
                models: None,
                fixtures: None,
                bucket: None,
                endpoints: None,
                graphql: None,
                behavior: None,
                twin: Some(twin),
            },
        }
    }
}

/// GraphQL configuration for a service
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct GraphQLConfig {
    /// Path to the GraphQL schema file (.graphql)
    pub schema_path: String,
    /// Map of operation names to Handlebars template files
    pub mocks: HashMap<String, String>,
}

/// Behavior configuration for simulation
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BehaviorConfig {
    #[serde(default)]
    pub latency: Option<LatencyConfig>,
    #[serde(default)]
    pub error_simulation: Option<ErrorSimulationConfig>,
    #[serde(default)]
    pub rate_limiting: Option<RateLimitingConfig>,
}

/// Latency simulation configuration
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct LatencyConfig {
    pub min_ms: u64,
    pub max_ms: u64,
}

/// Error simulation configuration
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ErrorSimulationConfig {
    pub enabled: bool,
    pub rate: f64, // Error rate between 0.0 and 1.0
    pub status_codes: Option<Vec<u16>>,
}

/// Rate limiting configuration
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RateLimitingConfig {
    pub enabled: bool,
    pub requests_per_minute: u32,
}
