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

use crate::db::Database;
use crate::models::{ProjectScopeTemplate, ProjectScopeTemplateCategory, ProjectScopeTemplateTask};

/// Project scope template GraphQL type - reusable template for creating project scopes
/// Unlike service templates, project templates don't have frequency on tasks
#[derive(SimpleObject)]
#[graphql(complex)]
pub struct ProjectScopeTemplateType {
    pub id: Uuid,
    pub name: String,
    pub description: Option<String>,
    pub is_active: bool,
}

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

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

        Ok(categories.into_iter().map(ProjectScopeTemplateCategoryType::from).collect())
    }

    /// Count of categories in this template
    async fn category_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 project_scope_template_categories WHERE template_id = $1",
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(count)
    }

    /// Total count of tasks across all categories 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 project_scope_template_tasks t
            JOIN project_scope_template_categories c ON t.category_id = c.id
            WHERE c.template_id = $1
            "#,
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(count)
    }
}

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

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

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

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

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

    /// Count of tasks in this category
    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 project_scope_template_tasks WHERE category_id = $1",
        )
        .bind(self.id)
        .fetch_one(&*pool)
        .await?;

        Ok(count)
    }
}

impl From<ProjectScopeTemplateCategory> for ProjectScopeTemplateCategoryType {
    fn from(category: ProjectScopeTemplateCategory) -> Self {
        Self {
            id: category.base.id,
            template_id: category.template_id,
            name: category.name,
            order: category.order,
        }
    }
}

/// Task within a project scope template category
/// Note: No frequency field - project tasks are one-time
#[derive(SimpleObject)]
pub struct ProjectScopeTemplateTaskType {
    pub id: Uuid,
    pub category_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 order: i32,
    pub estimated_minutes: Option<i32>,
}

impl From<ProjectScopeTemplateTask> for ProjectScopeTemplateTaskType {
    fn from(task: ProjectScopeTemplateTask) -> Self {
        Self {
            id: task.base.id,
            category_id: task.category_id,
            scope_description: task.scope_description,
            checklist_description: task.checklist_description,
            session_description: task.session_description,
            order: task.order,
            estimated_minutes: task.estimated_minutes,
        }
    }
}

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

/// Input for creating a project scope template
#[derive(InputObject)]
pub struct CreateProjectScopeTemplateInput {
    pub name: String,
    pub description: Option<String>,
    /// Optional: create categories with the template
    pub categories: Option<Vec<CreateProjectScopeTemplateCategoryInput>>,
}

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

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

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

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

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

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

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

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