use crate::{
    controllers::site_settings,
    models::_entities::{comments, friend_links, site_settings as site_settings_model},
    services::subscriptions,
};
use loco_rs::prelude::*;

fn notification_channel_type(settings: &site_settings_model::Model) -> &'static str {
    match settings
        .notification_channel_type
        .as_deref()
        .map(str::trim)
        .map(str::to_ascii_lowercase)
        .as_deref()
    {
        Some("ntfy") => subscriptions::CHANNEL_NTFY,
        _ => subscriptions::CHANNEL_WEBHOOK,
    }
}

fn trim_to_option(value: Option<String>) -> Option<String> {
    value.and_then(|item| {
        let trimmed = item.trim().to_string();
        if trimmed.is_empty() {
            None
        } else {
            Some(trimmed)
        }
    })
}

fn excerpt(value: Option<&str>, limit: usize) -> Option<String> {
    let flattened = value?
        .split_whitespace()
        .collect::<Vec<_>>()
        .join(" ")
        .trim()
        .to_string();

    if flattened.is_empty() {
        return None;
    }

    let mut shortened = flattened.chars().take(limit).collect::<String>();
    if flattened.chars().count() > limit {
        shortened.push_str("...");
    }
    Some(shortened)
}

pub async fn notify_new_comment(ctx: &AppContext, item: &comments::Model) {
    let settings = match site_settings::load_current(ctx).await {
        Ok(settings) => settings,
        Err(error) => {
            tracing::warn!("failed to load site settings before comment notification: {error}");
            return;
        }
    };

    let payload = serde_json::json!({
        "event_type": subscriptions::EVENT_COMMENT_CREATED,
        "id": item.id,
        "post_slug": item.post_slug,
        "author": item.author,
        "email": item.email,
        "scope": item.scope,
        "paragraph_key": item.paragraph_key,
        "approved": item.approved.unwrap_or(false),
        "excerpt": excerpt(item.content.as_deref(), 200),
        "created_at": item.created_at.to_rfc3339(),
    });
    let text = format!(
        "收到一条新的评论。\n\n文章：{}\n作者：{}\n范围：{}\n状态：{}\n摘要：{}",
        item.post_slug
            .clone()
            .unwrap_or_else(|| "未知文章".to_string()),
        item.author.clone().unwrap_or_else(|| "匿名".to_string()),
        item.scope,
        if item.approved.unwrap_or(false) {
            "已通过"
        } else {
            "待审核"
        },
        excerpt(item.content.as_deref(), 200).unwrap_or_else(|| "无".to_string()),
    );

    if let Err(error) = subscriptions::queue_event_for_active_subscriptions(
        ctx,
        subscriptions::EVENT_COMMENT_CREATED,
        "新评论通知",
        &text,
        payload.clone(),
        trim_to_option(settings.site_name.clone()),
        trim_to_option(settings.site_url.clone()),
    )
    .await
    {
        tracing::warn!("failed to queue comment subscription notification: {error}");
    }

    if settings.notification_comment_enabled.unwrap_or(false) {
        if let Some(target) = trim_to_option(settings.notification_webhook_url.clone()) {
            let channel_type = notification_channel_type(&settings);
            if let Err(error) = subscriptions::queue_direct_notification(
                ctx,
                channel_type,
                &target,
                subscriptions::EVENT_COMMENT_CREATED,
                "新评论通知",
                &text,
                payload,
                trim_to_option(settings.site_name),
                trim_to_option(settings.site_url),
            )
            .await
            {
                tracing::warn!("failed to queue comment admin notification: {error}");
            }
        }
    }
}

pub async fn notify_new_friend_link(ctx: &AppContext, item: &friend_links::Model) {
    let settings = match site_settings::load_current(ctx).await {
        Ok(settings) => settings,
        Err(error) => {
            tracing::warn!("failed to load site settings before friend-link notification: {error}");
            return;
        }
    };

    let payload = serde_json::json!({
        "event_type": subscriptions::EVENT_FRIEND_LINK_CREATED,
        "id": item.id,
        "site_name": item.site_name,
        "site_url": item.site_url,
        "category": item.category,
        "status": item.status,
        "description": item.description,
        "created_at": item.created_at.to_rfc3339(),
    });
    let text = format!(
        "收到新的友链申请。\n\n站点：{}\n链接：{}\n分类：{}\n状态：{}\n描述：{}",
        item.site_name
            .clone()
            .unwrap_or_else(|| "未命名站点".to_string()),
        item.site_url,
        item.category
            .clone()
            .unwrap_or_else(|| "未分类".to_string()),
        item.status.clone().unwrap_or_else(|| "pending".to_string()),
        item.description.clone().unwrap_or_else(|| "无".to_string()),
    );

    if let Err(error) = subscriptions::queue_event_for_active_subscriptions(
        ctx,
        subscriptions::EVENT_FRIEND_LINK_CREATED,
        "新友链申请通知",
        &text,
        payload.clone(),
        trim_to_option(settings.site_name.clone()),
        trim_to_option(settings.site_url.clone()),
    )
    .await
    {
        tracing::warn!("failed to queue friend-link subscription notification: {error}");
    }

    if settings.notification_friend_link_enabled.unwrap_or(false) {
        if let Some(target) = trim_to_option(settings.notification_webhook_url.clone()) {
            let channel_type = notification_channel_type(&settings);
            if let Err(error) = subscriptions::queue_direct_notification(
                ctx,
                channel_type,
                &target,
                subscriptions::EVENT_FRIEND_LINK_CREATED,
                "新友链申请通知",
                &text,
                payload,
                trim_to_option(settings.site_name),
                trim_to_option(settings.site_url),
            )
            .await
            {
                tracing::warn!("failed to queue friend-link admin notification: {error}");
            }
        }
    }
}
