use async_graphql::{ComplexObject, Context, InputObject, Result, SimpleObject};
use uuid::Uuid;

use crate::db::Database;
use crate::models::{ServiceScopeTemplate, ServiceScopeTemplateArea, ServiceScopeTemplateTask};

use super::TaskFrequencyType;

/// Service scope template GraphQL type - reusable template for creating service scopes
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct ServiceScopeTemplateType {
    pub id: Uuid,
    pub name: String,
    pub description: Option<String>,
    pub is_active: bool,
}

#[ComplexObject]
impl ServiceScopeTemplateType {
    /// Get all areas in this template
    async fn areas(&self, ctx: &Context<'_>) -> Result<Vec<ServiceScopeTemplateAreaType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let areas: Vec<ServiceScopeTemplateArea> = sqlx::query_as::<_, ServiceScopeTemplateArea>(
            r#"
            SELECT id, created_at, updated_at, template_id, name, "order"
            FROM service_scope_template_areas
            WHERE template_id = $1
            ORDER BY "order" ASC
            "#,
        )
        .bind(self.id)
        .fetch_all(&*pool)
        .await?;

        Ok(areas.into_iter().map(ServiceScopeTemplateAreaType::from).collect())
    }

    /// Count of areas in this template
    async fn area_count(&self, ctx: &Context<'_>) -> Result<i64> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let count: i64 = sqlx::query_scalar(
            "SELECT COUNT(*) FROM service_scope_template_areas WHERE template_id = $1",
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(count)
    }

    /// Total count of tasks across all areas in this template
    async fn task_count(&self, ctx: &Context<'_>) -> Result<i64> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let count: i64 = sqlx::query_scalar(
            r#"
            SELECT COUNT(*)
            FROM service_scope_template_tasks t
            JOIN service_scope_template_areas a ON t.area_id = a.id
            WHERE a.template_id = $1
            "#,
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(count)
    }
}

impl From<ServiceScopeTemplate> for ServiceScopeTemplateType {
    fn from(template: ServiceScopeTemplate) -> Self {
        Self {
            id: template.base.id,
            name: template.name,
            description: template.description,
            is_active: template.is_active,
        }
    }
}

/// Area within a service scope template
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct ServiceScopeTemplateAreaType {
    pub id: Uuid,
    pub template_id: Uuid,
    pub name: String,
    pub order: i32,
}

#[ComplexObject]
impl ServiceScopeTemplateAreaType {
    /// Get all tasks in this area
    async fn tasks(&self, ctx: &Context<'_>) -> Result<Vec<ServiceScopeTemplateTaskType>> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let tasks: Vec<ServiceScopeTemplateTask> = sqlx::query_as::<_, ServiceScopeTemplateTask>(
            r#"
            SELECT id, created_at, updated_at, area_id, scope_description,
                   checklist_description, session_description, frequency, "order", estimated_minutes
            FROM service_scope_template_tasks
            WHERE area_id = $1
            ORDER BY "order" ASC
            "#,
        )
        .bind(self.id)
        .fetch_all(&*pool)
        .await?;

        Ok(tasks.into_iter().map(ServiceScopeTemplateTaskType::from).collect())
    }

    /// Count of tasks in this area
    async fn task_count(&self, ctx: &Context<'_>) -> Result<i64> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        let count: i64 = sqlx::query_scalar(
            "SELECT COUNT(*) FROM service_scope_template_tasks WHERE area_id = $1",
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(count)
    }
}

impl From<ServiceScopeTemplateArea> for ServiceScopeTemplateAreaType {
    fn from(area: ServiceScopeTemplateArea) -> Self {
        Self {
            id: area.base.id,
            template_id: area.template_id,
            name: area.name,
            order: area.order,
        }
    }
}

/// Task within a service scope template area
#[derive(SimpleObject)]
pub struct ServiceScopeTemplateTaskType {
    pub id: Uuid,
    pub area_id: Uuid,
    /// Customer-facing description
    pub scope_description: String,
    /// QA/punchlist formatted description
    pub checklist_description: String,
    /// Team member work instructions
    pub session_description: String,
    pub frequency: TaskFrequencyType,
    pub order: i32,
    pub estimated_minutes: Option<i32>,
}

impl From<ServiceScopeTemplateTask> for ServiceScopeTemplateTaskType {
    fn from(task: ServiceScopeTemplateTask) -> Self {
        Self {
            id: task.base.id,
            area_id: task.area_id,
            scope_description: task.scope_description,
            checklist_description: task.checklist_description,
            session_description: task.session_description,
            frequency: task.frequency.into(),
            order: task.order,
            estimated_minutes: task.estimated_minutes,
        }
    }
}

// ==================== INPUT TYPES ====================

/// Input for creating a service scope template
#[derive(InputObject)]
pub struct CreateServiceScopeTemplateInput {
    pub name: String,
    pub description: Option<String>,
    /// Optional: create areas with the template
    pub areas: Option<Vec<CreateServiceScopeTemplateAreaInput>>,
}

/// Input for updating a service scope template
#[derive(InputObject)]
pub struct UpdateServiceScopeTemplateInput {
    pub name: Option<String>,
    pub description: Option<String>,
    pub is_active: Option<bool>,
}

/// Input for creating a service scope template area
#[derive(InputObject)]
pub struct CreateServiceScopeTemplateAreaInput {
    pub name: String,
    pub order: Option<i32>,
    /// Optional: create tasks with the area
    pub tasks: Option<Vec<CreateServiceScopeTemplateTaskInput>>,
}

/// Input for updating a service scope template area
#[derive(InputObject)]
pub struct UpdateServiceScopeTemplateAreaInput {
    pub name: Option<String>,
    pub order: Option<i32>,
}

/// Input for creating a service scope template task
#[derive(InputObject)]
pub struct CreateServiceScopeTemplateTaskInput {
    pub scope_description: String,
    pub checklist_description: Option<String>,
    pub session_description: Option<String>,
    pub frequency: Option<TaskFrequencyType>,
    pub order: Option<i32>,
    pub estimated_minutes: Option<i32>,
}

/// Input for updating a service scope template task
#[derive(InputObject)]
pub struct UpdateServiceScopeTemplateTaskInput {
    pub scope_description: Option<String>,
    pub checklist_description: Option<String>,
    pub session_description: Option<String>,
    pub frequency: Option<TaskFrequencyType>,
    pub order: Option<i32>,
    pub estimated_minutes: Option<i32>,
}

/// Input for importing a service scope template from JSON
#[derive(InputObject)]
pub struct ImportServiceScopeTemplateInput {
    pub name: String,
    pub description: Option<String>,
    pub areas: Vec<ImportServiceScopeTemplateAreaInput>,
    /// If true, replace existing template with same name
    pub replace: Option<bool>,
}

/// Area structure for import
#[derive(InputObject)]
pub struct ImportServiceScopeTemplateAreaInput {
    pub name: String,
    pub order: i32,
    pub tasks: Vec<ImportServiceScopeTemplateTaskInput>,
}

/// Task structure for import
#[derive(InputObject)]
pub struct ImportServiceScopeTemplateTaskInput {
    pub scope_description: String,
    pub checklist_description: Option<String>,
    pub session_description: Option<String>,
    pub frequency: Option<TaskFrequencyType>,
    pub order: i32,
    pub estimated_minutes: Option<i32>,
}
