use deployer_types::NodeInfo;
use std::collections::HashMap;
use tokio::sync::{Mutex, RwLock};
use tracing::{info, warn};

use crate::config::Config;
use crate::local_storage::LocalStorage;
use crate::storage::NodeStorage;

/// Application state shared across handlers
pub struct AppState {
    /// Server configuration
    pub config: Config,

    /// ChainFire-backed storage (when available)
    pub storage: Option<Mutex<NodeStorage>>,

    /// Local file-backed storage (when configured)
    pub local_storage: Option<Mutex<LocalStorage>>,

    /// Fallback in-memory node registry
    /// Key: node_id, Value: NodeInfo
    pub nodes: RwLock<HashMap<String, NodeInfo>>,

    /// Fallback in-memory machine_id → bootstrap node config mapping
    pub machine_configs: RwLock<HashMap<String, deployer_types::NodeConfig>>,
}

impl AppState {
    /// Create new application state with default config
    pub fn new() -> Self {
        Self::with_config(Config::default())
    }

    /// Create application state with custom config
    pub fn with_config(config: Config) -> Self {
        Self {
            config,
            storage: None,
            local_storage: None,
            nodes: RwLock::new(HashMap::new()),
            machine_configs: RwLock::new(HashMap::new()),
        }
    }

    /// Initialize ChainFire storage connection
    pub async fn init_storage(&mut self) -> anyhow::Result<()> {
        if let Some(path) = self.config.local_state_path.clone() {
            match LocalStorage::open(path) {
                Ok(storage) => {
                    info!("Local storage initialized for Deployer bootstrapper");
                    self.local_storage = Some(Mutex::new(storage));
                }
                Err(e) => {
                    warn!(error = %e, "Failed to initialize local storage");
                }
            }
        }

        if self.config.chainfire.endpoints.is_empty() {
            if self.config.require_chainfire {
                return Err(anyhow::anyhow!(
                    "No ChainFire endpoints configured while require_chainfire=true"
                ));
            }
            warn!("No ChainFire endpoints configured, using in-memory storage");
            return Ok(());
        }

        let namespace = &self.config.chainfire.namespace;

        for endpoint in &self.config.chainfire.endpoints {
            match NodeStorage::connect(endpoint, namespace).await {
                Ok(storage) => {
                    info!(
                        endpoint = %endpoint,
                        namespace = %namespace,
                        "Connected to ChainFire storage"
                    );
                    self.storage = Some(Mutex::new(storage));
                    return Ok(());
                }
                Err(e) => {
                    warn!(
                        endpoint = %endpoint,
                        error = %e,
                        "Failed to connect to ChainFire endpoint"
                    );
                }
            }
        }

        if self.config.require_chainfire {
            Err(anyhow::anyhow!(
                "Failed to connect to any ChainFire endpoints and require_chainfire=true"
            ))
        } else {
            warn!("Failed to connect to any ChainFire endpoints, using in-memory storage");
            Ok(())
        }
    }

    /// Check if ChainFire storage is available
    pub fn has_storage(&self) -> bool {
        self.storage.is_some()
    }

    /// Check if local storage is available
    pub fn has_local_storage(&self) -> bool {
        self.local_storage.is_some()
    }
}

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