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

use crate::db::Database;
use crate::graphql::types::{AccountFilter, AccountType};
use crate::models::{Account, EntityStatus};

#[derive(Default)]
pub struct AccountQuery;

#[Object]
impl AccountQuery {
    /// Get all accounts with optional filtering
    async fn accounts(
        &self,
        ctx: &Context<'_>,
        #[graphql(desc = "Filter by customer ID")] customer_id: Option<Uuid>,
        filter: Option<AccountFilter>,
    ) -> Result<Vec<AccountType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let filter = filter.unwrap_or_default();

        let name_pattern = filter
            .name
            .as_ref()
            .map(|n| format!("%{}%", n.to_lowercase()));
        let status: Option<EntityStatus> = filter.status.map(|s| s.into());

        let accounts: Vec<Account> = sqlx::query_as::<_, Account>(
            r#"
            SELECT id, created_at, updated_at, customer_id, name, status, start_date, end_date
            FROM accounts
            WHERE ($1::uuid IS NULL OR customer_id = $1)
              AND ($2::text IS NULL OR LOWER(name) LIKE $2)
              AND ($3::entity_status IS NULL OR status = $3)
            ORDER BY name ASC
            "#,
        )
        .bind(customer_id)
        .bind(name_pattern)
        .bind(status)
        .fetch_all(&*pool)
        .await?;

        let mut results: Vec<AccountType> = accounts.into_iter().map(AccountType::from).collect();

        if let Some(is_active) = filter.is_active {
            results.retain(|a| a.is_active == is_active);
        }

        Ok(results)
    }

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

        let account = sqlx::query_as::<_, Account>(
            r#"
            SELECT id, created_at, updated_at, customer_id, name, status, start_date, end_date
            FROM accounts
            WHERE id = $1
            "#,
        )
        .bind(id)
        .fetch_optional(&*pool)
        .await?;

        Ok(account.map(AccountType::from))
    }
}
