//! Transport abstraction for chat platforms.
//!
//! The [`ChatTransport`] trait defines a platform-agnostic interface for
//! sending and editing messages, allowing the bot logic (commands, htop,
//! notifications) to work against any chat platform — Matrix, WhatsApp, etc.

/// Bot command registry and dispatch — parses and routes incoming chat messages.
pub mod commands;
/// `compact` command: deterministic session-transcript distillation and seeding.
pub mod compact;
/// Protocol-agnostic chat dispatcher — coalesce window and per-session serial lock.
pub mod dispatcher;
/// Chat history utilities — loading and serialising conversation history.
pub mod history;
pub(crate) mod lookup;
#[cfg(test)]
pub(crate) mod test_helpers;
/// Rate-limit retry timers — stores and fires scheduled retry reminders.
pub mod timer;
/// Platform transports — pluggable backends (Matrix, Slack, WhatsApp, Discord).
pub mod transport;
/// Chat utility functions — shared helpers for message formatting and bot logic.
pub mod util;

use async_trait::async_trait;

/// A platform-agnostic identifier for a sent message.
///
/// On Matrix this is the event ID; on other platforms it may be a message ID
/// or similar opaque string.  The transport implementation is responsible for
/// producing and consuming these identifiers.
pub type MessageId = String;

/// Abstraction over a chat platform's message-sending capabilities.
///
/// Implementations must be `Send + Sync` so they can be shared across
/// async tasks via `Arc<dyn ChatTransport>`.
#[async_trait]
pub trait ChatTransport: Send + Sync {
    /// Send a plain-text + HTML message to a room.
    ///
    /// Returns the platform-specific message ID on success so it can be
    /// referenced later (e.g. for edits or reply detection).
    async fn send_message(
        &self,
        room_id: &str,
        plain: &str,
        html: &str,
    ) -> Result<MessageId, String>;

    /// Edit a previously sent message.
    ///
    /// `original_message_id` is the [`MessageId`] returned by a prior
    /// [`send_message`](ChatTransport::send_message) call.
    ///
    /// Platforms that do not support editing (e.g. WhatsApp) should send a
    /// new message instead.
    async fn edit_message(
        &self,
        room_id: &str,
        original_message_id: &str,
        plain: &str,
        html: &str,
    ) -> Result<(), String>;

    /// Signal that the bot is typing (or has stopped typing) in a room.
    ///
    /// Platforms that do not support typing indicators should no-op.
    async fn send_typing(&self, room_id: &str, typing: bool) -> Result<(), String>;
}

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

    /// Verify that WhatsAppTransport satisfies the ChatTransport trait and
    /// can be used as `Arc<dyn ChatTransport>` (compile-time check).
    /// Functional tests are in `whatsapp::tests` using mockito.
    #[test]
    fn whatsapp_transport_satisfies_trait() {
        fn assert_transport<T: ChatTransport>() {}
        assert_transport::<crate::chat::transport::whatsapp::WhatsAppTransport>();

        // Verify it can be wrapped in Arc<dyn ChatTransport>.
        let _: Arc<dyn ChatTransport> =
            Arc::new(crate::chat::transport::whatsapp::WhatsAppTransport::new(
                "test-phone".to_string(),
                "test-token".to_string(),
                "pipeline_notification".to_string(),
            ));
    }

    /// MatrixTransport cannot be tested without a live homeserver, but we
    /// can verify the type implements the trait at compile time.
    #[test]
    fn matrix_transport_is_send_sync() {
        fn assert_send_sync<T: Send + Sync>() {}
        assert_send_sync::<crate::chat::transport::matrix::transport_impl::MatrixTransport>();
    }

    /// Verify that SlackTransport satisfies the ChatTransport trait and
    /// can be used as `Arc<dyn ChatTransport>` (compile-time check).
    #[test]
    fn slack_transport_satisfies_trait() {
        fn assert_transport<T: ChatTransport>() {}
        assert_transport::<crate::chat::transport::slack::SlackTransport>();

        let _: Arc<dyn ChatTransport> = Arc::new(
            crate::chat::transport::slack::SlackTransport::new("xoxb-test".to_string()),
        );
    }

    /// Verify that TwilioWhatsAppTransport satisfies the ChatTransport trait
    /// and can be used as `Arc<dyn ChatTransport>` (compile-time check).
    #[test]
    fn twilio_transport_satisfies_trait() {
        fn assert_transport<T: ChatTransport>() {}
        assert_transport::<crate::chat::transport::whatsapp::TwilioWhatsAppTransport>();

        let _: Arc<dyn ChatTransport> = Arc::new(
            crate::chat::transport::whatsapp::TwilioWhatsAppTransport::new(
                "ACtest".to_string(),
                "authtoken".to_string(),
                "+14155551234".to_string(),
            ),
        );
    }
}
