use chrono::{DateTime, Datelike, Duration, NaiveDate, Utc, Weekday};
use gloo_storage::{LocalStorage, Storage};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response};

// Import RFC 5545 compliant VEvent and VAlarm from shared library
use calendar_models::{VEvent, VAlarm};

// Create type alias for backward compatibility
pub type CalendarEvent = VEvent;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct EventReminder {
    pub minutes_before: i32,
    pub action: ReminderAction,
    pub description: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ReminderAction {
    Display,
    Email,
    Audio,
}

impl Default for ReminderAction {
    fn default() -> Self {
        ReminderAction::Display
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UserInfo {
    pub username: String,
    pub server_url: String,
    pub calendars: Vec<CalendarInfo>,
    #[serde(default = "default_timestamp")]
    pub last_updated: u64,
}

fn default_timestamp() -> u64 {
    0
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CalendarInfo {
    pub path: String,
    pub display_name: String,
    pub color: String,
    pub is_visible: bool,
}

// CalendarEvent, EventStatus, and EventClass are now imported from shared library

// ==================== V2 API MODELS ====================

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AttendeeV2 {
    pub email: String,
    pub name: Option<String>,
    pub role: Option<AttendeeRoleV2>,
    pub status: Option<ParticipationStatusV2>,
    pub rsvp: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum AttendeeRoleV2 {
    Chair,
    Required,
    Optional,
    NonParticipant,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ParticipationStatusV2 {
    NeedsAction,
    Accepted,
    Declined,
    Tentative,
    Delegated,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AlarmV2 {
    pub action: AlarmActionV2,
    pub trigger_minutes: i32, // Minutes before event (negative = before)
    pub description: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum AlarmActionV2 {
    Audio,
    Display,
    Email,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum DeleteActionV2 {
    DeleteThis,
    DeleteFollowing,
    DeleteSeries,
}

// V2 Request/Response Models

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct EventSummaryV2 {
    pub uid: String,
    pub summary: Option<String>,
    pub dtstart: DateTime<Utc>,
    pub dtend: Option<DateTime<Utc>>,
    pub location: Option<String>,
    pub all_day: bool,
    pub href: Option<String>,
    pub etag: Option<String>,
}

pub struct CalendarService {
    base_url: String,
}

impl CalendarService {
    pub fn new() -> Self {
        let base_url = option_env!("BACKEND_API_URL")
            .unwrap_or("http://localhost:3000/api")
            .to_string();

        Self { base_url }
    }

    /// Fetch user info including available calendars
    pub async fn fetch_user_info(&self, token: &str, password: &str) -> Result<UserInfo, String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("GET");
        opts.set_mode(RequestMode::Cors);

        let url = format!("{}/user/info", self.base_url);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("X-CalDAV-Password", password)
            .map_err(|e| format!("Password header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            let user_info: UserInfo = serde_json::from_str(&text_string)
                .map_err(|e| format!("JSON parsing failed: {}", e))?;
            Ok(user_info)
        } else {
            Err(format!(
                "Request failed with status {}: {}",
                resp.status(),
                text_string
            ))
        }
    }

    /// Fetch calendar events for a specific month
    /// Fetch events for month as VEvent with RFC 5545 compliance  
    pub async fn fetch_events_for_month_vevent(
        &self,
        token: &str,
        password: &str,
        year: i32,
        month: u32,
    ) -> Result<Vec<VEvent>, String> {
        // Since CalendarEvent is now a type alias for VEvent, just call the main method
        self.fetch_events_for_month(token, password, year, month)
            .await
    }

    pub async fn fetch_events_for_month(
        &self,
        token: &str,
        password: &str,
        year: i32,
        month: u32,
    ) -> Result<Vec<CalendarEvent>, String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("GET");
        opts.set_mode(RequestMode::Cors);

        let url = format!(
            "{}/calendar/events?year={}&month={}",
            self.base_url, year, month
        );
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("X-CalDAV-Password", password)
            .map_err(|e| format!("Password header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            let events: Vec<CalendarEvent> = serde_json::from_str(&text_string)
                .map_err(|e| format!("JSON parsing failed: {}", e))?;
            
            
            Ok(events)
        } else {
            Err(format!(
                "Request failed with status {}: {}",
                resp.status(),
                text_string
            ))
        }
    }

    /// Convert events to a HashMap grouped by date for calendar display
    /// Internally uses VEvent for RFC 5545 compliance while maintaining CalendarEvent API
    /// Group VEvents by date with RFC 5545 compliance
    pub fn group_events_by_date(events: Vec<VEvent>) -> HashMap<NaiveDate, Vec<VEvent>> {
        let mut grouped = HashMap::new();

        // Expand recurring events using VEvent (better RFC 5545 compliance)
        let expanded_vevents = Self::expand_recurring_events(events);

        // Group by date
        for vevent in expanded_vevents {
            let date = vevent.get_date();
            grouped.entry(date).or_insert_with(Vec::new).push(vevent);
        }

        grouped
    }

    /// Convert UTC events to local timezone for display
    fn convert_utc_to_local(mut event: VEvent) -> VEvent {
        // All-day events should not have timezone conversions applied
        if event.all_day {
            return event;
        }
        
        // Check if event times are in UTC (legacy events from before timezone migration)
        let is_utc_event = event.dtstart_tzid.as_ref().map_or(true, |tz| tz == "UTC");
        
        if is_utc_event {
            
            // Get current timezone offset (convert from UTC to local)
            let date = js_sys::Date::new_0();
            let timezone_offset_minutes = date.get_timezone_offset() as i32;
            
            // Convert start time from UTC to local
            // getTimezoneOffset() returns minutes UTC is ahead of local time
            // To convert UTC to local, we subtract the offset (add negative offset)
            let local_start = event.dtstart + chrono::Duration::minutes(-timezone_offset_minutes as i64);
            event.dtstart = local_start;
            event.dtstart_tzid = None; // Clear UTC timezone indicator
            
            // Convert end time if present
            if let Some(end_utc) = event.dtend {
                let local_end = end_utc + chrono::Duration::minutes(-timezone_offset_minutes as i64);
                event.dtend = Some(local_end);
                event.dtend_tzid = None; // Clear UTC timezone indicator
            }
            
            // Convert created/modified times if present
            if let Some(created_utc) = event.created {
                event.created = Some(created_utc + chrono::Duration::minutes(-timezone_offset_minutes as i64));
                event.created_tzid = None;
            }
            
            if let Some(modified_utc) = event.last_modified {
                event.last_modified = Some(modified_utc + chrono::Duration::minutes(-timezone_offset_minutes as i64));
                event.last_modified_tzid = None;
            }
            
            // Convert EXDATE entries from UTC to local time
            event.exdate = event.exdate.into_iter()
                .map(|exdate| exdate + chrono::Duration::minutes(-timezone_offset_minutes as i64))
                .collect();
        }
        
        event
    }

    /// Expand recurring events using VEvent (RFC 5545 compliant)
    pub fn expand_recurring_events(events: Vec<VEvent>) -> Vec<VEvent> {
        let mut expanded_events = Vec::new();
        let today = chrono::Local::now().date_naive();
        let start_range = today - Duration::days(36500); // Show past 100 years (to catch any historical yearly events)
        let end_range = today + Duration::days(36500); // Show next 100 years

        for event in events {
            // Convert UTC events to local time for proper display
            let event = Self::convert_utc_to_local(event);
            if let Some(ref rrule) = event.rrule {
                // Generate occurrences for recurring events using VEvent
                let occurrences = Self::generate_occurrences(&event, rrule, start_range, end_range);
                expanded_events.extend(occurrences);
            } else {
                // Non-recurring event - add as-is
                expanded_events.push(event);
            }
        }

        expanded_events
    }

    /// Generate occurrence dates for a recurring VEvent based on RRULE (RFC 5545 compliant)
    fn generate_occurrences(
        base_event: &VEvent,
        rrule: &str,
        start_range: NaiveDate,
        end_range: NaiveDate,
    ) -> Vec<VEvent> {
        let mut occurrences = Vec::new();

        // Parse RRULE components
        let rrule_upper = rrule.to_uppercase();

        let components: HashMap<String, String> = rrule_upper
            .split(';')
            .filter_map(|part| {
                let mut split = part.split('=');
                if let (Some(key), Some(value)) = (split.next(), split.next()) {
                    Some((key.to_string(), value.to_string()))
                } else {
                    None
                }
            })
            .collect();

        // Get frequency
        let freq = components
            .get("FREQ")
            .map(|s| s.as_str())
            .unwrap_or("DAILY");

        // Get interval (default 1)
        let interval: i32 = components
            .get("INTERVAL")
            .and_then(|s| s.parse().ok())
            .unwrap_or(1);

        // Get count limit (default 100 to prevent infinite loops)
        let count: usize = components
            .get("COUNT")
            .and_then(|s| s.parse().ok())
            .unwrap_or(100)
            .min(365); // Cap at 365 occurrences for performance

        // Get UNTIL date if specified
        let until_date = components.get("UNTIL").and_then(|until_str| {
            // Parse UNTIL date in YYYYMMDDTHHMMSSZ format (treat as local time)
            if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(
                until_str.trim_end_matches('Z'),
                "%Y%m%dT%H%M%S",
            ) {
                Some(dt)
            } else if let Ok(dt) = chrono::DateTime::parse_from_str(until_str, "%Y%m%dT%H%M%SZ") {
                // Convert UTC to local (naive) time for consistency
                Some(dt.naive_utc())
            } else if let Ok(date) = chrono::NaiveDate::parse_from_str(until_str, "%Y%m%d") {
                // Handle date-only UNTIL
                Some(date.and_hms_opt(23, 59, 59).unwrap())
            } else {
                web_sys::console::log_1(
                    &format!("⚠️ Failed to parse UNTIL date: {}", until_str).into(),
                );
                None
            }
        });

        if let Some(_until) = until_date {
        }

        let start_date = base_event.dtstart.date();
        let mut current_date = start_date;
        let mut occurrence_count = 0;

        // Generate occurrences based on frequency
        while current_date <= end_range && occurrence_count < count {
            // Check UNTIL constraint - stop if current occurrence is after UNTIL date
            if let Some(until) = until_date {
                let current_datetime = base_event.dtstart
                    + Duration::days(current_date.signed_duration_since(start_date).num_days());
                if current_datetime > until {
                    break;
                }
            }

            if current_date >= start_range {
                // Calculate the occurrence datetime
                let days_diff = current_date.signed_duration_since(start_date).num_days();
                let occurrence_datetime = base_event.dtstart + Duration::days(days_diff);

                // Check if this occurrence is in the exception dates (EXDATE)
                let is_exception = base_event.exdate.iter().any(|exception_date| {
                    // EXDATE from server is in local time, but stored as NaiveDateTime
                    // We need to compare both as local time (naive datetimes) instead of UTC
                    let exception_naive = *exception_date;
                    let occurrence_naive = occurrence_datetime;

                    // Check if dates match (within a minute to handle minor time differences)
                    let diff = occurrence_naive - exception_naive;
                    diff.num_seconds().abs() < 60
                });

                if !is_exception {
                    // Create occurrence event with updated DTSTAMP
                    let mut occurrence_event = base_event.clone();
                    occurrence_event.dtstart = occurrence_datetime;
                    occurrence_event.dtstamp = chrono::Utc::now(); // Update DTSTAMP for each occurrence
                    

                    if let Some(base_end) = base_event.dtend {
                        if base_event.all_day {
                            // For all-day events, maintain the RFC-5545 end date pattern
                            // End date should always be exactly one day after start date
                            occurrence_event.dtend = Some(occurrence_datetime + Duration::days(1));
                        } else {
                            // For timed events, preserve the original duration
                            occurrence_event.dtend = Some(base_end + Duration::days(days_diff));
                        }
                    }

                    occurrences.push(occurrence_event);
                    occurrence_count += 1; // Only count when we actually add an occurrence
                }
            }

            // Calculate next occurrence date
            match freq {
                "DAILY" => {
                    current_date = current_date + Duration::days(interval as i64);
                }
                "WEEKLY" => {
                    if let Some(byday) = components.get("BYDAY") {
                        // For BYDAY weekly events, we need to handle multiple days per week
                        // Break out of the single-occurrence loop and use specialized logic
                        return Self::generate_weekly_byday_occurrences(
                            base_event,
                            byday,
                            interval,
                            start_range,
                            end_range,
                            until_date,
                            count,
                        );
                    } else {
                        current_date = current_date + Duration::weeks(interval as i64);
                    }
                }
                "MONTHLY" => {
                    // Handle MONTHLY with BYMONTHDAY or BYDAY
                    if let Some(bymonthday) = components.get("BYMONTHDAY") {
                        // Monthly by day of month (e.g., 15th of every month)
                        return Self::generate_monthly_bymonthday_occurrences(
                            base_event,
                            bymonthday,
                            interval,
                            start_range,
                            end_range,
                            until_date,
                            count,
                        );
                    } else if let Some(byday) = components.get("BYDAY") {
                        // Monthly by weekday position (e.g., first Monday)
                        return Self::generate_monthly_byday_occurrences(
                            base_event,
                            byday,
                            interval,
                            start_range,
                            end_range,
                            until_date,
                            count,
                        );
                    } else {
                        // Simple monthly increment (same day of month)
                        if let Some(next_month) = Self::add_months(current_date, interval) {
                            current_date = next_month;
                        } else {
                            break; // Invalid date
                        }
                    }
                }
                "YEARLY" => {
                    if let Some(bymonth) = components.get("BYMONTH") {
                        // Yearly with specific months (e.g., January, March, June)
                        return Self::generate_yearly_bymonth_occurrences(
                            base_event,
                            bymonth,
                            interval,
                            start_range,
                            end_range,
                            until_date,
                            count,
                        );
                    } else {
                        // Simple yearly increment (same date each year)
                        if let Some(next_year) = Self::add_years(current_date, interval) {
                            current_date = next_year;
                        } else {
                            break; // Invalid date
                        }
                    }
                }
                _ => break, // Unsupported frequency
            }
        }

        occurrences
    }

    /// Generate occurrences for WEEKLY frequency with BYDAY (handles multiple days per week)
    fn generate_weekly_byday_occurrences(
        base_event: &VEvent,
        byday: &str,
        interval: i32,
        start_range: NaiveDate,
        end_range: NaiveDate,
        until_date: Option<chrono::NaiveDateTime>,
        count: usize,
    ) -> Vec<VEvent> {
        let mut occurrences = Vec::new();
        let weekdays = Self::parse_byday(byday);

        if weekdays.is_empty() {
            return occurrences;
        }

        let start_date = base_event.dtstart.date();

        // Find the Monday of the week containing the start_date (reference week)
        let reference_week_start =
            start_date - Duration::days(start_date.weekday().num_days_from_monday() as i64);

        let mut total_events_generated = 0; // Count of actual events generated (matches RFC 5545 COUNT)
        let mut week_interval_number = 0; // Which interval week we're processing (0, 1, 2, ...)
        let max_weeks = (count * 10).min(520); // Prevent infinite loops - max 10 years

        // Continue generating until we reach count or date limits
        while total_events_generated < count && week_interval_number < max_weeks {
            // Calculate the actual week we're processing: original + (interval_number * interval) weeks
            let current_week_start = reference_week_start
                + Duration::weeks((week_interval_number as i32 * interval) as i64);

            // Stop if we've gone past the end range
            if current_week_start > end_range {
                break;
            }

            // Generate occurrences for all matching weekdays in this week
            for &weekday in &weekdays {
                let occurrence_date =
                    current_week_start + Duration::days(weekday.num_days_from_monday() as i64);

                // Skip if occurrence is before start_range or after end_range
                if occurrence_date < start_range || occurrence_date > end_range {
                    continue;
                }

                // Skip if this occurrence is before the original event date
                if occurrence_date < start_date {
                    continue;
                }

                // Check UNTIL constraint
                if let Some(until) = until_date {
                    let days_diff = occurrence_date.signed_duration_since(start_date).num_days();
                    let occurrence_datetime = base_event.dtstart + Duration::days(days_diff);
                    if occurrence_datetime > until {
                        return occurrences;
                    }
                }

                // Calculate the occurrence datetime
                let days_diff = occurrence_date.signed_duration_since(start_date).num_days();
                let occurrence_datetime = base_event.dtstart + Duration::days(days_diff);

                // Check if this occurrence is in the exception dates (EXDATE)
                let is_exception = base_event.exdate.iter().any(|exception_date| {
                    // Compare as local time (naive datetimes) instead of UTC
                    let exception_naive = *exception_date;
                    let occurrence_naive = occurrence_datetime;
                    let diff = occurrence_naive - exception_naive;
                    diff.num_seconds().abs() < 60
                });

                if !is_exception {
                    // Create occurrence event
                    let mut occurrence_event = base_event.clone();
                    occurrence_event.dtstart = occurrence_datetime;
                    occurrence_event.dtstamp = chrono::Utc::now();

                    if let Some(end) = base_event.dtend {
                        occurrence_event.dtend = Some(end + Duration::days(days_diff));
                    }

                    total_events_generated += 1;
                    occurrences.push(occurrence_event);

                    // Stop if we've reached the count limit
                    if total_events_generated >= count {
                        return occurrences;
                    }
                }
            }

            // Move to the next interval week
            week_interval_number += 1;
        }

        occurrences
    }

    /// Generate occurrences for MONTHLY frequency with BYMONTHDAY
    fn generate_monthly_bymonthday_occurrences(
        base_event: &VEvent,
        bymonthday: &str,
        interval: i32,
        start_range: NaiveDate,
        end_range: NaiveDate,
        until_date: Option<chrono::NaiveDateTime>,
        count: usize,
    ) -> Vec<VEvent> {
        let mut occurrences = Vec::new();

        // Parse BYMONTHDAY (e.g., "15" or "1,15,31")
        let monthdays: Vec<u32> = bymonthday
            .split(',')
            .filter_map(|day| day.trim().parse().ok())
            .filter(|&day| day >= 1 && day <= 31)
            .collect();

        if monthdays.is_empty() {
            return occurrences;
        }

        let start_date = base_event.dtstart.date();
        let mut current_month_start =
            NaiveDate::from_ymd_opt(start_date.year(), start_date.month(), 1).unwrap();
        let mut total_occurrences = 0;
        let mut months_processed = 0;
        let max_months = (count * 12).min(120); // Prevent infinite loops - max 10 years

        // Generate occurrences month by month
        while current_month_start <= end_range
            && total_occurrences < count
            && months_processed < max_months
        {
            // Generate occurrences for all matching days in this month
            for &day in &monthdays {
                // Try to create the date, skip if invalid (e.g., Feb 31)
                if let Some(occurrence_date) = NaiveDate::from_ymd_opt(
                    current_month_start.year(),
                    current_month_start.month(),
                    day,
                ) {
                    // Skip if occurrence is before start_range or after end_range
                    if occurrence_date < start_range || occurrence_date > end_range {
                        continue;
                    }

                    // Skip if we've reached the count limit
                    if total_occurrences >= count {
                        break;
                    }

                    // Skip if this occurrence is before the original event date
                    if occurrence_date < start_date {
                        continue;
                    }

                    // Check UNTIL constraint
                    if let Some(until) = until_date {
                        let days_diff =
                            occurrence_date.signed_duration_since(start_date).num_days();
                        let occurrence_datetime = base_event.dtstart + Duration::days(days_diff);
                        if occurrence_datetime > until {
                            return occurrences;
                        }
                    }

                    // Calculate the occurrence datetime
                    let days_diff = occurrence_date.signed_duration_since(start_date).num_days();
                    let occurrence_datetime = base_event.dtstart + Duration::days(days_diff);

                    // Check if this occurrence is in the exception dates (EXDATE)
                    let is_exception = base_event.exdate.iter().any(|exception_date| {
                        let diff = occurrence_datetime - *exception_date;
                        diff.num_seconds().abs() < 60
                    });

                    if !is_exception {
                        // Create occurrence event
                        let mut occurrence_event = base_event.clone();
                        occurrence_event.dtstart = occurrence_datetime;
                        occurrence_event.dtstamp = chrono::Utc::now();

                        if let Some(end) = base_event.dtend {
                            occurrence_event.dtend = Some(end + Duration::days(days_diff));
                        }

                        occurrences.push(occurrence_event);
                        total_occurrences += 1;
                    }
                }
            }

            // Move to next month interval
            months_processed += 1;
            if let Some(next_month_start) = Self::add_months(current_month_start, interval) {
                current_month_start =
                    NaiveDate::from_ymd_opt(next_month_start.year(), next_month_start.month(), 1)
                        .unwrap();
            } else {
                break;
            }
        }

        occurrences
    }

    /// Generate occurrences for MONTHLY frequency with BYDAY (e.g., "1MO" = first Monday)
    fn generate_monthly_byday_occurrences(
        base_event: &VEvent,
        byday: &str,
        interval: i32,
        start_range: NaiveDate,
        end_range: NaiveDate,
        until_date: Option<chrono::NaiveDateTime>,
        count: usize,
    ) -> Vec<VEvent> {
        let mut occurrences = Vec::new();

        // Parse BYDAY for monthly (e.g., "1MO" = first Monday, "-1FR" = last Friday)
        if let Some((position, weekday)) = Self::parse_monthly_byday(byday) {
            let start_date = base_event.dtstart.date();
            let mut current_month_start =
                NaiveDate::from_ymd_opt(start_date.year(), start_date.month(), 1).unwrap();
            let mut total_occurrences = 0;

            // Generate occurrences month by month
            while current_month_start <= end_range && total_occurrences < count {
                if let Some(occurrence_date) =
                    Self::find_nth_weekday_in_month(current_month_start, position, weekday)
                {
                    // Skip if occurrence is before start_range or after end_range
                    if occurrence_date < start_range || occurrence_date > end_range {
                    } else if occurrence_date >= start_date && total_occurrences < count {
                        // Check UNTIL constraint
                        if let Some(until) = until_date {
                            let days_diff =
                                occurrence_date.signed_duration_since(start_date).num_days();
                            let occurrence_datetime =
                                base_event.dtstart + Duration::days(days_diff);
                            if occurrence_datetime > until {
                                return occurrences;
                            }
                        }

                        // Calculate the occurrence datetime
                        let days_diff =
                            occurrence_date.signed_duration_since(start_date).num_days();
                        let occurrence_datetime = base_event.dtstart + Duration::days(days_diff);

                        // Check EXDATE
                        let is_exception = base_event.exdate.iter().any(|exception_date| {
                            let diff = occurrence_datetime - *exception_date;
                            diff.num_seconds().abs() < 60
                        });

                        if !is_exception {
                            let mut occurrence_event = base_event.clone();
                            occurrence_event.dtstart = occurrence_datetime;
                            occurrence_event.dtstamp = chrono::Utc::now();

                            if let Some(end) = base_event.dtend {
                                occurrence_event.dtend = Some(end + Duration::days(days_diff));
                            }

                            occurrences.push(occurrence_event);
                            total_occurrences += 1;
                        }
                    }
                }

                // Move to next month interval
                if let Some(next_month) = Self::add_months(current_month_start, interval) {
                    current_month_start =
                        NaiveDate::from_ymd_opt(next_month.year(), next_month.month(), 1).unwrap();
                } else {
                    break;
                }
            }
        }

        occurrences
    }

    /// Generate occurrences for YEARLY frequency with BYMONTH
    fn generate_yearly_bymonth_occurrences(
        base_event: &VEvent,
        bymonth: &str,
        interval: i32,
        start_range: NaiveDate,
        end_range: NaiveDate,
        until_date: Option<chrono::NaiveDateTime>,
        count: usize,
    ) -> Vec<VEvent> {
        let mut occurrences = Vec::new();

        // Parse BYMONTH (e.g., "1,3,6" -> [January, March, June])
        let months: Vec<u32> = bymonth
            .split(',')
            .filter_map(|month| month.trim().parse().ok())
            .filter(|&month| month >= 1 && month <= 12)
            .collect();

        if months.is_empty() {
            return occurrences;
        }

        let start_date = base_event.dtstart.date();
        let mut current_year = start_date.year();
        let mut total_occurrences = 0;

        // Generate occurrences year by year
        while total_occurrences < count {
            // Generate occurrences for all matching months in this year
            for &month in &months {
                // Create the date for this year/month/day
                if let Some(occurrence_date) =
                    NaiveDate::from_ymd_opt(current_year, month, start_date.day())
                {
                    // Skip if occurrence is before start_range or after end_range
                    if occurrence_date < start_range || occurrence_date > end_range {
                        continue;
                    }

                    // Skip if we've reached the count limit
                    if total_occurrences >= count {
                        break;
                    }

                    // Skip if this occurrence is before the original event date
                    if occurrence_date < start_date {
                        continue;
                    }

                    // Check UNTIL constraint
                    if let Some(until) = until_date {
                        let days_diff =
                            occurrence_date.signed_duration_since(start_date).num_days();
                        let occurrence_datetime = base_event.dtstart + Duration::days(days_diff);
                        if occurrence_datetime > until {
                            return occurrences;
                        }
                    }

                    // Calculate the occurrence datetime
                    let days_diff = occurrence_date.signed_duration_since(start_date).num_days();
                    let occurrence_datetime = base_event.dtstart + Duration::days(days_diff);

                    // Check EXDATE
                    let is_exception = base_event.exdate.iter().any(|exception_date| {
                        let diff = occurrence_datetime - *exception_date;
                        diff.num_seconds().abs() < 60
                    });

                    if !is_exception {
                        let mut occurrence_event = base_event.clone();
                        occurrence_event.dtstart = occurrence_datetime;
                        occurrence_event.dtstamp = chrono::Utc::now();

                        if let Some(end) = base_event.dtend {
                            occurrence_event.dtend = Some(end + Duration::days(days_diff));
                        }

                        occurrences.push(occurrence_event);
                        total_occurrences += 1;
                    }
                }
            }

            // Move to next year interval
            current_year += interval;

            // Stop if we've gone beyond reasonable range
            if current_year > end_range.year() + 10 {
                break;
            }
        }

        occurrences
    }

    /// Parse BYDAY parameter (e.g., "MO,WE,FR" -> [Monday, Wednesday, Friday])
    fn parse_byday(byday: &str) -> Vec<Weekday> {
        byday
            .split(',')
            .filter_map(|day| match day {
                "MO" => Some(Weekday::Mon),
                "TU" => Some(Weekday::Tue),
                "WE" => Some(Weekday::Wed),
                "TH" => Some(Weekday::Thu),
                "FR" => Some(Weekday::Fri),
                "SA" => Some(Weekday::Sat),
                "SU" => Some(Weekday::Sun),
                _ => None,
            })
            .collect()
    }

    /// Add months to a date (handling month boundary issues)
    fn add_months(date: NaiveDate, months: i32) -> Option<NaiveDate> {
        let mut year = date.year();
        let mut month = date.month() as i32 + months;

        while month > 12 {
            year += 1;
            month -= 12;
        }
        while month < 1 {
            year -= 1;
            month += 12;
        }

        // Handle day overflow (e.g., Jan 31 -> Feb 28)
        let day = date.day().min(Self::days_in_month(year, month as u32));

        NaiveDate::from_ymd_opt(year, month as u32, day)
    }

    /// Add years to a date
    fn add_years(date: NaiveDate, years: i32) -> Option<NaiveDate> {
        NaiveDate::from_ymd_opt(date.year() + years, date.month(), date.day())
    }

    /// Get number of days in a month
    fn days_in_month(year: i32, month: u32) -> u32 {
        match month {
            1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
            4 | 6 | 9 | 11 => 30,
            2 => {
                if Self::is_leap_year(year) {
                    29
                } else {
                    28
                }
            }
            _ => 30,
        }
    }

    /// Check if year is leap year
    fn is_leap_year(year: i32) -> bool {
        (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
    }

    /// Create a new calendar on the CalDAV server
    pub async fn create_calendar(
        &self,
        token: &str,
        password: &str,
        name: String,
        description: Option<String>,
        color: Option<String>,
    ) -> Result<(), String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("POST");
        opts.set_mode(RequestMode::Cors);

        let body = serde_json::json!({
            "name": name,
            "description": description,
            "color": color
        });

        let body_string = serde_json::to_string(&body)
            .map_err(|e| format!("JSON serialization failed: {}", e))?;

        opts.set_body(&body_string.into());

        let url = format!("{}/calendar/create", self.base_url);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("X-CalDAV-Password", password)
            .map_err(|e| format!("Password header setting failed: {:?}", e))?;

        request
            .headers()
            .set("Content-Type", "application/json")
            .map_err(|e| format!("Content-Type header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            Ok(())
        } else {
            Err(format!(
                "Request failed with status {}: {}",
                resp.status(),
                text_string
            ))
        }
    }

    /// Delete an event from the CalDAV server
    pub async fn delete_event(
        &self,
        token: &str,
        password: &str,
        calendar_path: String,
        event_href: String,
        delete_action: String,
        occurrence_date: Option<String>,
    ) -> Result<String, String> {
        // Forward to delete_event_with_uid with extracted UID
        let event_uid = event_href.trim_end_matches(".ics").to_string();
        self.delete_event_with_uid(
            token,
            password,
            calendar_path,
            event_href,
            delete_action,
            occurrence_date,
            event_uid,
            None, // No recurrence info available
        )
        .await
    }

    /// Delete an event from the CalDAV server with UID and recurrence support
    pub async fn delete_event_with_uid(
        &self,
        token: &str,
        password: &str,
        calendar_path: String,
        event_href: String,
        delete_action: String,
        occurrence_date: Option<String>,
        event_uid: String,
        recurrence: Option<String>,
    ) -> Result<String, String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("POST");
        opts.set_mode(RequestMode::Cors);

        // Determine if this is a series event based on recurrence
        let is_series = recurrence
            .as_ref()
            .map(|r| !r.is_empty() && r.to_uppercase() != "NONE")
            .unwrap_or(false);

        let (body, url) = if is_series {
            // Use series-specific endpoint and payload for recurring events
            // Map delete_action to delete_scope for series endpoint
            let delete_scope = match delete_action.as_str() {
                "delete_this" => "this_only",
                "delete_following" => "this_and_future",
                "delete_series" => "all_in_series",
                _ => "this_only", // Default to single occurrence
            };

            let body = serde_json::json!({
                "series_uid": event_uid,
                "calendar_path": calendar_path,
                "event_href": event_href,
                "delete_scope": delete_scope,
                "occurrence_date": occurrence_date
            });
            let url = format!("{}/calendar/events/series/delete", self.base_url);
            (body, url)
        } else {
            // Use regular endpoint for non-recurring events
            let body = serde_json::json!({
                "calendar_path": calendar_path,
                "event_href": event_href,
                "delete_action": delete_action,
                "occurrence_date": occurrence_date
            });
            let url = format!("{}/calendar/events/delete", self.base_url);
            (body, url)
        };

        let body_string = serde_json::to_string(&body)
            .map_err(|e| format!("JSON serialization failed: {}", e))?;
        opts.set_body(&body_string.into());
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("X-CalDAV-Password", password)
            .map_err(|e| format!("Password header setting failed: {:?}", e))?;

        request
            .headers()
            .set("Content-Type", "application/json")
            .map_err(|e| format!("Content-Type header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            // Parse the response to get the message
            let response: serde_json::Value = serde_json::from_str(&text_string)
                .map_err(|e| format!("Failed to parse response JSON: {}", e))?;
            let message = response["message"]
                .as_str()
                .unwrap_or("Event deleted successfully")
                .to_string();
            Ok(message)
        } else {
            Err(format!(
                "Request failed with status {}: {}",
                resp.status(),
                text_string
            ))
        }
    }

    /// Create a new event on the CalDAV server
    pub async fn create_event(
        &self,
        token: &str,
        password: &str,
        title: String,
        description: String,
        start_date: String,
        start_time: String,
        end_date: String,
        end_time: String,
        location: String,
        all_day: bool,
        status: String,
        class: String,
        priority: Option<u8>,
        organizer: String,
        attendees: String,
        categories: String,
        alarms: Vec<VAlarm>,
        recurrence: String,
        recurrence_days: Vec<bool>,
        recurrence_interval: u32,
        recurrence_count: Option<u32>,
        recurrence_until: Option<String>,
        calendar_path: Option<String>,
        timezone: String,
    ) -> Result<(), String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("POST");
        opts.set_mode(RequestMode::Cors);

        // Determine if this is a series event based on recurrence
        let is_series = !recurrence.is_empty() && recurrence.to_uppercase() != "NONE";

        let (body, url) = if is_series {
            // Use series-specific endpoint and payload for recurring events
            let body = serde_json::json!({
                "title": title,
                "description": description,
                "start_date": start_date,
                "start_time": start_time,
                "end_date": end_date,
                "end_time": end_time,
                "location": location,
                "all_day": all_day,
                "status": status,
                "class": class,
                "priority": priority,
                "organizer": organizer,
                "attendees": attendees,
                "categories": categories,
                "alarms": alarms,
                "recurrence": recurrence,
                "recurrence_days": recurrence_days,
                "recurrence_interval": recurrence_interval,
                "recurrence_end_date": recurrence_until,
                "recurrence_count": recurrence_count,
                "calendar_path": calendar_path,
                "timezone": timezone
            });
            let url = format!("{}/calendar/events/series/create", self.base_url);
            (body, url)
        } else {
            // Use regular endpoint for non-recurring events
            let body = serde_json::json!({
                "title": title,
                "description": description,
                "start_date": start_date,
                "start_time": start_time,
                "end_date": end_date,
                "end_time": end_time,
                "location": location,
                "all_day": all_day,
                "status": status,
                "class": class,
                "priority": priority,
                "organizer": organizer,
                "attendees": attendees,
                "categories": categories,
                "alarms": alarms,
                "recurrence": recurrence,
                "recurrence_days": recurrence_days,
                "calendar_path": calendar_path,
                "timezone": timezone
            });
            let url = format!("{}/calendar/events/create", self.base_url);
            (body, url)
        };

        let body_string = serde_json::to_string(&body)
            .map_err(|e| format!("JSON serialization failed: {}", e))?;
        opts.set_body(&body_string.into());
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("X-CalDAV-Password", password)
            .map_err(|e| format!("Password header setting failed: {:?}", e))?;

        request
            .headers()
            .set("Content-Type", "application/json")
            .map_err(|e| format!("Content-Type header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            Ok(())
        } else {
            Err(format!(
                "Request failed with status {}: {}",
                resp.status(),
                text_string
            ))
        }
    }

    pub async fn update_event(
        &self,
        token: &str,
        password: &str,
        event_uid: String,
        title: String,
        description: String,
        start_date: String,
        start_time: String,
        end_date: String,
        end_time: String,
        location: String,
        all_day: bool,
        status: String,
        class: String,
        priority: Option<u8>,
        organizer: String,
        attendees: String,
        categories: String,
        alarms: Vec<VAlarm>,
        recurrence: String,
        recurrence_days: Vec<bool>,
        recurrence_interval: u32,
        recurrence_count: Option<u32>,
        recurrence_until: Option<String>,
        calendar_path: Option<String>,
        timezone: String,
    ) -> Result<(), String> {
        // Forward to update_event_with_scope with default scope
        self.update_event_with_scope(
            token,
            password,
            event_uid,
            title,
            description,
            start_date,
            start_time,
            end_date,
            end_time,
            location,
            all_day,
            status,
            class,
            priority,
            organizer,
            attendees,
            categories,
            alarms,
            recurrence,
            recurrence_days,
            recurrence_interval,
            recurrence_count,
            recurrence_until,
            calendar_path,
            timezone,
        )
        .await
    }

    pub async fn update_event_with_scope(
        &self,
        token: &str,
        password: &str,
        event_uid: String,
        title: String,
        description: String,
        start_date: String,
        start_time: String,
        end_date: String,
        end_time: String,
        location: String,
        all_day: bool,
        status: String,
        class: String,
        priority: Option<u8>,
        organizer: String,
        attendees: String,
        categories: String,
        alarms: Vec<VAlarm>,
        recurrence: String,
        recurrence_days: Vec<bool>,
        recurrence_interval: u32,
        recurrence_count: Option<u32>,
        recurrence_until: Option<String>,
        calendar_path: Option<String>,
        timezone: String,
    ) -> Result<(), String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("POST");
        opts.set_mode(RequestMode::Cors);

        // Always use regular endpoint - recurring events should use update_series() instead
        let body = serde_json::json!({
            "uid": event_uid,
            "title": title,
            "description": description,
            "start_date": start_date,
            "start_time": start_time,
            "end_date": end_date,
            "end_time": end_time,
            "location": location,
            "all_day": all_day,
            "status": status,
            "class": class,
            "priority": priority,
            "organizer": organizer,
            "attendees": attendees,
            "categories": categories,
            "alarms": alarms,
            "recurrence": recurrence,
            "recurrence_days": recurrence_days,
            "recurrence_interval": recurrence_interval,
            "recurrence_count": recurrence_count,
            "recurrence_end_date": recurrence_until,
            "calendar_path": calendar_path,
            "timezone": timezone
        });
        let url = format!("{}/calendar/events/update", self.base_url);

        let body_string = serde_json::to_string(&body)
            .map_err(|e| format!("JSON serialization failed: {}", e))?;
        opts.set_body(&body_string.into());
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("X-CalDAV-Password", password)
            .map_err(|e| format!("Password header setting failed: {:?}", e))?;

        request
            .headers()
            .set("Content-Type", "application/json")
            .map_err(|e| format!("Content-Type header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            Ok(())
        } else {
            Err(format!(
                "Request failed with status {}: {}",
                resp.status(),
                text_string
            ))
        }
    }

    /// Delete a calendar from the CalDAV server
    pub async fn delete_calendar(
        &self,
        token: &str,
        password: &str,
        path: String,
    ) -> Result<(), String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("POST");
        opts.set_mode(RequestMode::Cors);

        let body = serde_json::json!({
            "path": path
        });

        let body_string = serde_json::to_string(&body)
            .map_err(|e| format!("JSON serialization failed: {}", e))?;

        opts.set_body(&body_string.into());

        let url = format!("{}/calendar/delete", self.base_url);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("X-CalDAV-Password", password)
            .map_err(|e| format!("Password header setting failed: {:?}", e))?;

        request
            .headers()
            .set("Content-Type", "application/json")
            .map_err(|e| format!("Content-Type header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            Ok(())
        } else {
            Err(format!(
                "Request failed with status {}: {}",
                resp.status(),
                text_string
            ))
        }
    }

    /// Refresh a single event by UID from the CalDAV server
    pub async fn refresh_event(
        &self,
        token: &str,
        password: &str,
        uid: &str,
    ) -> Result<Option<CalendarEvent>, String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("GET");
        opts.set_mode(RequestMode::Cors);

        let url = format!("{}/calendar/events/{}", self.base_url, uid);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("X-CalDAV-Password", password)
            .map_err(|e| format!("Password header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            let event: Option<CalendarEvent> = serde_json::from_str(&text_string)
                .map_err(|e| format!("JSON parsing failed: {}", e))?;
            Ok(event)
        } else {
            Err(format!(
                "Request failed with status {}: {}",
                resp.status(),
                text_string
            ))
        }
    }

    pub async fn update_series(
        &self,
        token: &str,
        password: &str,
        series_uid: String,
        title: String,
        description: String,
        start_date: String,
        start_time: String,
        end_date: String,
        end_time: String,
        location: String,
        all_day: bool,
        status: String,
        class: String,
        priority: Option<u8>,
        organizer: String,
        attendees: String,
        categories: String,
        alarms: Vec<VAlarm>,
        recurrence: String,
        recurrence_days: Vec<bool>,
        recurrence_interval: u32,
        recurrence_count: Option<u32>,
        recurrence_until: Option<String>,
        calendar_path: Option<String>,
        update_scope: String,
        occurrence_date: Option<String>,
        timezone: String,
    ) -> Result<(), String> {
        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("POST");
        opts.set_mode(RequestMode::Cors);

        let body = serde_json::json!({
            "series_uid": series_uid,
            "title": title,
            "description": description,
            "start_date": start_date,
            "start_time": start_time,
            "end_date": end_date,
            "end_time": end_time,
            "location": location,
            "all_day": all_day,
            "status": status,
            "class": class,
            "priority": priority,
            "organizer": organizer,
            "attendees": attendees,
            "categories": categories,
            "alarms": alarms,
            "recurrence": recurrence,
            "recurrence_days": recurrence_days,
            "recurrence_interval": recurrence_interval,
            "recurrence_end_date": recurrence_until,
            "recurrence_count": recurrence_count,
            "calendar_path": calendar_path,
            "update_scope": update_scope,
            "occurrence_date": occurrence_date,
            "timezone": timezone
        });

        let url = format!("{}/calendar/events/series/update", self.base_url);

        web_sys::console::log_1(
            &format!("🔄 update_series: Making request to URL: {}", url).into(),
        );
        web_sys::console::log_1(
            &format!(
                "🔄 update_series: Request body: {}",
                serde_json::to_string_pretty(&body).unwrap_or_default()
            )
            .into(),
        );

        let body_string = serde_json::to_string(&body)
            .map_err(|e| format!("JSON serialization failed: {}", e))?;
        opts.set_body(&body_string.into());
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("X-CalDAV-Password", password)
            .map_err(|e| format!("Password header setting failed: {:?}", e))?;

        request
            .headers()
            .set("Content-Type", "application/json")
            .map_err(|e| format!("Content-Type header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Network request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response cast failed: {:?}", e))?;

        let text = JsFuture::from(
            resp.text()
                .map_err(|e| format!("Text extraction failed: {:?}", e))?,
        )
        .await
        .map_err(|e| format!("Text promise failed: {:?}", e))?;

        let text_string = text.as_string().ok_or("Response text is not a string")?;

        if resp.ok() {
            Ok(())
        } else {
            Err(format!(
                "Request failed with status {}: {}",
                resp.status(),
                text_string
            ))
        }
    }

    /// Parse monthly BYDAY (e.g., "1MO" -> (1, Monday), "-1FR" -> (-1, Friday))
    fn parse_monthly_byday(byday: &str) -> Option<(i8, chrono::Weekday)> {
        if byday.len() < 3 {
            return None;
        }

        // Extract position and weekday
        let (position_str, weekday_str) = if byday.starts_with('-') {
            (&byday[..2], &byday[2..])
        } else {
            (&byday[..1], &byday[1..])
        };

        let position = position_str.parse::<i8>().ok()?;
        let weekday = match weekday_str {
            "MO" => chrono::Weekday::Mon,
            "TU" => chrono::Weekday::Tue,
            "WE" => chrono::Weekday::Wed,
            "TH" => chrono::Weekday::Thu,
            "FR" => chrono::Weekday::Fri,
            "SA" => chrono::Weekday::Sat,
            "SU" => chrono::Weekday::Sun,
            _ => return None,
        };

        Some((position, weekday))
    }

    /// Find nth weekday in a month (e.g., 1st Monday, 2nd Tuesday, -1 = last)
    fn find_nth_weekday_in_month(
        month_start: NaiveDate,
        position: i8,
        weekday: chrono::Weekday,
    ) -> Option<NaiveDate> {
        let year = month_start.year();
        let month = month_start.month();

        if position > 0 {
            // Find nth occurrence from beginning of month
            let mut current = NaiveDate::from_ymd_opt(year, month, 1)?;
            let mut count = 0;

            while current.month() == month {
                if current.weekday() == weekday {
                    count += 1;
                    if count == position as u8 {
                        return Some(current);
                    }
                }
                current = current.succ_opt()?;
            }
        } else if position < 0 {
            // Find nth occurrence from end of month
            let next_month = if month == 12 {
                NaiveDate::from_ymd_opt(year + 1, 1, 1)?
            } else {
                NaiveDate::from_ymd_opt(year, month + 1, 1)?
            };
            let mut current = next_month.pred_opt()?; // Last day of current month
            let mut count = 0;

            while current.month() == month {
                if current.weekday() == weekday {
                    count += 1;
                    if count == (-position) as u8 {
                        return Some(current);
                    }
                }
                current = current.pred_opt()?;
            }
        }

        None
    }

    // ==================== EXTERNAL CALENDAR METHODS ====================

    pub async fn get_external_calendars() -> Result<Vec<ExternalCalendar>, String> {
        let token = LocalStorage::get::<String>("auth_token")
            .map_err(|_| "No authentication token found".to_string())?;

        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("GET");
        opts.set_mode(RequestMode::Cors);

        let service = Self::new();
        let url = format!("{}/external-calendars", service.base_url);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response casting failed: {:?}", e))?;

        if !resp.ok() {
            return Err(format!("HTTP error: {}", resp.status()));
        }

        let json = JsFuture::from(resp.json().unwrap())
            .await
            .map_err(|e| format!("JSON parsing failed: {:?}", e))?;

        let external_calendars: Vec<ExternalCalendar> = serde_wasm_bindgen::from_value(json)
            .map_err(|e| format!("Deserialization failed: {:?}", e))?;

        Ok(external_calendars)
    }

    pub async fn create_external_calendar(name: &str, url: &str, color: &str) -> Result<ExternalCalendar, String> {
        let token = LocalStorage::get::<String>("auth_token")
            .map_err(|_| "No authentication token found".to_string())?;

        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("POST");
        opts.set_mode(RequestMode::Cors);

        let body = serde_json::json!({
            "name": name,
            "url": url,
            "color": color
        });

        let service = Self::new();
        let body_string = serde_json::to_string(&body)
            .map_err(|e| format!("JSON serialization failed: {}", e))?;
        opts.set_body(&body_string.into());

        let url = format!("{}/external-calendars", service.base_url);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("Content-Type", "application/json")
            .map_err(|e| format!("Content-Type header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response casting failed: {:?}", e))?;

        if !resp.ok() {
            return Err(format!("HTTP error: {}", resp.status()));
        }

        let json = JsFuture::from(resp.json().unwrap())
            .await
            .map_err(|e| format!("JSON parsing failed: {:?}", e))?;

        let external_calendar: ExternalCalendar = serde_wasm_bindgen::from_value(json)
            .map_err(|e| format!("Deserialization failed: {:?}", e))?;

        Ok(external_calendar)
    }

    pub async fn update_external_calendar(
        id: i32,
        name: &str,
        url: &str,
        color: &str,
        is_visible: bool,
    ) -> Result<(), String> {
        let token = LocalStorage::get::<String>("auth_token")
            .map_err(|_| "No authentication token found".to_string())?;

        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("POST");
        opts.set_mode(RequestMode::Cors);

        let body = serde_json::json!({
            "name": name,
            "url": url,
            "color": color,
            "is_visible": is_visible
        });

        let service = Self::new();
        let body_string = serde_json::to_string(&body)
            .map_err(|e| format!("JSON serialization failed: {}", e))?;
        opts.set_body(&body_string.into());

        let url = format!("{}/external-calendars/{}", service.base_url, id);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        request
            .headers()
            .set("Content-Type", "application/json")
            .map_err(|e| format!("Content-Type header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response casting failed: {:?}", e))?;

        if !resp.ok() {
            return Err(format!("HTTP error: {}", resp.status()));
        }

        Ok(())
    }

    pub async fn delete_external_calendar(id: i32) -> Result<(), String> {
        let token = LocalStorage::get::<String>("auth_token")
            .map_err(|_| "No authentication token found".to_string())?;

        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("DELETE");
        opts.set_mode(RequestMode::Cors);

        let service = Self::new();
        let url = format!("{}/external-calendars/{}", service.base_url, id);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response casting failed: {:?}", e))?;

        if !resp.ok() {
            return Err(format!("HTTP error: {}", resp.status()));
        }

        Ok(())
    }

    pub async fn fetch_external_calendar_events(id: i32) -> Result<Vec<VEvent>, String> {
        let token = LocalStorage::get::<String>("auth_token")
            .map_err(|_| "No authentication token found".to_string())?;

        let window = web_sys::window().ok_or("No global window exists")?;

        let opts = RequestInit::new();
        opts.set_method("GET");
        opts.set_mode(RequestMode::Cors);

        let service = Self::new();
        let url = format!("{}/external-calendars/{}/events", service.base_url, id);
        let request = Request::new_with_str_and_init(&url, &opts)
            .map_err(|e| format!("Request creation failed: {:?}", e))?;

        request
            .headers()
            .set("Authorization", &format!("Bearer {}", token))
            .map_err(|e| format!("Authorization header setting failed: {:?}", e))?;

        let resp_value = JsFuture::from(window.fetch_with_request(&request))
            .await
            .map_err(|e| format!("Request failed: {:?}", e))?;

        let resp: Response = resp_value
            .dyn_into()
            .map_err(|e| format!("Response casting failed: {:?}", e))?;

        if !resp.ok() {
            return Err(format!("HTTP error: {}", resp.status()));
        }

        let json = JsFuture::from(resp.json().unwrap())
            .await
            .map_err(|e| format!("JSON parsing failed: {:?}", e))?;

        #[derive(Deserialize)]
        struct ExternalCalendarEventsResponse {
            events: Vec<VEvent>,
        }

        let response: ExternalCalendarEventsResponse = serde_wasm_bindgen::from_value(json)
            .map_err(|e| format!("Deserialization failed: {:?}", e))?;

        Ok(response.events)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ExternalCalendar {
    pub id: i32,
    pub name: String,
    pub url: String,
    pub color: String,
    pub is_visible: bool,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub updated_at: chrono::DateTime<chrono::Utc>,
    pub last_fetched: Option<chrono::DateTime<chrono::Utc>>,
}
