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

use crate::db::Database;
use crate::graphql::types::{
    PaginationInput, ProjectConnection, ProjectFilterInput, ProjectType,
};
use crate::models::{Project, WorkStatus};

#[derive(Default)]
pub struct ProjectQuery;

#[Object]
impl ProjectQuery {
    /// Get paginated projects with optional filtering
    async fn projects(
        &self,
        ctx: &Context<'_>,
        filter: Option<ProjectFilterInput>,
        pagination: Option<PaginationInput>,
    ) -> Result<ProjectConnection> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let filter = filter.unwrap_or_default();
        let pagination = pagination.unwrap_or_default();
        let offset = pagination.offset();
        let limit = pagination.limit();

        // Convert filter status to model enum
        let status: Option<WorkStatus> = filter.status.map(WorkStatus::from);

        // Merge customer_id into customer_ids for backward compatibility
        let customer_ids: Option<Vec<Uuid>> = match (&filter.customer_ids, &filter.customer_id) {
            (Some(ids), _) if !ids.is_empty() => Some(ids.clone()),
            (_, Some(id)) => Some(vec![*id]),
            _ => None,
        };

        // Get total count first
        let total_count: i64 = sqlx::query_scalar(
            r#"
            SELECT COUNT(*)
            FROM projects
            WHERE ($1::date IS NULL OR date >= $1)
              AND ($2::date IS NULL OR date <= $2)
              AND ($3::work_status IS NULL OR status = $3)
              AND ($4::uuid[] IS NULL OR customer_id = ANY($4))
              AND ($5::uuid IS NULL OR account_address_id = $5)
              AND ($6::uuid IS NULL OR EXISTS (
                  SELECT 1 FROM project_team_members ptm
                  WHERE ptm.project_id = projects.id AND ptm.team_profile_id = $6
              ))
            "#,
        )
        .bind(filter.date_from)
        .bind(filter.date_to)
        .bind(status)
        .bind(&customer_ids)
        .bind(filter.account_address_id)
        .bind(filter.team_profile_id)
        .fetch_one(&*pool)
        .await?;

        // Get paginated projects
        let projects: Vec<Project> = sqlx::query_as::<_, Project>(
            r#"
            SELECT id, created_at, updated_at, customer_id, name, date, status, labor, amount, notes,
                   calendar_event_id, wave_service_id, account_address_id, street_address, city, state, zip_code
            FROM projects
            WHERE ($1::date IS NULL OR date >= $1)
              AND ($2::date IS NULL OR date <= $2)
              AND ($3::work_status IS NULL OR status = $3)
              AND ($4::uuid[] IS NULL OR customer_id = ANY($4))
              AND ($5::uuid IS NULL OR account_address_id = $5)
              AND ($6::uuid IS NULL OR EXISTS (
                  SELECT 1 FROM project_team_members ptm
                  WHERE ptm.project_id = projects.id AND ptm.team_profile_id = $6
              ))
            ORDER BY date ASC, name ASC
            LIMIT $7 OFFSET $8
            "#,
        )
        .bind(filter.date_from)
        .bind(filter.date_to)
        .bind(status)
        .bind(&customer_ids)
        .bind(filter.account_address_id)
        .bind(filter.team_profile_id)
        .bind(limit)
        .bind(offset)
        .fetch_all(&*pool)
        .await?;

        let items: Vec<ProjectType> = projects.into_iter().map(ProjectType::from).collect();
        let has_next_page = (offset + limit) < total_count;

        Ok(ProjectConnection {
            items,
            total_count: total_count as i32,
            has_next_page,
        })
    }

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

        let project = sqlx::query_as::<_, Project>(
            r#"
            SELECT id, created_at, updated_at, customer_id, name, date, status, labor, amount, notes,
                   calendar_event_id, wave_service_id, account_address_id, street_address, city, state, zip_code
            FROM projects
            WHERE id = $1
            "#,
        )
        .bind(id)
        .fetch_optional(&*pool)
        .await?;

        Ok(project.map(ProjectType::from))
    }

    /// Get status counts for projects in a date range (for status tabs)
    async fn project_status_counts(
        &self,
        ctx: &Context<'_>,
        date_from: Option<chrono::NaiveDate>,
        date_to: Option<chrono::NaiveDate>,
        team_profile_id: Option<Uuid>,
        customer_ids: Option<Vec<Uuid>>,
    ) -> Result<ProjectStatusCounts> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let counts = sqlx::query_as::<_, StatusCountRow>(
            r#"
            SELECT
                COUNT(*) FILTER (WHERE status = 'SCHEDULED') as scheduled,
                COUNT(*) FILTER (WHERE status = 'IN_PROGRESS') as in_progress,
                COUNT(*) FILTER (WHERE status = 'COMPLETED') as completed,
                COUNT(*) FILTER (WHERE status = 'CANCELLED') as cancelled
            FROM projects
            WHERE ($1::date IS NULL OR date >= $1)
              AND ($2::date IS NULL OR date <= $2)
              AND ($3::uuid IS NULL OR EXISTS (
                  SELECT 1 FROM project_team_members ptm
                  WHERE ptm.project_id = projects.id AND ptm.team_profile_id = $3
              ))
              AND ($4::uuid[] IS NULL OR customer_id = ANY($4))
            "#,
        )
        .bind(date_from)
        .bind(date_to)
        .bind(team_profile_id)
        .bind(&customer_ids)
        .fetch_one(&*pool)
        .await?;

        Ok(ProjectStatusCounts {
            scheduled: counts.scheduled.unwrap_or(0) as i32,
            in_progress: counts.in_progress.unwrap_or(0) as i32,
            completed: counts.completed.unwrap_or(0) as i32,
            cancelled: counts.cancelled.unwrap_or(0) as i32,
        })
    }
}

/// Status counts for the status tabs
#[derive(async_graphql::SimpleObject)]
pub struct ProjectStatusCounts {
    pub scheduled: i32,
    pub in_progress: i32,
    pub completed: i32,
    pub cancelled: i32,
}

/// Helper struct for query results
#[derive(sqlx::FromRow)]
struct StatusCountRow {
    scheduled: Option<i64>,
    in_progress: Option<i64>,
    completed: Option<i64>,
    cancelled: Option<i64>,
}
