use async_trait::async_trait;
use std::sync::Arc;

use crate::bus::{BusError, InboundMessage, MessageBus, OutboundMessage};

#[derive(Debug)]
pub enum ChannelError {
    ConfigError(String),
    ConnectionError(String),
    SendError(String),
    BusError(String),
    Other(String),
}

impl std::fmt::Display for ChannelError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ChannelError::ConfigError(s) => write!(f, "Config error: {}", s),
            ChannelError::ConnectionError(s) => write!(f, "Connection error: {}", s),
            ChannelError::SendError(s) => write!(f, "Send error: {}", s),
            ChannelError::BusError(s) => write!(f, "Bus error: {}", s),
            ChannelError::Other(s) => write!(f, "Error: {}", s),
        }
    }
}

impl std::error::Error for ChannelError {}

impl From<BusError> for ChannelError {
    fn from(e: BusError) -> Self {
        ChannelError::BusError(e.to_string())
    }
}

#[async_trait]
pub trait Channel: Send + Sync + 'static {
    fn name(&self) -> &str;
    fn is_running(&self) -> bool;

    /// Start the channel with a reference to the MessageBus
    async fn start(&self, bus: Arc<MessageBus>) -> Result<(), ChannelError>;

    /// Stop the channel
    async fn stop(&self) -> Result<(), ChannelError>;

    /// Send a message to the channel (called by gateway outbound dispatch)
    async fn send(&self, msg: OutboundMessage) -> Result<(), ChannelError>;

    /// Send a streaming delta (optional, for channels that support it)
    async fn send_delta(&self, chat_id: &str, delta: &str) -> Result<(), ChannelError> {
        let _ = chat_id;
        let _ = delta;
        Ok(())
    }

    /// Check if a sender is allowed to use this channel
    fn is_allowed(&self, _sender_id: &str) -> bool {
        true
    }

    /// Handle an inbound message: check permissions and publish to bus
    async fn handle_and_publish(
        &self,
        bus: &Arc<MessageBus>,
        msg: &InboundMessage,
    ) -> Result<(), ChannelError> {
        if !self.is_allowed(&msg.sender_id) {
            tracing::warn!(
                channel = %self.name(),
                sender = %msg.sender_id,
                "Access denied"
            );
            return Ok(());
        }

        bus.publish_inbound(msg.clone()).await?;
        Ok(())
    }
}
