use chrono::{DateTime, Datelike, Duration, TimeZone, Utc, Weekday, Timelike};
use sqlx::PgPool;
use tokio::time::{interval, Duration as TokioDuration};
use tracing::{info, error};

use crate::error::{Result, ApiError};

pub async fn start_recurring_events_scheduler(pool: PgPool) {
    tokio::spawn(async move {
        let mut interval = interval(TokioDuration::from_secs(6 * 60 * 60)); // Every 6 hours
        
        loop {
            interval.tick().await;
            if let Err(e) = update_recurring_events(&pool).await {
                error!("Error updating recurring events: {:?}", e);
            }
        }
    });
}

async fn update_recurring_events(pool: &PgPool) -> Result<()> {
    info!("Starting recurring events update");
    
    let expired_events = sqlx::query!(
        "SELECT id, title, start_time, end_time, recurring_type 
         FROM events 
         WHERE recurring_type IS NOT NULL 
         AND start_time < NOW()"
    )
    .fetch_all(pool)
    .await
    .map_err(|e| ApiError::ValidationError(format!("Database error: {}", e)))?;

    for event in expired_events {
        if let Some(recurring_type) = &event.recurring_type {
            let duration = event.end_time - event.start_time;
            let next_start = calculate_next_occurrence(&event.start_time, recurring_type);
            let next_end = next_start + duration;

            info!("Updating {} event: {} from {} to {}", 
                recurring_type, event.title, event.start_time, next_start);

            sqlx::query!(
                "UPDATE events SET start_time = $1, end_time = $2 WHERE id = $3",
                next_start,
                next_end,
                event.id
            )
            .execute(pool)
            .await
            .map_err(|e| ApiError::ValidationError(format!("Update error: {}", e)))?;
        }
    }

    Ok(())
}

fn calculate_next_occurrence(current_start: &DateTime<Utc>, recurring_type: &str) -> DateTime<Utc> {
    let now = Utc::now();
    let mut next_start = *current_start;

    match recurring_type {
        "DAILY" => {
            while next_start < now {
                next_start = next_start + Duration::days(1);
                // Skip Saturdays for daily events
                if next_start.weekday() == Weekday::Sat {
                    next_start = next_start + Duration::days(1);
                }
            }
        }
        "WEEKLY" => {
            while next_start < now {
                next_start = next_start + Duration::weeks(1);
            }
        }
        "BIWEEKLY" => {
            while next_start < now {
                next_start = next_start + Duration::weeks(2);
            }
        }
        "first_tuesday" => {
            // Find first Tuesday of current month first
            let current_month_first = Utc.with_ymd_and_hms(now.year(), now.month(), 1, 0, 0, 0).unwrap();
            let mut first_tuesday_current = current_month_first;
            while first_tuesday_current.weekday() != Weekday::Tue {
                first_tuesday_current = first_tuesday_current + Duration::days(1);
            }
            
            // Set to the same time as the original event
            let first_tuesday_current_time = Utc.with_ymd_and_hms(
                first_tuesday_current.year(),
                first_tuesday_current.month(), 
                first_tuesday_current.day(),
                current_start.hour(),
                current_start.minute(),
                0
            ).unwrap();
            
            // If we haven't passed the first Tuesday of current month, use it
            if first_tuesday_current_time > now {
                next_start = first_tuesday_current_time;
            } else {
                // Otherwise, find first Tuesday of next month
                let next_month_date = if now.month() == 12 {
                    Utc.with_ymd_and_hms(now.year() + 1, 1, 1, 0, 0, 0).unwrap()
                } else {
                    Utc.with_ymd_and_hms(now.year(), now.month() + 1, 1, 0, 0, 0).unwrap()
                };
                
                let mut first_tuesday_next = next_month_date;
                while first_tuesday_next.weekday() != Weekday::Tue {
                    first_tuesday_next = first_tuesday_next + Duration::days(1);
                }
                
                next_start = Utc.with_ymd_and_hms(
                    first_tuesday_next.year(), 
                    first_tuesday_next.month(), 
                    first_tuesday_next.day(),
                    current_start.hour(),
                    current_start.minute(),
                    0
                ).unwrap();
            }
        }
        "2nd/3rd Saturday Monthly" => {
            // Determine if current event is on 2nd or 3rd Saturday
            let current_month_first = Utc.with_ymd_and_hms(current_start.year(), current_start.month(), 1, 0, 0, 0).unwrap();
            let mut first_saturday = current_month_first;
            while first_saturday.weekday() != Weekday::Sat {
                first_saturday = first_saturday + Duration::days(1);
            }
            
            let second_saturday = first_saturday + Duration::days(7);
            
            // Check if current event is on 2nd or 3rd Saturday
            let is_second_saturday = current_start.day() == second_saturday.day();
            
            // Calculate next month
            let (next_year, next_month) = if current_start.month() == 12 {
                (current_start.year() + 1, 1)
            } else {
                (current_start.year(), current_start.month() + 1)
            };
            
            let next_month_first = Utc.with_ymd_and_hms(next_year, next_month, 1, 0, 0, 0).unwrap();
            let mut next_first_saturday = next_month_first;
            while next_first_saturday.weekday() != Weekday::Sat {
                next_first_saturday = next_first_saturday + Duration::days(1);
            }
            
            // Alternate: if current is 2nd Saturday, next is 3rd Saturday; if current is 3rd Saturday, next is 2nd Saturday
            let target_saturday = if is_second_saturday {
                next_first_saturday + Duration::days(14) // 3rd Saturday
            } else {
                next_first_saturday + Duration::days(7)  // 2nd Saturday
            };
            
            next_start = Utc.with_ymd_and_hms(
                target_saturday.year(),
                target_saturday.month(),
                target_saturday.day(),
                current_start.hour(),
                current_start.minute(),
                0
            ).unwrap();
        }
        _ => {
            // Unknown recurring type, don't update
            return next_start;
        }
    }

    next_start
}
