use std::sync::Arc;
use crate::state::AppState;

pub async fn fire_event(state: &Arc<AppState>, event: &str, payload: serde_json::Value) {
    let dbi = state.db.lock().await;
    let hooks = crate::db::get_enabled_webhooks(&dbi.db, event);
    drop(dbi);

    for (id, url, secret, format) in hooks {
        if crate::security::is_internal_url(&url) {
            tracing::warn!("skipping webhook to internal URL: {}", url);
            continue;
        }
        let body = match format.as_str() {
            "discord" => format_discord(event, &payload),
            "slack" => format_slack(event, &payload),
            _ => serde_json::json!({ "event": event, "data": payload }).to_string(),
        };

        let client = reqwest::Client::builder()
            .redirect(crate::security::safe_redirect_policy())
            .build()
            .unwrap_or_default();
        let mut req = client.post(&url)
            .header("Content-Type", "application/json")
            .header("X-Cruciverb-Event", event);

        if let Some(ref sec) = secret {
            use sha2::{Sha256, Digest};
            let sig = hex::encode(Sha256::digest(format!("{}{}", body, sec).as_bytes()));
            req = req.header("X-Cruciverb-Signature", sig);
        }

        let state_clone = state.clone();
        let id_clone = id.clone();
        tokio::spawn(async move {
            match req.body(body).send().await {
                Ok(res) if res.status().is_success() => {
                    if crate::security::is_internal_url(res.url().as_str()) {
                        tracing::warn!("skipping webhook delivery to internal URL after redirect: {}", res.url());
                    }
                }
                _ => {
                    let dbi = state_clone.db.lock().await;
                    crate::db::record_webhook_failure(&dbi.db, &id_clone);
                }
            }
        });
    }
}

fn format_discord(event: &str, payload: &serde_json::Value) -> String {
    let title = match event {
        "daily_puzzle.published" => "New daily puzzle",
        "puzzle.completed" => "Puzzle completed",
        "achievement.unlocked" => "Achievement unlocked",
        _ => event,
    };
    let desc = payload.get("description").and_then(|v| v.as_str()).unwrap_or("");
    serde_json::json!({
        "embeds": [{
            "title": title,
            "description": desc,
            "color": 13848143,
        }]
    }).to_string()
}

fn format_slack(event: &str, payload: &serde_json::Value) -> String {
    let desc = payload.get("description").and_then(|v| v.as_str()).unwrap_or(event);
    serde_json::json!({
        "text": desc,
        "blocks": [{
            "type": "section",
            "text": { "type": "mrkdwn", "text": format!("*{}*\n{}", event, desc) }
        }]
    }).to_string()
}
