use anyhow::{Context, Result};
use reqwest::Client;
use serde::Serialize;

const PUSHBULLET_API_URL: &str = "https://api.pushbullet.com/v2/pushes";

#[derive(Debug, Serialize)]
struct NotePush {
    #[serde(rename = "type")]
    push_type: String,
    title: String,
    body: String,
}

#[derive(Debug, Serialize)]
struct LinkPush {
    #[serde(rename = "type")]
    push_type: String,
    title: String,
    body: String,
    url: String,
}

/// Pushbullet notification service.
///
/// Sends push notifications to server administrators via the Pushbullet
/// API. Used as a secondary notification channel alongside Discord for
/// critical alerts that need to reach admins on their mobile devices.
pub struct PushbulletNotifier {
    http: Client,
    api_key: String,
}

impl PushbulletNotifier {
    pub fn new(api_key: String) -> Self {
        Self {
            http: Client::new(),
            api_key,
        }
    }

    /// Send a text notification (note type push).
    pub async fn send_notification(&self, title: &str, body: &str) -> Result<()> {
        let payload = NotePush {
            push_type: "note".to_string(),
            title: title.to_string(),
            body: body.to_string(),
        };

        let response = self
            .http
            .post(PUSHBULLET_API_URL)
            .header("Access-Token", &self.api_key)
            .json(&payload)
            .send()
            .await
            .context("Failed to send Pushbullet notification")?;

        if !response.status().is_success() {
            let status = response.status();
            let body = response.text().await.unwrap_or_default();
            tracing::error!("Pushbullet push failed: {} — {}", status, body);
            anyhow::bail!("Pushbullet returned {status}");
        }

        Ok(())
    }

    /// Send a link notification with a clickable URL.
    pub async fn send_link(&self, title: &str, body: &str, url: &str) -> Result<()> {
        let payload = LinkPush {
            push_type: "link".to_string(),
            title: title.to_string(),
            body: body.to_string(),
            url: url.to_string(),
        };

        let response = self
            .http
            .post(PUSHBULLET_API_URL)
            .header("Access-Token", &self.api_key)
            .json(&payload)
            .send()
            .await
            .context("Failed to send Pushbullet link push")?;

        if !response.status().is_success() {
            let status = response.status();
            let body = response.text().await.unwrap_or_default();
            tracing::error!("Pushbullet push failed: {} — {}", status, body);
            anyhow::bail!("Pushbullet returned {status}");
        }

        Ok(())
    }

    /// Send a wipe-starting notification.
    pub async fn send_wipe_start(&self, server_name: &str, wipe_type: &str) -> Result<()> {
        self.send_notification(
            &format!("🔄 {} — Wipe Starting", server_name),
            &format!("{} wipe is beginning. Server will be offline briefly.", wipe_type),
        )
        .await
    }

    /// Send a wipe-completed notification.
    pub async fn send_wipe_complete(&self, server_name: &str, wipe_type: &str) -> Result<()> {
        self.send_notification(
            &format!("✅ {} — Wipe Complete", server_name),
            &format!("{} wipe completed. Server is back online.", wipe_type),
        )
        .await
    }

    /// Send a wipe-failed notification.
    pub async fn send_wipe_failed(&self, server_name: &str, error: &str) -> Result<()> {
        self.send_notification(
            &format!("❌ {} — Wipe Failed", server_name),
            &format!("Wipe failed: {}", error),
        )
        .await
    }

    /// Send a crash alert.
    pub async fn send_crash_alert(
        &self,
        server_name: &str,
        crash_count: u32,
        auto_recovered: bool,
    ) -> Result<()> {
        let title = if auto_recovered {
            format!("⚠️ {} — Crash Recovered", server_name)
        } else {
            format!("🔴 {} — Crash — Manual Action Needed", server_name)
        };

        let body = if auto_recovered {
            format!("Server crashed and was auto-restarted (attempt {}).", crash_count)
        } else {
            format!(
                "Server crashed {} times. Auto-recovery exhausted. Check the server.",
                crash_count
            )
        };

        self.send_notification(&title, &body).await
    }
}
