//! Matrix implementation of [`ChatTransport`].
//!
//! Wraps a [`matrix_sdk::Client`] and delegates message sending / editing
//! to the Matrix SDK.

use async_trait::async_trait;
use matrix_sdk::Client;
use matrix_sdk::ruma::OwnedRoomId;
use matrix_sdk::ruma::events::room::message::{
    ReplacementMetadata, RoomMessageEventContent, RoomMessageEventContentWithoutRelation,
};

use crate::chat::{ChatTransport, MessageId};

/// Matrix-backed [`ChatTransport`] implementation.
///
/// Holds a [`Client`] and resolves room IDs at send time.
pub struct MatrixTransport {
    client: Client,
}

impl MatrixTransport {
    /// Creates a new `MatrixTransport` wrapping the given Matrix SDK `Client`.
    pub fn new(client: Client) -> Self {
        Self { client }
    }
}

#[async_trait]
impl ChatTransport for MatrixTransport {
    async fn send_message(
        &self,
        room_id: &str,
        plain: &str,
        html: &str,
    ) -> Result<MessageId, String> {
        let room_id: OwnedRoomId = room_id
            .parse()
            .map_err(|e| format!("Invalid room ID '{room_id}': {e}"))?;
        let room = self
            .client
            .get_room(&room_id)
            .ok_or_else(|| format!("Room {room_id} not found in client state"))?;

        let content = RoomMessageEventContent::text_html(plain.to_string(), html.to_string());
        let resp = room
            .send(content)
            .await
            .map_err(|e| format!("Matrix send error: {e}"))?;

        Ok(resp.response.event_id.to_string())
    }

    async fn edit_message(
        &self,
        room_id: &str,
        original_message_id: &str,
        plain: &str,
        html: &str,
    ) -> Result<(), String> {
        let room_id: OwnedRoomId = room_id
            .parse()
            .map_err(|e| format!("Invalid room ID '{room_id}': {e}"))?;
        let room = self
            .client
            .get_room(&room_id)
            .ok_or_else(|| format!("Room {room_id} not found in client state"))?;

        let original_event_id = original_message_id
            .parse()
            .map_err(|e| format!("Invalid event ID '{original_message_id}': {e}"))?;

        let new_content =
            RoomMessageEventContentWithoutRelation::text_html(plain.to_string(), html.to_string());
        let metadata = ReplacementMetadata::new(original_event_id, None);
        let content = new_content.make_replacement(metadata);

        room.send(content)
            .await
            .map(|_| ())
            .map_err(|e| format!("Matrix edit error: {e}"))
    }

    async fn send_typing(&self, room_id: &str, typing: bool) -> Result<(), String> {
        let room_id: OwnedRoomId = room_id
            .parse()
            .map_err(|e| format!("Invalid room ID '{room_id}': {e}"))?;
        let room = self
            .client
            .get_room(&room_id)
            .ok_or_else(|| format!("Room {room_id} not found in client state"))?;

        room.typing_notice(typing)
            .await
            .map_err(|e| format!("Matrix typing indicator error: {e}"))
    }
}
