use std::pin::Pin;
use std::sync::Arc;

use async_trait::async_trait;
use futures::Stream;
use garudust_core::{
    error::PlatformError,
    platform::{MessageHandler, PlatformAdapter},
    types::{ChannelId, InboundMessage, OutboundMessage},
};
use teloxide::prelude::*;

pub struct TelegramAdapter {
    bot: Bot,
}

impl TelegramAdapter {
    pub fn new(token: String) -> Self {
        Self {
            bot: Bot::new(token),
        }
    }
}

#[async_trait]
impl PlatformAdapter for TelegramAdapter {
    fn name(&self) -> &'static str {
        "telegram"
    }

    async fn start(&self, handler: Arc<dyn MessageHandler>) -> Result<(), PlatformError> {
        let bot = self.bot.clone();
        tokio::spawn(async move {
            teloxide::repl(bot, move |_bot: Bot, msg: Message| {
                let handler = handler.clone();
                async move {
                    if let Some(text) = msg.text() {
                        let is_group = msg.chat.is_group() || msg.chat.is_supergroup();
                        let inbound = InboundMessage {
                            channel: ChannelId {
                                platform: "telegram".into(),
                                chat_id: msg.chat.id.to_string(),
                                thread_id: None,
                            },
                            user_id: msg
                                .from
                                .as_ref()
                                .map(|u| u.id.to_string())
                                .unwrap_or_default(),
                            user_name: msg
                                .from
                                .as_ref()
                                .and_then(|u| u.username.clone())
                                .unwrap_or_default(),
                            text: text.to_string(),
                            session_key: format!("telegram:{}", msg.chat.id),
                            is_group,
                        };
                        let _ = handler.handle(inbound).await;
                    }
                    respond(())
                }
            })
            .await;
        });
        Ok(())
    }

    async fn send_message(
        &self,
        channel: &ChannelId,
        message: OutboundMessage,
    ) -> Result<(), PlatformError> {
        let chat_id: i64 = channel
            .chat_id
            .parse()
            .map_err(|_| PlatformError::Send("invalid chat_id".into()))?;
        self.bot
            .send_message(ChatId(chat_id), &message.text)
            .await
            .map_err(|e| PlatformError::Send(e.to_string()))?;
        Ok(())
    }

    async fn send_stream(
        &self,
        channel: &ChannelId,
        mut stream: Pin<Box<dyn Stream<Item = String> + Send>>,
    ) -> Result<(), PlatformError> {
        use futures::StreamExt;
        let mut buf = String::new();
        while let Some(chunk) = stream.next().await {
            buf.push_str(&chunk);
        }
        self.send_message(channel, OutboundMessage::text(buf)).await
    }
}
