//! Google Calendar API service

use chrono::{DateTime, Utc};
use reqwest::{Client, Response};
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use std::sync::Arc;
use thiserror::Error;

use super::google_auth::{GoogleAuthService, GOOGLE_CALENDAR_SCOPE};

const CALENDAR_API_BASE: &str = "https://www.googleapis.com/calendar/v3";

#[derive(Debug, Error)]
pub enum CalendarError {
    #[error("Authentication failed: {0}")]
    Auth(#[from] super::google_auth::GoogleAuthError),
    #[error("Request failed: {0}")]
    Request(String),
    #[error("Failed to parse response: {0}")]
    Parse(String),
    #[error("Event not found")]
    NotFound,
    #[error("Invalid event ID: {0}")]
    InvalidEventId(String),
    #[error("Google Calendar API error: {0}")]
    ApiError(String),
    #[error("Calendar not configured")]
    NotConfigured,
}

/// Reminder configuration for an event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventReminder {
    /// Method: "email" or "popup"
    pub method: String,
    /// Minutes before the event
    pub minutes: i32,
}

/// Reminders settings for an event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventReminders {
    #[serde(rename = "useDefault")]
    pub use_default: bool,
    pub overrides: Option<Vec<EventReminder>>,
}

/// Date/time for an event (supports both timed and all-day events)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventDateTime {
    #[serde(rename = "dateTime", skip_serializing_if = "Option::is_none")]
    pub date_time: Option<DateTime<Utc>>,
    /// For all-day events (YYYY-MM-DD format)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub date: Option<String>,
    #[serde(rename = "timeZone", skip_serializing_if = "Option::is_none")]
    pub time_zone: Option<String>,
}

/// Event attendee
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attendee {
    pub email: String,
    #[serde(rename = "displayName", skip_serializing_if = "Option::is_none")]
    pub display_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub optional: Option<bool>,
    #[serde(rename = "responseStatus", skip_serializing_if = "Option::is_none")]
    pub response_status: Option<String>,
}

/// Calendar event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalendarEvent {
    pub id: String,
    pub summary: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub location: Option<String>,
    pub start: EventDateTime,
    pub end: EventDateTime,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attendees: Option<Vec<Attendee>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reminders: Option<EventReminders>,
    #[serde(rename = "colorId", skip_serializing_if = "Option::is_none")]
    pub color_id: Option<String>,
    #[serde(rename = "htmlLink", skip_serializing_if = "Option::is_none")]
    pub html_link: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub created: Option<DateTime<Utc>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub updated: Option<DateTime<Utc>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,
}

/// Request to create a calendar event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateEventRequest {
    /// Custom event ID (optional, must be 5-1024 chars, a-v and 0-9 only)
    pub id: Option<String>,
    pub summary: String,
    pub description: Option<String>,
    pub location: Option<String>,
    pub start: EventDateTime,
    pub end: EventDateTime,
    pub attendees: Option<Vec<Attendee>>,
    pub reminders: Option<EventReminders>,
    #[serde(rename = "colorId")]
    pub color_id: Option<String>,
}

/// Request to update a calendar event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateEventRequest {
    pub summary: Option<String>,
    pub description: Option<String>,
    pub location: Option<String>,
    pub start: Option<EventDateTime>,
    pub end: Option<EventDateTime>,
    pub attendees: Option<Vec<Attendee>>,
    pub reminders: Option<EventReminders>,
    #[serde(rename = "colorId")]
    pub color_id: Option<String>,
}

/// Query parameters for listing events
#[derive(Debug, Clone, Default)]
pub struct ListEventsQuery {
    pub time_min: Option<DateTime<Utc>>,
    pub time_max: Option<DateTime<Utc>>,
    pub max_results: Option<u32>,
    pub q: Option<String>,
    pub single_events: Option<bool>,
    pub order_by: Option<String>,
}

/// Google Calendar service
pub struct GoogleCalendarService {
    client: Client,
    auth: Arc<GoogleAuthService>,
    calendar_id: String,
    /// Email to impersonate via Domain-Wide Delegation
    impersonate_user: String,
}

impl GoogleCalendarService {
    /// Create a new calendar service
    ///
    /// `impersonate_user` is the email address to impersonate via Domain-Wide Delegation.
    /// This is required for operations like inviting attendees.
    pub fn new(auth: Arc<GoogleAuthService>, calendar_id: String, impersonate_user: String) -> Self {
        Self {
            client: Client::new(),
            auth,
            calendar_id,
            impersonate_user,
        }
    }

    /// Create a new calendar event
    pub async fn create_event(&self, request: CreateEventRequest) -> Result<CalendarEvent, CalendarError> {
        if let Some(ref id) = request.id {
            validate_event_id(id)?;
        }

        let url = format!("{}/calendars/{}/events", CALENDAR_API_BASE, urlencoding::encode(&self.calendar_id));
        let event_body = build_create_event_body(request);

        let response = self.authenticated_post(&url, &event_body).await?;
        handle_event_response(response).await
    }

    /// Get an event by ID
    pub async fn get_event(&self, event_id: &str) -> Result<CalendarEvent, CalendarError> {
        let url = format!(
            "{}/calendars/{}/events/{}",
            CALENDAR_API_BASE,
            urlencoding::encode(&self.calendar_id),
            urlencoding::encode(event_id)
        );

        let response = self.authenticated_get(&url).await?;
        handle_event_response_with_404(response).await
    }

    /// List events
    pub async fn list_events(&self, query: ListEventsQuery) -> Result<Vec<CalendarEvent>, CalendarError> {
        let url = build_list_events_url(&self.calendar_id, query);
        let response = self.authenticated_get(&url).await?;
        handle_list_events_response(response).await
    }

    /// Update an event
    pub async fn update_event(
        &self,
        event_id: &str,
        request: UpdateEventRequest,
    ) -> Result<CalendarEvent, CalendarError> {
        let existing = self.get_event(event_id).await?;
        let url = format!(
            "{}/calendars/{}/events/{}",
            CALENDAR_API_BASE,
            urlencoding::encode(&self.calendar_id),
            urlencoding::encode(event_id)
        );
        let event_body = build_update_event_body(request, existing);

        let response = self.authenticated_put(&url, &event_body).await?;
        handle_event_response_with_404(response).await
    }

    /// Delete an event
    pub async fn delete_event(&self, event_id: &str) -> Result<(), CalendarError> {
        let url = format!(
            "{}/calendars/{}/events/{}",
            CALENDAR_API_BASE,
            urlencoding::encode(&self.calendar_id),
            urlencoding::encode(event_id)
        );

        let response = self.authenticated_delete(&url).await?;
        handle_delete_response(response).await
    }

    /// Get the calendar ID
    pub fn calendar_id(&self) -> &str {
        &self.calendar_id
    }

    // Private helpers

    async fn authenticated_get(&self, url: &str) -> Result<Response, CalendarError> {
        let token = self.auth.get_access_token(GOOGLE_CALENDAR_SCOPE, Some(&self.impersonate_user)).await?;
        self.client
            .get(url)
            .header("Authorization", format!("Bearer {}", token))
            .send()
            .await
            .map_err(|e| CalendarError::Request(e.to_string()))
    }

    async fn authenticated_post(&self, url: &str, body: &Value) -> Result<Response, CalendarError> {
        let token = self.auth.get_access_token(GOOGLE_CALENDAR_SCOPE, Some(&self.impersonate_user)).await?;
        self.client
            .post(url)
            .header("Authorization", format!("Bearer {}", token))
            .header("Content-Type", "application/json")
            .json(body)
            .send()
            .await
            .map_err(|e| CalendarError::Request(e.to_string()))
    }

    async fn authenticated_put(&self, url: &str, body: &Value) -> Result<Response, CalendarError> {
        let token = self.auth.get_access_token(GOOGLE_CALENDAR_SCOPE, Some(&self.impersonate_user)).await?;
        self.client
            .put(url)
            .header("Authorization", format!("Bearer {}", token))
            .header("Content-Type", "application/json")
            .json(body)
            .send()
            .await
            .map_err(|e| CalendarError::Request(e.to_string()))
    }

    async fn authenticated_delete(&self, url: &str) -> Result<Response, CalendarError> {
        let token = self.auth.get_access_token(GOOGLE_CALENDAR_SCOPE, Some(&self.impersonate_user)).await?;
        self.client
            .delete(url)
            .header("Authorization", format!("Bearer {}", token))
            .send()
            .await
            .map_err(|e| CalendarError::Request(e.to_string()))
    }
}

// Helper functions

fn validate_event_id(id: &str) -> Result<(), CalendarError> {
    if id.len() < 5 || id.len() > 1024 {
        return Err(CalendarError::InvalidEventId(
            "ID must be between 5 and 1024 characters".to_string(),
        ));
    }

    for ch in id.chars() {
        if !ch.is_ascii_lowercase() && !ch.is_ascii_digit() {
            return Err(CalendarError::InvalidEventId(
                "ID can only contain lowercase letters a-v and digits 0-9".to_string(),
            ));
        }
        if ch.is_ascii_lowercase() && ch > 'v' {
            return Err(CalendarError::InvalidEventId(
                "ID can only contain lowercase letters a-v and digits 0-9".to_string(),
            ));
        }
    }

    Ok(())
}

fn build_create_event_body(request: CreateEventRequest) -> Value {
    let mut body = json!({
        "summary": request.summary,
        "start": request.start,
        "end": request.end
    });

    if let Some(id) = request.id {
        body["id"] = json!(id);
    }
    if let Some(description) = request.description {
        body["description"] = json!(description);
    }
    if let Some(location) = request.location {
        body["location"] = json!(location);
    }
    if let Some(attendees) = request.attendees {
        body["attendees"] = json!(attendees);
    }
    if let Some(reminders) = request.reminders {
        body["reminders"] = json!(reminders);
    }
    if let Some(color_id) = request.color_id {
        body["colorId"] = json!(color_id);
    }

    body
}

fn build_update_event_body(request: UpdateEventRequest, existing: CalendarEvent) -> Value {
    let mut body = json!({
        "summary": request.summary.unwrap_or(existing.summary),
        "start": request.start.unwrap_or(existing.start),
        "end": request.end.unwrap_or(existing.end)
    });

    if let Some(description) = request.description.or(existing.description) {
        body["description"] = json!(description);
    }
    if let Some(location) = request.location.or(existing.location) {
        body["location"] = json!(location);
    }
    if let Some(attendees) = request.attendees.or(existing.attendees) {
        body["attendees"] = json!(attendees);
    }
    if let Some(reminders) = request.reminders.or(existing.reminders) {
        body["reminders"] = json!(reminders);
    }
    if let Some(color_id) = request.color_id.or(existing.color_id) {
        body["colorId"] = json!(color_id);
    }

    body
}

fn build_list_events_url(calendar_id: &str, query: ListEventsQuery) -> String {
    let mut url = format!(
        "{}/calendars/{}/events",
        CALENDAR_API_BASE,
        urlencoding::encode(calendar_id)
    );
    let mut params = Vec::new();

    if let Some(time_min) = query.time_min {
        params.push(format!("timeMin={}", urlencoding::encode(&time_min.to_rfc3339())));
    }
    if let Some(time_max) = query.time_max {
        params.push(format!("timeMax={}", urlencoding::encode(&time_max.to_rfc3339())));
    }
    if let Some(max_results) = query.max_results {
        params.push(format!("maxResults={}", max_results));
    }
    if let Some(q) = query.q {
        params.push(format!("q={}", urlencoding::encode(&q)));
    }
    if let Some(single_events) = query.single_events {
        params.push(format!("singleEvents={}", single_events));
    }
    if let Some(order_by) = query.order_by {
        params.push(format!("orderBy={}", urlencoding::encode(&order_by)));
    }

    if !params.is_empty() {
        url.push('?');
        url.push_str(&params.join("&"));
    }

    url
}

async fn handle_event_response(response: Response) -> Result<CalendarEvent, CalendarError> {
    if response.status().is_success() {
        response
            .json()
            .await
            .map_err(|e| CalendarError::Parse(e.to_string()))
    } else {
        Err(handle_error_response(response).await)
    }
}

async fn handle_event_response_with_404(response: Response) -> Result<CalendarEvent, CalendarError> {
    match response.status().as_u16() {
        200 => response
            .json()
            .await
            .map_err(|e| CalendarError::Parse(e.to_string())),
        404 => Err(CalendarError::NotFound),
        _ => Err(handle_error_response(response).await),
    }
}

async fn handle_list_events_response(response: Response) -> Result<Vec<CalendarEvent>, CalendarError> {
    if response.status().is_success() {
        let json: Value = response
            .json()
            .await
            .map_err(|e| CalendarError::Parse(e.to_string()))?;

        let events = json["items"]
            .as_array()
            .unwrap_or(&vec![])
            .iter()
            .filter_map(|item| serde_json::from_value(item.clone()).ok())
            .collect();

        Ok(events)
    } else {
        Err(handle_error_response(response).await)
    }
}

async fn handle_delete_response(response: Response) -> Result<(), CalendarError> {
    match response.status().as_u16() {
        204 => Ok(()),
        404 => Err(CalendarError::NotFound),
        _ => Err(handle_error_response(response).await),
    }
}

async fn handle_error_response(response: Response) -> CalendarError {
    let status = response.status();
    let error_text = response
        .text()
        .await
        .unwrap_or_else(|_| "Unknown error".to_string());

    CalendarError::ApiError(format!("HTTP {}: {}", status, error_text))
}
