mod info;
mod message;

use anyhow::Result;
use futures::future;
pub use info::DownloadNotifyInfo;
pub use message::Message;
use reqwest::header;
use serde::{Deserialize, Serialize};

use crate::config::TEMPLATE;

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum Notifier {
    Telegram {
        bot_token: String,
        chat_id: String,
    },
    Webhook {
        url: String,
        template: Option<String>,
        #[serde(skip)]
        // 一个内部辅助字段，用于决定是否强制渲染当前模板，在测试时使用
        ignore_cache: Option<()>,
    },
}

pub fn webhook_template_key(url: &str) -> String {
    format!("payload_{}", url)
}

pub fn webhook_template_content(template: &Option<String>) -> &str {
    template
        .as_deref()
        .filter(|t| !t.trim().is_empty())
        .unwrap_or(r#"{"text": "{{{message}}}"}"#)
}

pub trait NotifierAllExt {
    async fn notify_all<'a>(&self, client: &reqwest::Client, message: impl Into<Message<'a>>) -> Result<()>;
}

impl NotifierAllExt for Vec<Notifier> {
    async fn notify_all<'a>(&self, client: &reqwest::Client, message: impl Into<Message<'a>>) -> Result<()> {
        let message = message.into();
        future::join_all(self.iter().map(|notifier| notifier.notify_internal(client, &message))).await;
        Ok(())
    }
}

impl Notifier {
    pub async fn notify<'a>(&self, client: &reqwest::Client, message: impl Into<Message<'a>>) -> Result<()> {
        self.notify_internal(client, &message.into()).await
    }

    async fn notify_internal<'a>(&self, client: &reqwest::Client, message: &Message<'a>) -> Result<()> {
        match self {
            Notifier::Telegram { bot_token, chat_id } => {
                if let Some(img_url) = &message.image_url {
                    let url = format!("https://api.telegram.org/bot{}/sendPhoto", bot_token);
                    let params = [
                        ("chat_id", chat_id.as_str()),
                        ("photo", img_url.as_str()),
                        ("caption", message.message.as_ref()),
                    ];
                    client.post(&url).form(&params).send().await?;
                } else {
                    let url = format!("https://api.telegram.org/bot{}/sendMessage", bot_token);
                    let params = [("chat_id", chat_id.as_str()), ("text", message.message.as_ref())];
                    client.post(&url).form(&params).send().await?;
                }
            }
            Notifier::Webhook {
                url,
                template,
                ignore_cache,
            } => {
                let key = webhook_template_key(url);
                let handlebar = TEMPLATE.read();
                let payload = match ignore_cache {
                    Some(_) => handlebar.render_template(webhook_template_content(template), &message)?,
                    None => handlebar.render(&key, &message)?,
                };
                client
                    .post(url)
                    .header(header::CONTENT_TYPE, "application/json")
                    .body(payload)
                    .send()
                    .await?;
            }
        }
        Ok(())
    }
}
