use async_graphql::{Context, Object, Result};
use chrono::{DateTime, Utc};
use std::sync::Arc;

use crate::graphql::types::{CalendarEventFilterInput, CalendarEventType};
use crate::services::google_calendar::{GoogleCalendarService, ListEventsQuery};

#[derive(Default)]
pub struct CalendarQuery;

#[Object]
impl CalendarQuery {
    /// Get a calendar event by ID
    async fn calendar_event(
        &self,
        ctx: &Context<'_>,
        event_id: String,
    ) -> Result<Option<CalendarEventType>> {
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;

        match calendar.get_event(&event_id).await {
            Ok(event) => Ok(Some(CalendarEventType::from(event))),
            Err(crate::services::google_calendar::CalendarError::NotFound) => Ok(None),
            Err(e) => Err(e.into()),
        }
    }

    /// List calendar events
    async fn calendar_events(
        &self,
        ctx: &Context<'_>,
        filter: Option<CalendarEventFilterInput>,
    ) -> Result<Vec<CalendarEventType>> {
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;

        let filter = filter.unwrap_or_default();
        let query = ListEventsQuery {
            time_min: filter.time_min,
            time_max: filter.time_max,
            max_results: filter.max_results.map(|v| v as u32),
            q: filter.q,
            single_events: Some(true),
            order_by: Some("startTime".to_string()),
        };

        let events = calendar.list_events(query).await?;

        Ok(events.into_iter().map(CalendarEventType::from).collect())
    }

    /// Get calendar events for today
    async fn todays_events(
        &self,
        ctx: &Context<'_>,
    ) -> Result<Vec<CalendarEventType>> {
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;

        let now = Utc::now();
        let start_of_day = now.date_naive().and_hms_opt(0, 0, 0).unwrap();
        let end_of_day = now.date_naive().and_hms_opt(23, 59, 59).unwrap();

        let query = ListEventsQuery {
            time_min: Some(DateTime::from_naive_utc_and_offset(start_of_day, Utc)),
            time_max: Some(DateTime::from_naive_utc_and_offset(end_of_day, Utc)),
            max_results: Some(50),
            q: None,
            single_events: Some(true),
            order_by: Some("startTime".to_string()),
        };

        let events = calendar.list_events(query).await?;

        Ok(events.into_iter().map(CalendarEventType::from).collect())
    }

    /// Get upcoming calendar events (next 7 days)
    async fn upcoming_events(
        &self,
        ctx: &Context<'_>,
        days: Option<i32>,
    ) -> Result<Vec<CalendarEventType>> {
        let calendar = ctx.data::<Arc<GoogleCalendarService>>()?;

        let days = days.unwrap_or(7) as i64;
        let now = Utc::now();
        let end = now + chrono::Duration::days(days);

        let query = ListEventsQuery {
            time_min: Some(now),
            time_max: Some(end),
            max_results: Some(100),
            q: None,
            single_events: Some(true),
            order_by: Some("startTime".to_string()),
        };

        let events = calendar.list_events(query).await?;

        Ok(events.into_iter().map(CalendarEventType::from).collect())
    }
}
