//! Session GraphQL queries
//!
//! Queries for service and project sessions.

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

use crate::db::Database;
use crate::graphql::types::{
    ProjectSessionType, ServiceSessionType,
};
use crate::models::{ProjectSession, ServiceSession};

#[derive(Default)]
pub struct SessionQuery;

#[Object]
impl SessionQuery {
    // ==================== SERVICE SESSION QUERIES ====================

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

        let session = sqlx::query_as::<_, ServiceSession>(
            r#"
            SELECT id, created_at, updated_at, service_id, account_id, account_address_id,
                   customer_id, scope_id, start, "end", created_by_id, closed_by_id, date
            FROM service_sessions
            WHERE id = $1
            "#,
        )
        .bind(id)
        .fetch_optional(&*pool)
        .await?;

        Ok(session.map(ServiceSessionType::from))
    }

    /// Get all sessions for a service (ordered by date descending)
    async fn service_sessions(
        &self,
        ctx: &Context<'_>,
        service_id: Uuid,
    ) -> Result<Vec<ServiceSessionType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let sessions: Vec<ServiceSession> = sqlx::query_as(
            r#"
            SELECT id, created_at, updated_at, service_id, account_id, account_address_id,
                   customer_id, scope_id, start, "end", created_by_id, closed_by_id, date
            FROM service_sessions
            WHERE service_id = $1
            ORDER BY date DESC, start DESC
            "#,
        )
        .bind(service_id)
        .fetch_all(&*pool)
        .await?;

        Ok(sessions.into_iter().map(ServiceSessionType::from).collect())
    }

    /// Get the active (unclosed) session for a service, if one exists
    async fn active_service_session(
        &self,
        ctx: &Context<'_>,
        service_id: Uuid,
    ) -> Result<Option<ServiceSessionType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let session = sqlx::query_as::<_, ServiceSession>(
            r#"
            SELECT id, created_at, updated_at, service_id, account_id, account_address_id,
                   customer_id, scope_id, start, "end", created_by_id, closed_by_id, date
            FROM service_sessions
            WHERE service_id = $1 AND "end" IS NULL
            LIMIT 1
            "#,
        )
        .bind(service_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(session.map(ServiceSessionType::from))
    }

    /// Get the most recent session for a service (active or closed)
    async fn latest_service_session(
        &self,
        ctx: &Context<'_>,
        service_id: Uuid,
    ) -> Result<Option<ServiceSessionType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let session = sqlx::query_as::<_, ServiceSession>(
            r#"
            SELECT id, created_at, updated_at, service_id, account_id, account_address_id,
                   customer_id, scope_id, start, "end", created_by_id, closed_by_id, date
            FROM service_sessions
            WHERE service_id = $1
            ORDER BY date DESC, start DESC
            LIMIT 1
            "#,
        )
        .bind(service_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(session.map(ServiceSessionType::from))
    }

    // ==================== PROJECT SESSION QUERIES ====================

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

        let session = sqlx::query_as::<_, ProjectSession>(
            r#"
            SELECT id, created_at, updated_at, project_id, account_id, account_address_id,
                   customer_id, scope_id, start, "end", created_by_id, closed_by_id, date
            FROM project_sessions
            WHERE id = $1
            "#,
        )
        .bind(id)
        .fetch_optional(&*pool)
        .await?;

        Ok(session.map(ProjectSessionType::from))
    }

    /// Get all sessions for a project (ordered by date descending)
    async fn project_sessions(
        &self,
        ctx: &Context<'_>,
        project_id: Uuid,
    ) -> Result<Vec<ProjectSessionType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let sessions: Vec<ProjectSession> = sqlx::query_as(
            r#"
            SELECT id, created_at, updated_at, project_id, account_id, account_address_id,
                   customer_id, scope_id, start, "end", created_by_id, closed_by_id, date
            FROM project_sessions
            WHERE project_id = $1
            ORDER BY date DESC, start DESC
            "#,
        )
        .bind(project_id)
        .fetch_all(&*pool)
        .await?;

        Ok(sessions.into_iter().map(ProjectSessionType::from).collect())
    }

    /// Get the active (unclosed) session for a project, if one exists
    async fn active_project_session(
        &self,
        ctx: &Context<'_>,
        project_id: Uuid,
    ) -> Result<Option<ProjectSessionType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let session = sqlx::query_as::<_, ProjectSession>(
            r#"
            SELECT id, created_at, updated_at, project_id, account_id, account_address_id,
                   customer_id, scope_id, start, "end", created_by_id, closed_by_id, date
            FROM project_sessions
            WHERE project_id = $1 AND "end" IS NULL
            LIMIT 1
            "#,
        )
        .bind(project_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(session.map(ProjectSessionType::from))
    }

    /// Get the most recent session for a project (active or closed)
    async fn latest_project_session(
        &self,
        ctx: &Context<'_>,
        project_id: Uuid,
    ) -> Result<Option<ProjectSessionType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let session = sqlx::query_as::<_, ProjectSession>(
            r#"
            SELECT id, created_at, updated_at, project_id, account_id, account_address_id,
                   customer_id, scope_id, start, "end", created_by_id, closed_by_id, date
            FROM project_sessions
            WHERE project_id = $1
            ORDER BY date DESC, start DESC
            LIMIT 1
            "#,
        )
        .bind(project_id)
        .fetch_optional(&*pool)
        .await?;

        Ok(session.map(ProjectSessionType::from))
    }
}
