use std::{sync::Arc, time::SystemTime};

use super::{NotificationLevel, Notifier};
use async_trait::async_trait;
use base64;
use lazy_static::lazy_static;
use log::*;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::marker::Sync;

lazy_static! {
    static ref NOTIFIER: Arc<dyn Notifier + Send + Sync> = Arc::new(FeishuNotifier::default());
}

pub async fn critical(msg: &str, module: Option<&str>) {
    NOTIFIER.send_critical(msg, module).await;
}

pub async fn warn(msg: &str, module: Option<&str>) {
    NOTIFIER.send_warn(msg, module).await;
}

pub async fn notice(msg: &str, module: Option<&str>) {
    NOTIFIER.send_notice(msg, module).await;
}

pub async fn debug(msg: &str, module: Option<&str>) {
    NOTIFIER.send_debug(msg, module).await;
}

pub struct FeishuNotifier {
    webhook_url: Option<String>,
    secret: Option<String>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
struct FeishuResponse {
    code: i32,
    msg: String,
    data: serde_json::Value,
}

impl Default for FeishuNotifier {
    fn default() -> Self {
        let webhook_url = if let Ok(url) = std::env::var("FEISHU_WEBHOOK_URL") {
            Some(url)
        } else {
            None
        };

        let secret = if let Ok(secret) = std::env::var("FEISHU_SECRET") {
            Some(secret)
        } else {
            None
        };

        Self {
            webhook_url,
            secret,
        }
    }
}

#[async_trait]
impl Notifier for FeishuNotifier {
    async fn send(&self, level: NotificationLevel, message: &str, module: Option<&str>) {
        if self.webhook_url.is_none()
            || self.secret.is_none()
            || std::env::var("FEISHU_NOTICE").unwrap_or("false".into()) != "true"
        {
            warn!("Webhook URL or SECRET is not set or env::LOCALHOST is true. Skipping notification.");
            return;
        }

        let template = match level {
            NotificationLevel::Critical => "red",
            NotificationLevel::Warn => "yellow",
            NotificationLevel::Notice => "indigo",
            NotificationLevel::Debug => "default",
        };

        let region = std::env::var("AWS_REGION").unwrap_or("default".into());

        let timestamp = SystemTime::now()
            .duration_since(SystemTime::UNIX_EPOCH)
            .unwrap()
            .as_secs();
        let payload = json!({
            "timestamp": timestamp.to_string(),
            "sign": gen_sign(timestamp, self.secret.as_ref().unwrap()),
            "msg_type": "interactive",
            "card": {
                "elements": [{
                    "tag": "div",
                    "text": {
                            "content": message,
                            "tag": "lark_md"
                    }
                }],
                "header": {
                    "text_tag_list": [
                        {
                            "tag": "text_tag",
                            "text": {
                                "tag": "plain_text",
                                "content": region
                            }
                        },
                        {
                            "tag": "text_tag",
                            "text": {
                                "tag": "plain_text",
                                "content": module.unwrap_or_default()
                            }
                        }
                    ],
                    "template": template,
                    "title": {
                            "content": format!("{:?}", level),
                            "tag": "plain_text"
                    }
                }
            }

        });

        let res = match Client::new()
            .post(self.webhook_url.as_ref().unwrap())
            .header("Content-Type", "application/json")
            .json(&payload)
            .send()
            .await
        {
            Ok(res) => res,
            Err(e) => {
                error!("Feishu message error: {:?}", e);
                return;
            }
        };
        let res: FeishuResponse = match res.json().await {
            Ok(res) => res,
            Err(e) => {
                error!("Feishu message parse error: {:?}", e);
                return;
            }
        };

        if res.code != 0 {
            error!("Feishu message send error: {:?}", res);
        }
    }
}

fn gen_sign(timestamp: u64, secret: &str) -> String {
    let sign_str = format!("{}\n{}", timestamp, secret);
    let sign = solana_hmac_sha256::HmacSha256::new(sign_str.as_bytes()).finalize();

    base64::encode(sign)
}

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

    #[test]
    fn test_gen_sign() {
        let timestamp = 1622547800;
        let secret = "test_secret";
        let expected_sign = "aAq2J6B3um3pxYc31kTrNFXrzM/o/UbAkaWIUW6Qi4o=";
        let generated_sign = gen_sign(timestamp, secret);

        assert_eq!(generated_sign, expected_sign);
    }

    #[tokio::test]
    async fn test_send() {
        // notifier.send_critical("critical test").await;
        // notifier.send_warn("warn test").await;
        // notifier.send_notice("notice test").await;
        debug("debug test", Some("ctl")).await;

        assert!(true);
    }
}
