//! Job GraphQL mutations
//!
//! Admin-only mutations for manually triggering background jobs.

use async_graphql::{Context, Object, Result, SimpleObject};
use chrono::{DateTime, Utc};

use crate::auth::{ProfileType, UserContext};
use crate::db::Database;
use crate::jobs::{
    EventCleanupJob, EventCleanupResult as JobCleanupResult,
    IncompleteWorkReminderJob, IncompleteWorkReminderResult as JobReminderResult,
    NightlyAssignmentsJob, NightlyAssignmentsResult as JobAssignmentsResult,
};
use crate::models::TeamRole;
use crate::services::GmailService;
use std::sync::Arc;

// ==================== RESULT TYPES ====================

/// Result of running the event cleanup job
#[derive(SimpleObject)]
pub struct EventCleanupResultType {
    /// Number of events deleted
    pub deleted_count: i64,
    /// Cutoff date used for cleanup
    pub cutoff_date: DateTime<Utc>,
    /// Retention period in days
    pub retention_days: i64,
}

impl From<JobCleanupResult> for EventCleanupResultType {
    fn from(result: JobCleanupResult) -> Self {
        Self {
            deleted_count: result.deleted_count,
            cutoff_date: result.cutoff_date,
            retention_days: result.retention_days,
        }
    }
}

/// Result of running the incomplete work reminder job
#[derive(SimpleObject)]
pub struct IncompleteWorkReminderResultType {
    /// Number of team members notified
    pub team_members_notified: i32,
    /// Number of incomplete services found
    pub services_count: i32,
    /// Number of incomplete projects found
    pub projects_count: i32,
    /// Number of emails successfully sent
    pub emails_sent: i32,
    /// Any errors that occurred
    pub errors: Vec<String>,
}

impl From<JobReminderResult> for IncompleteWorkReminderResultType {
    fn from(result: JobReminderResult) -> Self {
        Self {
            team_members_notified: result.team_members_notified,
            services_count: result.services_count,
            projects_count: result.projects_count,
            emails_sent: result.emails_sent,
            errors: result.errors,
        }
    }
}

/// Result of running the nightly assignments job
#[derive(SimpleObject)]
pub struct NightlyAssignmentsResultType {
    /// Number of team members notified
    pub team_members_notified: i32,
    /// Number of scheduled services
    pub services_count: i32,
    /// Number of scheduled projects
    pub projects_count: i32,
    /// Number of emails successfully sent
    pub emails_sent: i32,
    /// Any errors that occurred
    pub errors: Vec<String>,
}

impl From<JobAssignmentsResult> for NightlyAssignmentsResultType {
    fn from(result: JobAssignmentsResult) -> Self {
        Self {
            team_members_notified: result.team_members_notified,
            services_count: result.services_count,
            projects_count: result.projects_count,
            emails_sent: result.emails_sent,
            errors: result.errors,
        }
    }
}

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

#[derive(Default)]
pub struct JobMutation;

#[Object]
impl JobMutation {
    /// Run the event cleanup job manually (ADMIN only)
    ///
    /// This job deletes non-critical events older than 90 days.
    /// Mission-critical events (session, invoice, report events) are preserved.
    async fn run_event_cleanup(&self, ctx: &Context<'_>) -> Result<EventCleanupResultType> {
        // Verify admin access
        verify_admin_access(ctx).await?;

        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        tracing::info!("Manual event cleanup triggered via GraphQL");

        let result = EventCleanupJob::execute(&pool).await?;

        Ok(result.into())
    }

    /// Run the incomplete work reminder job manually (ADMIN only)
    ///
    /// This job sends email reminders to team members about incomplete
    /// services and projects from yesterday (or Friday for Monday runs).
    async fn run_incomplete_work_reminder(
        &self,
        ctx: &Context<'_>,
    ) -> Result<IncompleteWorkReminderResultType> {
        // Verify admin access
        verify_admin_access(ctx).await?;

        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;
        let gmail_service = ctx.data::<Option<Arc<GmailService>>>().ok().and_then(|o| o.clone());

        tracing::info!("Manual incomplete work reminder triggered via GraphQL");

        let result = IncompleteWorkReminderJob::execute(
            &pool,
            gmail_service.as_ref().map(|s| s.as_ref()),
        )
        .await?;

        Ok(result.into())
    }

    /// Run the nightly assignments job manually (ADMIN only)
    ///
    /// This job sends email notifications to team members about
    /// their scheduled services and projects for today.
    async fn run_nightly_assignments(
        &self,
        ctx: &Context<'_>,
    ) -> Result<NightlyAssignmentsResultType> {
        // Verify admin access
        verify_admin_access(ctx).await?;

        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;
        let gmail_service = ctx.data::<Option<Arc<GmailService>>>().ok().and_then(|o| o.clone());

        tracing::info!("Manual nightly assignments triggered via GraphQL");

        let result = NightlyAssignmentsJob::execute(
            &pool,
            gmail_service.as_ref().map(|s| s.as_ref()),
        )
        .await?;

        Ok(result.into())
    }
}

/// Verify that the current user is an admin
async fn verify_admin_access(ctx: &Context<'_>) -> Result<()> {
    let user = ctx.data::<UserContext>()?;

    // Must be a team profile
    if user.profile_type != ProfileType::Team {
        return Err("Only team members can access this resource".into());
    }

    // Fetch role from database
    let db = ctx.data::<Database>()?;
    let pool = db.pool().await;

    let role: Option<TeamRole> = sqlx::query_scalar(
        "SELECT role FROM team_profiles WHERE id = $1",
    )
    .bind(user.user_id)
    .fetch_optional(&*pool)
    .await?;

    match role {
        Some(TeamRole::Admin) => Ok(()),
        Some(_) => Err("Admin access required".into()),
        None => Err("Team profile not found".into()),
    }
}
