//! Messaging GraphQL queries
//!
//! Queries for conversations, messages, and participants.

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

use crate::auth::{ProfileType, UserContext};
use crate::db::Database;
use crate::graphql::types::{ConversationType_, MessageType};
use crate::models::{Conversation, Message};

#[derive(Default)]
pub struct MessagingQuery;

#[Object]
impl MessagingQuery {
    // ==================== CONVERSATION QUERIES ====================

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

        let conversation = sqlx::query_as::<_, Conversation>(
            r#"
            SELECT id, created_at, updated_at, subject, conversation_type, entity_type, entity_id,
                   created_by_type, created_by_id, last_message_at, is_archived, metadata
            FROM conversations
            WHERE id = $1
            "#,
        )
        .bind(id)
        .fetch_optional(&*pool)
        .await?;

        Ok(conversation.map(ConversationType_::from))
    }

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

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

        let conversations: Vec<Conversation> = if include_archived {
            sqlx::query_as(
                r#"
                SELECT c.id, c.created_at, c.updated_at, c.subject, c.conversation_type,
                       c.entity_type, c.entity_id, c.created_by_type, c.created_by_id,
                       c.last_message_at, c.is_archived, c.metadata
                FROM conversations c
                INNER JOIN conversation_participants cp ON c.id = cp.conversation_id
                WHERE cp.participant_type = $1
                  AND cp.participant_id = $2
                ORDER BY c.last_message_at DESC NULLS LAST, c.created_at DESC
                LIMIT $3 OFFSET $4
                "#,
            )
            .bind(participant_type)
            .bind(user.user_id)
            .bind(limit)
            .bind(offset)
            .fetch_all(&*pool)
            .await?
        } else {
            sqlx::query_as(
                r#"
                SELECT c.id, c.created_at, c.updated_at, c.subject, c.conversation_type,
                       c.entity_type, c.entity_id, c.created_by_type, c.created_by_id,
                       c.last_message_at, c.is_archived, c.metadata
                FROM conversations c
                INNER JOIN conversation_participants cp ON c.id = cp.conversation_id
                WHERE cp.participant_type = $1
                  AND cp.participant_id = $2
                  AND c.is_archived = false
                  AND cp.is_archived = false
                ORDER BY c.last_message_at DESC NULLS LAST, c.created_at DESC
                LIMIT $3 OFFSET $4
                "#,
            )
            .bind(participant_type)
            .bind(user.user_id)
            .bind(limit)
            .bind(offset)
            .fetch_all(&*pool)
            .await?
        };

        Ok(conversations.into_iter().map(ConversationType_::from).collect())
    }

    /// Get conversations by entity (e.g., all conversations about a specific service)
    async fn conversations_by_entity(
        &self,
        ctx: &Context<'_>,
        entity_type: String,
        entity_id: Uuid,
    ) -> Result<Vec<ConversationType_>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let conversations: Vec<Conversation> = sqlx::query_as(
            r#"
            SELECT id, created_at, updated_at, subject, conversation_type, entity_type, entity_id,
                   created_by_type, created_by_id, last_message_at, is_archived, metadata
            FROM conversations
            WHERE entity_type = $1 AND entity_id = $2 AND is_archived = false
            ORDER BY last_message_at DESC NULLS LAST, created_at DESC
            "#,
        )
        .bind(&entity_type)
        .bind(entity_id)
        .fetch_all(&*pool)
        .await?;

        Ok(conversations.into_iter().map(ConversationType_::from).collect())
    }

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

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

        let count: Option<i64> = sqlx::query_scalar(
            r#"
            SELECT COALESCE(SUM(unread_count), 0)
            FROM conversation_participants cp
            INNER JOIN conversations c ON c.id = cp.conversation_id
            WHERE cp.participant_type = $1
              AND cp.participant_id = $2
              AND cp.is_archived = false
              AND c.is_archived = false
            "#,
        )
        .bind(participant_type)
        .bind(user.user_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(count.unwrap_or(0) as i32)
    }

    // ==================== MESSAGE QUERIES ====================

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

        let message = sqlx::query_as::<_, Message>(
            r#"
            SELECT id, created_at, updated_at, conversation_id, author_type, author_id,
                   content, is_deleted, reply_to_id, attachments, is_system_message, metadata
            FROM messages
            WHERE id = $1
            "#,
        )
        .bind(id)
        .fetch_optional(&*pool)
        .await?;

        Ok(message.map(MessageType::from))
    }

    /// Get messages in a conversation with pagination
    async fn messages(
        &self,
        ctx: &Context<'_>,
        conversation_id: Uuid,
        #[graphql(default = 50)] limit: i32,
        #[graphql(default = 0)] offset: i32,
        #[graphql(default = false)] include_deleted: bool,
        #[graphql(default = false)] include_system: bool,
    ) -> Result<Vec<MessageType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let messages: Vec<Message> = match (include_deleted, include_system) {
            (true, true) => {
                sqlx::query_as(
                    r#"
                    SELECT id, created_at, updated_at, conversation_id, author_type, author_id,
                           content, is_deleted, reply_to_id, attachments, is_system_message, metadata
                    FROM messages
                    WHERE conversation_id = $1
                    ORDER BY created_at DESC
                    LIMIT $2 OFFSET $3
                    "#,
                )
                .bind(conversation_id)
                .bind(limit)
                .bind(offset)
                .fetch_all(&*pool)
                .await?
            }
            (true, false) => {
                sqlx::query_as(
                    r#"
                    SELECT id, created_at, updated_at, conversation_id, author_type, author_id,
                           content, is_deleted, reply_to_id, attachments, is_system_message, metadata
                    FROM messages
                    WHERE conversation_id = $1 AND is_system_message = false
                    ORDER BY created_at DESC
                    LIMIT $2 OFFSET $3
                    "#,
                )
                .bind(conversation_id)
                .bind(limit)
                .bind(offset)
                .fetch_all(&*pool)
                .await?
            }
            (false, true) => {
                sqlx::query_as(
                    r#"
                    SELECT id, created_at, updated_at, conversation_id, author_type, author_id,
                           content, is_deleted, reply_to_id, attachments, is_system_message, metadata
                    FROM messages
                    WHERE conversation_id = $1 AND is_deleted = false
                    ORDER BY created_at DESC
                    LIMIT $2 OFFSET $3
                    "#,
                )
                .bind(conversation_id)
                .bind(limit)
                .bind(offset)
                .fetch_all(&*pool)
                .await?
            }
            (false, false) => {
                sqlx::query_as(
                    r#"
                    SELECT id, created_at, updated_at, conversation_id, author_type, author_id,
                           content, is_deleted, reply_to_id, attachments, is_system_message, metadata
                    FROM messages
                    WHERE conversation_id = $1 AND is_deleted = false AND is_system_message = false
                    ORDER BY created_at DESC
                    LIMIT $2 OFFSET $3
                    "#,
                )
                .bind(conversation_id)
                .bind(limit)
                .bind(offset)
                .fetch_all(&*pool)
                .await?
            }
        };

        Ok(messages.into_iter().map(MessageType::from).collect())
    }
}
