//! Notification GraphQL mutations
//!
//! Mutations for managing notifications and notification rules.

use async_graphql::{Context, InputObject, Object, Result};
use chrono::Utc;
use serde_json::Value as JsonValue;
use uuid::Uuid;

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

// ==================== INPUT TYPES ====================

/// Input for creating a notification rule
#[derive(InputObject)]
pub struct CreateNotificationRuleInput {
    pub name: String,
    pub description: Option<String>,
    /// Event types that trigger this rule (array of event type strings)
    pub event_types: JsonValue,
    /// Channels to deliver through (array of channel strings, e.g., ["IN_APP", "EMAIL"])
    pub channels: JsonValue,
    /// Target roles (array of role strings, e.g., ["ADMIN", "TEAM_LEADER"])
    pub target_roles: Option<JsonValue>,
    /// Custom conditions for triggering (JSON)
    pub conditions: Option<JsonValue>,
    /// Notification subject template
    pub subject_template: Option<String>,
    /// Notification body template
    pub body_template: Option<String>,
    /// Specific team profiles to target
    pub target_team_profile_ids: Option<Vec<Uuid>>,
    /// Specific customer profiles to target
    pub target_customer_profile_ids: Option<Vec<Uuid>>,
}

/// Input for updating a notification rule
#[derive(InputObject)]
pub struct UpdateNotificationRuleInput {
    pub name: Option<String>,
    pub description: Option<String>,
    pub event_types: Option<JsonValue>,
    pub channels: Option<JsonValue>,
    pub target_roles: Option<JsonValue>,
    pub conditions: Option<JsonValue>,
    pub subject_template: Option<String>,
    pub body_template: Option<String>,
    /// Specific team profiles to target (replaces existing)
    pub target_team_profile_ids: Option<Vec<Uuid>>,
    /// Specific customer profiles to target (replaces existing)
    pub target_customer_profile_ids: Option<Vec<Uuid>>,
}

// ==================== MUTATION IMPLEMENTATION ====================

#[derive(Default)]
pub struct NotificationMutation;

#[Object]
impl NotificationMutation {
    // ==================== USER NOTIFICATION MUTATIONS ====================

    /// Mark a notification as read
    async fn mark_notification_as_read(
        &self,
        ctx: &Context<'_>,
        id: Uuid,
    ) -> Result<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 now = Utc::now();

        let notification: Notification = sqlx::query_as(
            r#"
            UPDATE notifications
            SET read_at = $1, status = $2, updated_at = $1
            WHERE id = $3 AND recipient_type = $4 AND recipient_id = $5
            RETURNING id, created_at, updated_at, recipient_type, recipient_id, rule_id, event_id,
                      status, subject, body, action_url, read_at, metadata
            "#,
        )
        .bind(now)
        .bind(NotificationStatus::Read)
        .bind(id)
        .bind(recipient_type)
        .bind(user.user_id)
        .fetch_one(&*pool)
        .await?;

        Ok(NotificationType::from(notification))
    }

    /// Mark all notifications as read for the current user
    async fn mark_all_notifications_as_read(&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 now = Utc::now();

        let result = sqlx::query(
            r#"
            UPDATE notifications
            SET read_at = $1, status = $2, updated_at = $1
            WHERE recipient_type = $3 AND recipient_id = $4 AND read_at IS NULL
            "#,
        )
        .bind(now)
        .bind(NotificationStatus::Read)
        .bind(recipient_type)
        .bind(user.user_id)
        .execute(&*pool)
        .await?;

        Ok(result.rows_affected() as i32)
    }

    /// Delete a notification
    async fn delete_notification(&self, ctx: &Context<'_>, id: Uuid) -> Result<bool> {
        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 result = sqlx::query(
            r#"
            DELETE FROM notifications
            WHERE id = $1 AND recipient_type = $2 AND recipient_id = $3
            "#,
        )
        .bind(id)
        .bind(recipient_type)
        .bind(user.user_id)
        .execute(&*pool)
        .await?;

        Ok(result.rows_affected() > 0)
    }

    // ==================== ADMIN NOTIFICATION RULE MUTATIONS ====================

    /// Create a notification rule (admin only)
    async fn create_notification_rule(
        &self,
        ctx: &Context<'_>,
        input: CreateNotificationRuleInput,
    ) -> Result<NotificationRuleType> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let now = Utc::now();

        // Create the rule
        let rule: NotificationRule = sqlx::query_as(
            r#"
            INSERT INTO notification_rules (
                id, created_at, updated_at, name, description, is_active, event_types,
                channels, target_roles, conditions, subject_template, body_template
            )
            VALUES (gen_random_uuid(), $1, $1, $2, $3, true, $4, $5, $6, $7, $8, $9)
            RETURNING id, created_at, updated_at, name, description, is_active, event_types,
                      channels, target_roles, conditions, subject_template, body_template
            "#,
        )
        .bind(now)
        .bind(&input.name)
        .bind(&input.description)
        .bind(&input.event_types)
        .bind(&input.channels)
        .bind(&input.target_roles)
        .bind(&input.conditions)
        .bind(&input.subject_template)
        .bind(&input.body_template)
        .fetch_one(&*pool)
        .await?;

        // Add team profile targets
        if let Some(team_ids) = input.target_team_profile_ids {
            for team_id in team_ids {
                sqlx::query(
                    r#"
                    INSERT INTO notification_rule_team_profiles (id, rule_id, team_profile_id, created_at)
                    VALUES (gen_random_uuid(), $1, $2, $3)
                    "#,
                )
                .bind(rule.base.id)
                .bind(team_id)
                .bind(now)
                .execute(&*pool)
                .await?;
            }
        }

        // Add customer profile targets
        if let Some(customer_ids) = input.target_customer_profile_ids {
            for customer_id in customer_ids {
                sqlx::query(
                    r#"
                    INSERT INTO notification_rule_customer_profiles (id, rule_id, customer_profile_id, created_at)
                    VALUES (gen_random_uuid(), $1, $2, $3)
                    "#,
                )
                .bind(rule.base.id)
                .bind(customer_id)
                .bind(now)
                .execute(&*pool)
                .await?;
            }
        }

        Ok(NotificationRuleType::from(rule))
    }

    /// Update a notification rule (admin only)
    async fn update_notification_rule(
        &self,
        ctx: &Context<'_>,
        id: Uuid,
        input: UpdateNotificationRuleInput,
    ) -> Result<NotificationRuleType> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let now = Utc::now();

        let rule: NotificationRule = sqlx::query_as(
            r#"
            UPDATE notification_rules
            SET name = COALESCE($1, name),
                description = COALESCE($2, description),
                event_types = COALESCE($3, event_types),
                channels = COALESCE($4, channels),
                target_roles = COALESCE($5, target_roles),
                conditions = COALESCE($6, conditions),
                subject_template = COALESCE($7, subject_template),
                body_template = COALESCE($8, body_template),
                updated_at = $9
            WHERE id = $10
            RETURNING id, created_at, updated_at, name, description, is_active, event_types,
                      channels, target_roles, conditions, subject_template, body_template
            "#,
        )
        .bind(&input.name)
        .bind(&input.description)
        .bind(&input.event_types)
        .bind(&input.channels)
        .bind(&input.target_roles)
        .bind(&input.conditions)
        .bind(&input.subject_template)
        .bind(&input.body_template)
        .bind(now)
        .bind(id)
        .fetch_one(&*pool)
        .await?;

        // Update team profile targets if provided
        if let Some(team_ids) = input.target_team_profile_ids {
            // Remove existing
            sqlx::query("DELETE FROM notification_rule_team_profiles WHERE rule_id = $1")
                .bind(id)
                .execute(&*pool)
                .await?;

            // Add new
            for team_id in team_ids {
                sqlx::query(
                    r#"
                    INSERT INTO notification_rule_team_profiles (id, rule_id, team_profile_id, created_at)
                    VALUES (gen_random_uuid(), $1, $2, $3)
                    "#,
                )
                .bind(id)
                .bind(team_id)
                .bind(now)
                .execute(&*pool)
                .await?;
            }
        }

        // Update customer profile targets if provided
        if let Some(customer_ids) = input.target_customer_profile_ids {
            // Remove existing
            sqlx::query("DELETE FROM notification_rule_customer_profiles WHERE rule_id = $1")
                .bind(id)
                .execute(&*pool)
                .await?;

            // Add new
            for customer_id in customer_ids {
                sqlx::query(
                    r#"
                    INSERT INTO notification_rule_customer_profiles (id, rule_id, customer_profile_id, created_at)
                    VALUES (gen_random_uuid(), $1, $2, $3)
                    "#,
                )
                .bind(id)
                .bind(customer_id)
                .bind(now)
                .execute(&*pool)
                .await?;
            }
        }

        Ok(NotificationRuleType::from(rule))
    }

    /// Delete a notification rule (admin only)
    async fn delete_notification_rule(&self, ctx: &Context<'_>, id: Uuid) -> Result<bool> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        // Delete associated targets first (due to foreign keys)
        sqlx::query("DELETE FROM notification_rule_team_profiles WHERE rule_id = $1")
            .bind(id)
            .execute(&*pool)
            .await?;

        sqlx::query("DELETE FROM notification_rule_customer_profiles WHERE rule_id = $1")
            .bind(id)
            .execute(&*pool)
            .await?;

        let result = sqlx::query("DELETE FROM notification_rules WHERE id = $1")
            .bind(id)
            .execute(&*pool)
            .await?;

        Ok(result.rows_affected() > 0)
    }

    /// Toggle a notification rule's active status (admin only)
    async fn toggle_notification_rule(
        &self,
        ctx: &Context<'_>,
        id: Uuid,
        is_active: bool,
    ) -> Result<NotificationRuleType> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let rule: NotificationRule = sqlx::query_as(
            r#"
            UPDATE notification_rules
            SET is_active = $1, updated_at = NOW()
            WHERE id = $2
            RETURNING id, created_at, updated_at, name, description, is_active, event_types,
                      channels, target_roles, conditions, subject_template, body_template
            "#,
        )
        .bind(is_active)
        .bind(id)
        .fetch_one(&*pool)
        .await?;

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