//! TOML configuration loader.
//!
//! All non-secret configuration lives in `config/<env>.toml`. Secrets come from
//! environment variables (named by `*_env` keys), never from the TOML itself.
//! See `_docs/02_document/deployment/containerization.md §6`.

use std::path::Path;

use serde::{Deserialize, Serialize};

use crate::error::{AutopilotError, Result};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    pub health: HealthConfig,
    pub observability: ObservabilityConfig,
    pub storage: StorageConfig,
    pub rtsp: RtspConfig,
    pub gimbal: GimbalConfig,
    pub mavlink: MavlinkConfig,
    pub missions_api: MissionsApiConfig,
    pub ground_station: GroundStationConfig,
    pub detections: DetectionsConfig,
    pub vlm: VlmConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthConfig {
    pub bind: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ObservabilityConfig {
    pub log_format: String,
    pub default_log_filter: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageConfig {
    pub state_dir: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RtspConfig {
    pub url: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GimbalConfig {
    pub endpoint: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MavlinkConfig {
    pub connection: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MissionsApiConfig {
    pub endpoint: String,
    pub auth_env: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GroundStationConfig {
    pub endpoint: String,
    pub auth_env: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionsConfig {
    pub endpoint: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VlmConfig {
    pub enabled: bool,
    pub ipc_socket: String,
}

pub struct ConfigLoader;

impl ConfigLoader {
    /// Load + parse a TOML config from disk.
    pub fn from_path(path: impl AsRef<Path>) -> Result<Config> {
        let raw = std::fs::read_to_string(path.as_ref())
            .map_err(|e| AutopilotError::Config(format!("cannot read {:?}: {e}", path.as_ref())))?;
        let config: Config = toml::from_str(&raw)?;
        Ok(config)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const DEV_CONFIG: &str = r#"
[health]
bind = "127.0.0.1:8080"

[observability]
log_format = "json"
default_log_filter = "info"

[storage]
state_dir = "/var/lib/autopilot"

[rtsp]
url = "rtsp://127.0.0.1:8554/mock"

[gimbal]
endpoint = "127.0.0.1:6000"

[mavlink]
connection = "udp://127.0.0.1:14550"

[missions_api]
endpoint = "http://127.0.0.1:8443"
auth_env = "MISSIONS_API_TOKEN"

[ground_station]
endpoint = "http://127.0.0.1:8444"
auth_env = "GROUND_STATION_TOKEN"

[detections]
endpoint = "http://127.0.0.1:50051"

[vlm]
enabled = false
ipc_socket = "/var/run/vila/ipc.sock"
"#;

    #[test]
    fn config_parses_canonical_layout() {
        // Act
        let config: Config = toml::from_str(DEV_CONFIG).expect("dev config must parse");

        // Assert
        assert_eq!(config.health.bind, "127.0.0.1:8080");
        assert!(!config.vlm.enabled);
        assert_eq!(config.missions_api.auth_env, "MISSIONS_API_TOKEN");
    }
}
