use crate::error::{HermesError, Result};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;

fn default_rpc_url() -> String {
    "wss://rpc.hippius.network:443".to_string()
}

fn default_sync_engine_url() -> String {
    "https://hippius.store".to_string()
}

/// Complete configuration required to initialize a Hermes node.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
    /// Path to the 32-byte Ed25519 secret key file used as the Iroh node identity.
    /// The NodeId derived from this key must match what is registered on-chain
    /// in the AccountProfile pallet for this node's SS58 address.
    pub node_secret_key_path: PathBuf,
    pub ss58_address: String,
    pub api_token: String,
    pub storage_directory: PathBuf,
    #[serde(default = "default_rpc_url")]
    pub rpc_url: String,
    /// Base URL of the Hippius Sync-Engine used for file upload/download.
    #[serde(default = "default_sync_engine_url")]
    pub sync_engine_url: String,
    /// Subnet netuids this node participates in. Controls which per-subnet
    /// ALPNs are registered (e.g. `hippius-hermes/subnet/42`). The cross-subnet
    /// ALPN `hippius-hermes/1` is always registered regardless.
    #[serde(default)]
    pub subnet_ids: Vec<u16>,
}

impl Config {
    /// Loads the configuration securely from a JSON file.
    pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
        let content = fs::read_to_string(path)
            .map_err(|e| HermesError::Config(format!("Failed to read config file: {}", e)))?;
        let config: Config = serde_json::from_str(&content)
            .map_err(|e| HermesError::Config(format!("Failed to parse config file: {}", e)))?;
        Ok(config)
    }
}

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

    #[test]
    fn test_config_deserialize_without_subnet_ids() {
        let json = r#"{
            "node_secret_key_path": "/etc/hermes/iroh.key",
            "ss58_address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
            "api_token": "test-token",
            "storage_directory": "/tmp/hermes"
        }"#;
        let config: Config = serde_json::from_str(json).unwrap();
        assert!(config.subnet_ids.is_empty());
        assert_eq!(config.rpc_url, "wss://rpc.hippius.network:443");
    }

    #[test]
    fn test_config_deserialize_with_subnet_ids() {
        let json = r#"{
            "node_secret_key_path": "/etc/hermes/iroh.key",
            "ss58_address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
            "api_token": "test-token",
            "storage_directory": "/tmp/hermes",
            "subnet_ids": [1, 42, 255]
        }"#;
        let config: Config = serde_json::from_str(json).unwrap();
        assert_eq!(config.subnet_ids, vec![1, 42, 255]);
    }

    #[test]
    fn test_config_deserialize_empty_subnet_ids() {
        let json = r#"{
            "node_secret_key_path": "/etc/hermes/iroh.key",
            "ss58_address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
            "api_token": "test-token",
            "storage_directory": "/tmp/hermes",
            "subnet_ids": []
        }"#;
        let config: Config = serde_json::from_str(json).unwrap();
        assert!(config.subnet_ids.is_empty());
    }

    #[test]
    fn test_config_serialize_roundtrip() {
        let config = Config {
            node_secret_key_path: "/etc/hermes/iroh.key".into(),
            ss58_address: "5GrwvaEF".into(),
            api_token: "token".into(),
            storage_directory: "/tmp".into(),
            rpc_url: "wss://rpc.hippius.network:443".into(),
            sync_engine_url: "https://hippius.store".into(),
            subnet_ids: vec![42, 69],
        };
        let json = serde_json::to_string(&config).unwrap();
        let deserialized: Config = serde_json::from_str(&json).unwrap();
        assert_eq!(deserialized.subnet_ids, vec![42, 69]);
        assert_eq!(
            deserialized.node_secret_key_path,
            PathBuf::from("/etc/hermes/iroh.key")
        );
    }

    #[test]
    fn test_config_from_file() {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("config.json");
        let json = r#"{
            "node_secret_key_path": "/etc/hermes/iroh.key",
            "ss58_address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
            "api_token": "test-token",
            "storage_directory": "/tmp/hermes",
            "subnet_ids": [7, 42]
        }"#;
        fs::write(&path, json).unwrap();
        let config = Config::from_file(&path).unwrap();
        assert_eq!(config.subnet_ids, vec![7, 42]);
    }

    #[test]
    fn test_config_from_file_missing_subnet_ids_defaults() {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("config.json");
        let json = r#"{
            "node_secret_key_path": "/etc/hermes/iroh.key",
            "ss58_address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
            "api_token": "test-token",
            "storage_directory": "/tmp/hermes"
        }"#;
        fs::write(&path, json).unwrap();
        let config = Config::from_file(&path).unwrap();
        assert!(config.subnet_ids.is_empty());
    }
}
