//! Notification GraphQL queries
//!
//! Queries for notifications and notification rules.

use async_graphql::{Context, Object, Result};
use uuid::Uuid;

use crate::auth::{ProfileType, UserContext};
use crate::db::Database;
use crate::graphql::types::{NotificationDeliveryType, NotificationRuleType, NotificationType};
use crate::models::{Notification, NotificationDelivery, NotificationRule};

#[derive(Default)]
pub struct NotificationQuery;

#[Object]
impl NotificationQuery {
    // ==================== USER NOTIFICATION QUERIES ====================

    /// Get a single notification by ID
    async fn notification(
        &self,
        ctx: &Context<'_>,
        id: Uuid,
    ) -> Result<Option<NotificationType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let notification = sqlx::query_as::<_, Notification>(
            r#"
            SELECT id, created_at, updated_at, recipient_type, recipient_id, rule_id, event_id,
                   status, subject, body, action_url, read_at, metadata
            FROM notifications
            WHERE id = $1
            "#,
        )
        .bind(id)
        .fetch_optional(&*pool)
        .await?;

        Ok(notification.map(NotificationType::from))
    }

    /// Get notifications for the current user
    async fn my_notifications(
        &self,
        ctx: &Context<'_>,
        #[graphql(default = false)] unread_only: bool,
        #[graphql(default = 50)] limit: i32,
        #[graphql(default = 0)] offset: i32,
    ) -> Result<Vec<NotificationType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;
        let user = ctx.data::<UserContext>()?;

        let recipient_type = match user.profile_type {
            ProfileType::Team => "team_profile",
            ProfileType::Customer => "customer_profile",
        };

        let notifications: Vec<Notification> = if unread_only {
            sqlx::query_as(
                r#"
                SELECT id, created_at, updated_at, recipient_type, recipient_id, rule_id, event_id,
                       status, subject, body, action_url, read_at, metadata
                FROM notifications
                WHERE recipient_type = $1 AND recipient_id = $2 AND read_at IS NULL
                ORDER BY created_at DESC
                LIMIT $3 OFFSET $4
                "#,
            )
            .bind(recipient_type)
            .bind(user.user_id)
            .bind(limit)
            .bind(offset)
            .fetch_all(&*pool)
            .await?
        } else {
            sqlx::query_as(
                r#"
                SELECT id, created_at, updated_at, recipient_type, recipient_id, rule_id, event_id,
                       status, subject, body, action_url, read_at, metadata
                FROM notifications
                WHERE recipient_type = $1 AND recipient_id = $2
                ORDER BY created_at DESC
                LIMIT $3 OFFSET $4
                "#,
            )
            .bind(recipient_type)
            .bind(user.user_id)
            .bind(limit)
            .bind(offset)
            .fetch_all(&*pool)
            .await?
        };

        Ok(notifications.into_iter().map(NotificationType::from).collect())
    }

    /// Get unread notification count for the current user
    async fn my_unread_notification_count(&self, ctx: &Context<'_>) -> Result<i32> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;
        let user = ctx.data::<UserContext>()?;

        let recipient_type = match user.profile_type {
            ProfileType::Team => "team_profile",
            ProfileType::Customer => "customer_profile",
        };

        let count: i64 = sqlx::query_scalar(
            r#"
            SELECT COUNT(*)
            FROM notifications
            WHERE recipient_type = $1 AND recipient_id = $2 AND read_at IS NULL
            "#,
        )
        .bind(recipient_type)
        .bind(user.user_id)
        .fetch_one(&*pool)
        .await?;

        Ok(count as i32)
    }

    // ==================== ADMIN NOTIFICATION RULE QUERIES ====================

    /// Get all notification rules (admin only)
    async fn notification_rules(
        &self,
        ctx: &Context<'_>,
        #[graphql(default)] is_active: Option<bool>,
    ) -> Result<Vec<NotificationRuleType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let rules: Vec<NotificationRule> = match is_active {
            Some(active) => {
                sqlx::query_as(
                    r#"
                    SELECT id, created_at, updated_at, name, description, is_active, event_types,
                           channels, target_roles, conditions, subject_template, body_template
                    FROM notification_rules
                    WHERE is_active = $1
                    ORDER BY name
                    "#,
                )
                .bind(active)
                .fetch_all(&*pool)
                .await?
            }
            None => {
                sqlx::query_as(
                    r#"
                    SELECT id, created_at, updated_at, name, description, is_active, event_types,
                           channels, target_roles, conditions, subject_template, body_template
                    FROM notification_rules
                    ORDER BY name
                    "#,
                )
                .fetch_all(&*pool)
                .await?
            }
        };

        Ok(rules.into_iter().map(NotificationRuleType::from).collect())
    }

    /// Get a single notification rule by ID (admin only)
    async fn notification_rule(
        &self,
        ctx: &Context<'_>,
        id: Uuid,
    ) -> Result<Option<NotificationRuleType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let rule = sqlx::query_as::<_, NotificationRule>(
            r#"
            SELECT id, created_at, updated_at, name, description, is_active, event_types,
                   channels, target_roles, conditions, subject_template, body_template
            FROM notification_rules
            WHERE id = $1
            "#,
        )
        .bind(id)
        .fetch_optional(&*pool)
        .await?;

        Ok(rule.map(NotificationRuleType::from))
    }

    /// Get delivery history for a notification (admin only)
    async fn notification_deliveries(
        &self,
        ctx: &Context<'_>,
        notification_id: Uuid,
    ) -> Result<Vec<NotificationDeliveryType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let deliveries: Vec<NotificationDelivery> = sqlx::query_as(
            r#"
            SELECT id, created_at, updated_at, notification_id, channel, status, attempts,
                   last_attempt_at, sent_at, delivered_at, error_message, external_id, metadata
            FROM notification_deliveries
            WHERE notification_id = $1
            ORDER BY created_at ASC
            "#,
        )
        .bind(notification_id)
        .fetch_all(&*pool)
        .await?;

        Ok(deliveries.into_iter().map(NotificationDeliveryType::from).collect())
    }
}
