use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

use crate::bus::MessageBus;
use crate::channels::base::{Channel, ChannelError};
use crate::channels::feishu::FeishuChannel;
use crate::config::Config;

/// ChannelManager manages all Channel instances and the MessageBus
#[derive(Clone)]
pub struct ChannelManager {
    channels: Arc<RwLock<HashMap<String, Arc<dyn Channel + Send + Sync>>>>,
    cli_chat_channel: Arc<crate::channels::CliChatChannel>,
    bus: Arc<MessageBus>,
}

impl ChannelManager {
    pub fn with_bus(
        cli_chat_channel: Arc<crate::channels::CliChatChannel>,
        bus: Arc<MessageBus>,
    ) -> Self {
        Self {
            channels: Arc::new(RwLock::new(HashMap::new())),
            cli_chat_channel,
            bus,
        }
    }

    /// Get a reference to the MessageBus
    pub fn bus(&self) -> Arc<MessageBus> {
        self.bus.clone()
    }

    /// Register a channel with the manager
    pub async fn register_channel(&self, name: &str, channel: Arc<dyn Channel + Send + Sync>) {
        self.channels
            .write()
            .await
            .insert(name.to_string(), channel);
    }

    /// Get CLI chat channel
    pub fn cli_chat_channel(&self) -> Arc<crate::channels::CliChatChannel> {
        self.cli_chat_channel.clone()
    }

    /// Initialize all Channel instances from config
    pub async fn init(
        &self,
        config: &Config,
        workspace_dir: std::path::PathBuf,
    ) -> Result<(), ChannelError> {
        // Initialize Feishu channel if enabled
        if let Some(feishu_config) = config.channels.get("feishu") {
            if feishu_config.enabled {
                let channel =
                    FeishuChannel::new(feishu_config.clone(), &workspace_dir).map_err(|e| {
                        ChannelError::Other(format!("Failed to create Feishu channel: {}", e))
                    })?;

                self.channels
                    .write()
                    .await
                    .insert("feishu".to_string(), Arc::new(channel));
                tracing::info!(
                    "Feishu channel registered (media_dir: {}/media/feishu)",
                    workspace_dir.display()
                );
            } else {
                tracing::info!("Feishu channel disabled in config");
            }
        }

        // Register CLI chat channel
        self.channels
            .write()
            .await
            .insert("cli_chat".to_string(), self.cli_chat_channel.clone());
        tracing::info!("CLI chat channel registered");

        Ok(())
    }

    pub async fn start_all(&self) -> Result<(), ChannelError> {
        let channels: Vec<_> = self
            .channels
            .read()
            .await
            .iter()
            .map(|(name, channel)| (name.clone(), channel.clone()))
            .collect();
        let bus = self.bus.clone();
        let mut failures = Vec::new();
        for (name, channel) in channels {
            tracing::info!(channel = %name, "Starting channel");
            if let Err(e) = channel.start(bus.clone()).await {
                tracing::error!(channel = %name, error = %e, "Failed to start channel");
                failures.push(format!("{name}: {e}"));
            }
        }
        if failures.is_empty() {
            Ok(())
        } else {
            Err(ChannelError::Other(format!(
                "failed to start channels: {}",
                failures.join("; ")
            )))
        }
    }

    pub async fn stop_all(&self) -> Result<(), ChannelError> {
        let channels: Vec<_> = {
            let mut registered = self.channels.write().await;
            registered.drain().collect()
        };
        let mut failures = Vec::new();
        for (name, channel) in channels {
            tracing::info!(channel = %name, "Stopping channel");
            if let Err(e) = channel.stop().await {
                tracing::error!(channel = %name, error = %e, "Error stopping channel");
                failures.push(format!("{name}: {e}"));
            }
        }
        if failures.is_empty() {
            Ok(())
        } else {
            Err(ChannelError::Other(format!(
                "failed to stop channels: {}",
                failures.join("; ")
            )))
        }
    }

    pub async fn get_channel(&self, name: &str) -> Option<Arc<dyn Channel + Send + Sync>> {
        self.channels.read().await.get(name).cloned()
    }

    /// Get list of registered channel names
    pub async fn list_channel_names(&self) -> Vec<String> {
        self.channels.read().await.keys().cloned().collect()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use async_trait::async_trait;
    use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

    struct TestChannel {
        name: &'static str,
        fail_start: bool,
        fail_stop: bool,
        running: AtomicBool,
        starts: AtomicUsize,
        stops: AtomicUsize,
    }

    impl TestChannel {
        fn new(name: &'static str, fail_start: bool, fail_stop: bool) -> Self {
            Self {
                name,
                fail_start,
                fail_stop,
                running: AtomicBool::new(false),
                starts: AtomicUsize::new(0),
                stops: AtomicUsize::new(0),
            }
        }
    }

    #[async_trait]
    impl Channel for TestChannel {
        fn name(&self) -> &str {
            self.name
        }

        fn is_running(&self) -> bool {
            self.running.load(Ordering::SeqCst)
        }

        async fn start(&self, _bus: Arc<MessageBus>) -> Result<(), ChannelError> {
            self.starts.fetch_add(1, Ordering::SeqCst);
            if self.fail_start {
                return Err(ChannelError::ConnectionError("unavailable".into()));
            }
            self.running.store(true, Ordering::SeqCst);
            Ok(())
        }

        async fn stop(&self) -> Result<(), ChannelError> {
            self.stops.fetch_add(1, Ordering::SeqCst);
            self.running.store(false, Ordering::SeqCst);
            if self.fail_stop {
                return Err(ChannelError::Other("stop failed".into()));
            }
            Ok(())
        }

        async fn send(&self, _msg: crate::bus::OutboundMessage) -> Result<(), ChannelError> {
            Ok(())
        }
    }

    fn manager() -> ChannelManager {
        ChannelManager::with_bus(
            Arc::new(crate::channels::CliChatChannel::new()),
            MessageBus::new(8),
        )
    }

    #[tokio::test]
    async fn start_all_reports_channel_failures_and_starts_the_rest() {
        let manager = manager();
        let healthy = Arc::new(TestChannel::new("healthy", false, false));
        let broken = Arc::new(TestChannel::new("broken", true, false));
        manager.register_channel("healthy", healthy.clone()).await;
        manager.register_channel("broken", broken.clone()).await;

        let error = manager.start_all().await.unwrap_err().to_string();

        assert!(error.contains("broken"));
        assert_eq!(healthy.starts.load(Ordering::SeqCst), 1);
        assert_eq!(broken.starts.load(Ordering::SeqCst), 1);
    }

    #[tokio::test]
    async fn stop_all_reports_failures_and_unregisters_every_channel() {
        let manager = manager();
        let healthy = Arc::new(TestChannel::new("healthy", false, false));
        let broken = Arc::new(TestChannel::new("broken", false, true));
        manager.register_channel("healthy", healthy.clone()).await;
        manager.register_channel("broken", broken.clone()).await;

        let error = manager.stop_all().await.unwrap_err().to_string();

        assert!(error.contains("broken"));
        assert_eq!(healthy.stops.load(Ordering::SeqCst), 1);
        assert_eq!(broken.stops.load(Ordering::SeqCst), 1);
        assert!(manager.list_channel_names().await.is_empty());
    }
}
