use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use tokio::fs;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ContainerRuntime {
    Podman,
    Docker,
    Auto,
}

impl ContainerRuntime {
    pub fn from_str(s: &str) -> Self {
        match s.to_lowercase().as_str() {
            "podman" => ContainerRuntime::Podman,
            "docker" => ContainerRuntime::Docker,
            "auto" | _ => ContainerRuntime::Auto,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BitcoinSimulation {
    Mock,
    Testnet,
    Mainnet,
    None,
}

impl BitcoinSimulation {
    pub fn from_str(s: &str) -> Self {
        match s.to_lowercase().as_str() {
            "mock" => BitcoinSimulation::Mock,
            "testnet" => BitcoinSimulation::Testnet,
            "mainnet" => BitcoinSimulation::Mainnet,
            "none" | _ => BitcoinSimulation::None,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    pub data_dir: PathBuf,
    pub bind_host: String,
    pub bind_port: u16,
    pub log_level: String,
    /// Host IP for container env vars (FM_API_URL, BACKEND_MAINNET_HTTP_HOST, etc.)
    pub host_ip: String,
    // Dev mode configuration
    pub dev_mode: bool,
    pub container_runtime: ContainerRuntime,
    pub port_offset: u16,
    pub bitcoin_simulation: BitcoinSimulation,
    pub dev_data_dir: PathBuf,
    /// Nostr discovery: opt-in only. When true + relays non-empty, publish node to relays.
    #[serde(default)]
    pub nostr_discovery_enabled: bool,
    /// Nostr relay URLs (comma-separated). Only used when nostr_discovery_enabled.
    #[serde(default)]
    pub nostr_relays: Vec<String>,
    /// Tor SOCKS5 proxy (e.g. 127.0.0.1:9050). When set, ALL Nostr traffic routes through Tor.
    #[serde(default)]
    pub nostr_tor_proxy: Option<String>,
}

impl Config {
    /// Detect primary host IP (first non-loopback IPv4)
    fn detect_host_ip() -> Result<String> {
        let output = std::process::Command::new("hostname")
            .args(["-I"])
            .output()
            .context("Failed to run hostname -I")?;
        let s = String::from_utf8_lossy(&output.stdout);
        let ip = s
            .split_whitespace()
            .find(|s| !s.starts_with("127.") && s.contains('.'))
            .unwrap_or("127.0.0.1");
        Ok(ip.to_string())
    }

    pub async fn load() -> Result<Self> {
        // Default configuration
        let mut config = Self::default();
        
        // Detect if running from macOS app bundle
        if let Ok(exe_path) = std::env::current_exe() {
            if let Some(exe_str) = exe_path.to_str() {
                if exe_str.contains(".app/Contents/MacOS") {
                    // Running from macOS bundle - use user's Library directory
                    if let Some(home) = std::env::var_os("HOME") {
                        let app_support = PathBuf::from(home)
                            .join("Library/Application Support/Archipelago");
                        config.data_dir = app_support.join("data");
                        config.dev_data_dir = app_support.join("data");
                        tracing::info!("🍎 Detected macOS bundle, using: {}", app_support.display());
                    }
                }
            }
        }

        // Try to load from config file
        let config_path = Path::new("/etc/archipelago/config.toml");
        if config_path.exists() {
            let content = fs::read_to_string(config_path).await
                .context("Failed to read config file")?;
            let file_config: Config = toml::de::from_str(&content)
                .context("Failed to parse config file")?;
            config = file_config;
        }

        // Override with environment variables
        if let Ok(data_dir) = std::env::var("ARCHIPELAGO_DATA_DIR") {
            config.data_dir = PathBuf::from(data_dir);
        }

        if let Ok(bind) = std::env::var("ARCHIPELAGO_BIND") {
            let parts: Vec<&str> = bind.split(':').collect();
            if parts.len() == 2 {
                config.bind_host = parts[0].to_string();
                config.bind_port = parts[1].parse()
                    .context("Invalid port in ARCHIPELAGO_BIND")?;
            }
        }

        if let Ok(level) = std::env::var("ARCHIPELAGO_LOG_LEVEL") {
            config.log_level = level;
        }

        // Dev mode configuration
        if let Ok(dev_mode) = std::env::var("ARCHIPELAGO_DEV_MODE") {
            config.dev_mode = dev_mode.parse().unwrap_or(false);
        }

        if let Ok(runtime) = std::env::var("ARCHIPELAGO_CONTAINER_RUNTIME") {
            config.container_runtime = ContainerRuntime::from_str(&runtime);
        }

        if let Ok(offset) = std::env::var("ARCHIPELAGO_PORT_OFFSET") {
            config.port_offset = offset.parse()
                .context("Invalid port offset in ARCHIPELAGO_PORT_OFFSET")?;
        }

        if let Ok(sim) = std::env::var("ARCHIPELAGO_BITCOIN_SIMULATION") {
            config.bitcoin_simulation = BitcoinSimulation::from_str(&sim);
        }

        if let Ok(dev_data_dir) = std::env::var("ARCHIPELAGO_DEV_DATA_DIR") {
            config.dev_data_dir = PathBuf::from(dev_data_dir);
        }

        // Nostr discovery (opt-in, secure by default)
        if let Ok(v) = std::env::var("ARCHIPELAGO_NOSTR_DISCOVERY_ENABLED") {
            config.nostr_discovery_enabled = v.parse().unwrap_or(false);
        }
        if let Ok(v) = std::env::var("ARCHIPELAGO_NOSTR_RELAYS") {
            config.nostr_relays = v
                .split(',')
                .map(|s| s.trim().to_string())
                .filter(|s| !s.is_empty())
                .collect();
        }
        if let Ok(v) = std::env::var("ARCHIPELAGO_NOSTR_TOR_PROXY") {
            let s = v.trim().to_string();
            config.nostr_tor_proxy = if s.is_empty() { None } else { Some(s) };
        }

        // Host IP for container env vars (detect if not set)
        if let Ok(ip) = std::env::var("ARCHIPELAGO_HOST_IP") {
            config.host_ip = ip;
        } else {
            config.host_ip = Self::detect_host_ip().unwrap_or_else(|_| "127.0.0.1".to_string());
        }

        // Ensure data directory exists
        fs::create_dir_all(&config.data_dir).await
            .context("Failed to create data directory")?;

        // Ensure dev data directory exists if in dev mode
        if config.dev_mode {
            fs::create_dir_all(&config.dev_data_dir).await
                .context("Failed to create dev data directory")?;
        }

        Ok(config)
    }
}

impl Default for Config {
    fn default() -> Self {
        Self {
            data_dir: PathBuf::from("/var/lib/archipelago"),
            bind_host: "0.0.0.0".to_string(),
            bind_port: 5678,
            log_level: "info".to_string(),
            host_ip: "127.0.0.1".to_string(),
            dev_mode: false,
            container_runtime: ContainerRuntime::Auto,
            port_offset: 10000,
            bitcoin_simulation: BitcoinSimulation::Mock,
            dev_data_dir: PathBuf::from("/tmp/archipelago-dev"),
            nostr_discovery_enabled: true,
            nostr_relays: vec![
                "wss://relay.damus.io".into(),
                "wss://relay.nostr.info".into(),
            ],
            nostr_tor_proxy: Some("127.0.0.1:9050".into()),
        }
    }
}
