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

use crate::db::Database;
use crate::graphql::types::{
    CalendarEventType, CreateCalendarEventInput, UpdateCalendarEventInput,
};
use crate::models::{Project, Service};
use crate::services::google_calendar::{CreateEventRequest, GoogleCalendarService, UpdateEventRequest};

#[derive(Default)]
pub struct CalendarMutation;

#[Object]
impl CalendarMutation {
    /// Create a new calendar event
    async fn create_calendar_event(
        &self,
        ctx: &Context<'_>,
        input: CreateCalendarEventInput,
    ) -> Result<CalendarEventType> {
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;

        let request = CreateEventRequest::from(input);
        let event = calendar.create_event(request).await?;

        Ok(CalendarEventType::from(event))
    }

    /// Update a calendar event
    async fn update_calendar_event(
        &self,
        ctx: &Context<'_>,
        event_id: String,
        input: UpdateCalendarEventInput,
    ) -> Result<CalendarEventType> {
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;

        let request = UpdateEventRequest::from(input);
        let event = calendar.update_event(&event_id, request).await?;

        Ok(CalendarEventType::from(event))
    }

    /// Delete a calendar event
    async fn delete_calendar_event(
        &self,
        ctx: &Context<'_>,
        event_id: String,
    ) -> Result<bool> {
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;

        calendar.delete_event(&event_id).await?;

        Ok(true)
    }

    /// Link a calendar event to a service
    async fn link_calendar_event_to_service(
        &self,
        ctx: &Context<'_>,
        service_id: Uuid,
        event_id: String,
    ) -> Result<bool> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        sqlx::query(
            r#"
            UPDATE services
            SET calendar_event_id = $2, updated_at = NOW()
            WHERE id = $1
            "#,
        )
        .bind(service_id)
        .bind(&event_id)
        .execute(&*pool)
        .await?;

        Ok(true)
    }

    /// Unlink a calendar event from a service
    async fn unlink_calendar_event_from_service(
        &self,
        ctx: &Context<'_>,
        service_id: Uuid,
    ) -> Result<bool> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        sqlx::query(
            r#"
            UPDATE services
            SET calendar_event_id = NULL, updated_at = NOW()
            WHERE id = $1
            "#,
        )
        .bind(service_id)
        .execute(&*pool)
        .await?;

        Ok(true)
    }

    /// Link a calendar event to a project
    async fn link_calendar_event_to_project(
        &self,
        ctx: &Context<'_>,
        project_id: Uuid,
        event_id: String,
    ) -> Result<bool> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        sqlx::query(
            r#"
            UPDATE projects
            SET calendar_event_id = $2, updated_at = NOW()
            WHERE id = $1
            "#,
        )
        .bind(project_id)
        .bind(&event_id)
        .execute(&*pool)
        .await?;

        Ok(true)
    }

    /// Unlink a calendar event from a project
    async fn unlink_calendar_event_from_project(
        &self,
        ctx: &Context<'_>,
        project_id: Uuid,
    ) -> Result<bool> {
        let db = ctx.data::<Database>()?;
        let pool = db.pool().await;

        sqlx::query(
            r#"
            UPDATE projects
            SET calendar_event_id = NULL, updated_at = NOW()
            WHERE id = $1
            "#,
        )
        .bind(project_id)
        .execute(&*pool)
        .await?;

        Ok(true)
    }

    /// Create a calendar event for a service and link it
    async fn create_service_calendar_event(
        &self,
        ctx: &Context<'_>,
        service_id: Uuid,
        input: CreateCalendarEventInput,
    ) -> Result<CalendarEventType> {
        let db = ctx.data::<Database>()?;
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;
        let pool = db.pool().await;

        // Create the event
        let request = CreateEventRequest::from(input);
        let event = calendar.create_event(request).await?;

        // Link to service
        sqlx::query(
            r#"
            UPDATE services
            SET calendar_event_id = $2, updated_at = NOW()
            WHERE id = $1
            "#,
        )
        .bind(service_id)
        .bind(&event.id)
        .execute(&*pool)
        .await?;

        Ok(CalendarEventType::from(event))
    }

    /// Create a calendar event for a project and link it
    async fn create_project_calendar_event(
        &self,
        ctx: &Context<'_>,
        project_id: Uuid,
        input: CreateCalendarEventInput,
    ) -> Result<CalendarEventType> {
        let db = ctx.data::<Database>()?;
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;
        let pool = db.pool().await;

        // Create the event
        let request = CreateEventRequest::from(input);
        let event = calendar.create_event(request).await?;

        // Link to project
        sqlx::query(
            r#"
            UPDATE projects
            SET calendar_event_id = $2, updated_at = NOW()
            WHERE id = $1
            "#,
        )
        .bind(project_id)
        .bind(&event.id)
        .execute(&*pool)
        .await?;

        Ok(CalendarEventType::from(event))
    }

    /// Delete a service's calendar event and unlink it
    async fn delete_service_calendar_event(
        &self,
        ctx: &Context<'_>,
        service_id: Uuid,
    ) -> Result<bool> {
        let db = ctx.data::<Database>()?;
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;
        let pool = db.pool().await;

        // Get the current calendar event ID
        let service = sqlx::query_as::<_, Service>(
            r#"
            SELECT id, created_at, updated_at, account_id, account_address_id, date, status, notes, calendar_event_id
            FROM services
            WHERE id = $1
            "#,
        )
        .bind(service_id)
        .fetch_one(&*pool)
        .await?;

        if let Some(event_id) = service.calendar_event_id {
            // Delete from Google Calendar
            calendar.delete_event(&event_id).await?;

            // Unlink from service
            sqlx::query(
                r#"
                UPDATE services
                SET calendar_event_id = NULL, updated_at = NOW()
                WHERE id = $1
                "#,
            )
            .bind(service_id)
            .execute(&*pool)
            .await?;
        }

        Ok(true)
    }

    /// Delete a project's calendar event and unlink it
    async fn delete_project_calendar_event(
        &self,
        ctx: &Context<'_>,
        project_id: Uuid,
    ) -> Result<bool> {
        let db = ctx.data::<Database>()?;
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;
        let pool = db.pool().await;

        // Get the current calendar event ID
        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(project_id)
        .fetch_one(&*pool)
        .await?;

        if let Some(event_id) = project.calendar_event_id {
            // Delete from Google Calendar
            calendar.delete_event(&event_id).await?;

            // Unlink from project
            sqlx::query(
                r#"
                UPDATE projects
                SET calendar_event_id = NULL, updated_at = NOW()
                WHERE id = $1
                "#,
            )
            .bind(project_id)
            .execute(&*pool)
            .await?;
        }

        Ok(true)
    }
}
