use std::time::Duration;

use anyhow::{Context, Result};
use discord_webhook2::message::Message;
use discord_webhook2::webhook::DiscordWebhook;
use tracing::error;

const COLOR_RED: u32 = 0xE74C3C;
const COLOR_GREEN: u32 = 0x2ECC71;
const COLOR_PURPLE: u32 = 0x9B59B6;

pub async fn build_started(
    url: &str,
    branch: &str,
    name: Option<&str>,
    old_sha: Option<&str>,
    new_sha: &str,
    subject: &str,
) {
    if let Err(e) = try_build_started(url, branch, name, old_sha, new_sha, subject).await {
        error!(branch = %branch, "discord notification failed: {e}");
    }
}

pub async fn build_succeeded(
    url: &str,
    branch: &str,
    name: Option<&str>,
    sha: &str,
    subject: &str,
    elapsed: Duration,
) {
    if let Err(e) = try_build_succeeded(url, branch, name, sha, subject, elapsed).await {
        error!(branch = %branch, "discord notification failed: {e}");
    }
}

pub async fn build_failed(
    url: &str,
    branch: &str,
    name: Option<&str>,
    sha: &str,
    subject: &str,
    elapsed: Duration,
) {
    if let Err(e) = try_build_failed(url, branch, name, sha, subject, elapsed).await {
        error!(branch = %branch, "discord notification failed: {e}");
    }
}

fn format_title(name: Option<&str>, base: &str) -> String {
    match name {
        Some(n) => format!("[{n}] {base}"),
        None => base.to_string(),
    }
}

async fn try_build_started(
    url: &str,
    branch: &str,
    name: Option<&str>,
    old_sha: Option<&str>,
    new_sha: &str,
    subject: &str,
) -> Result<()> {
    let webhook = DiscordWebhook::new(url).context("invalid webhook url")?;

    let title = format_title(name, "Build Started");
    let description = match old_sha {
        Some(old) => format!(
            "Building on `{branch}`\n`{}` → `{}` — {subject}",
            old.get(..8).unwrap_or(old),
            new_sha.get(..8).unwrap_or(new_sha)
        ),
        None => format!(
            "Building on `{branch}`\n`{}` — {subject}",
            new_sha.get(..8).unwrap_or(new_sha)
        ),
    };

    let message = Message::new(|m| {
        m.embed(|e| {
            e.title(title)
                .description(description)
                .color(COLOR_PURPLE)
        })
    });

    webhook.send(&message).await.context("failed to send webhook")?;
    Ok(())
}

async fn try_build_succeeded(
    url: &str,
    branch: &str,
    name: Option<&str>,
    sha: &str,
    subject: &str,
    elapsed: Duration,
) -> Result<()> {
    let webhook = DiscordWebhook::new(url).context("invalid webhook url")?;

    let title = format_title(name, "Build Succeeded");
    let description = format!(
        "Build completed on `{branch}` in {:.1}s",
        elapsed.as_secs_f64()
    );
    let footer_text = format!("{} — {subject}", sha.get(..8).unwrap_or(sha));
    let message = Message::new(|m| {
        m.embed(|e| {
            e.title(title)
                .description(description)
                .color(COLOR_GREEN)
                .footer(|f| f.text(footer_text))
        })
    });

    webhook.send(&message).await.context("failed to send webhook")?;
    Ok(())
}

async fn try_build_failed(
    url: &str,
    branch: &str,
    name: Option<&str>,
    sha: &str,
    subject: &str,
    elapsed: Duration,
) -> Result<()> {
    let webhook = DiscordWebhook::new(url).context("invalid webhook url")?;

    let title = format_title(name, "Build Failed");
    let description = format!(
        "Build failed on `{branch}` after {:.1}s",
        elapsed.as_secs_f64()
    );
    let footer_text = format!("{} — {subject}", sha.get(..8).unwrap_or(sha));
    let message = Message::new(|m| {
        m.embed(|e| {
            e.title(title)
                .description(description)
                .color(COLOR_RED)
                .footer(|f| f.text(footer_text))
        })
    });

    webhook.send(&message).await.context("failed to send webhook")?;
    Ok(())
}
