use crate::components::{
    CalendarHeader, CreateEventModal, EventCreationData, EventModal, MonthView, PrintPreviewModal, ViewMode, WeekView,
};
use crate::models::ical::VEvent;
use crate::services::{calendar_service::{UserInfo, ExternalCalendar}, CalendarService};
use chrono::{Datelike, Duration, Local, NaiveDate, Weekday};
use gloo_storage::{LocalStorage, Storage};
use std::collections::HashMap;
use web_sys::MouseEvent;
use yew::prelude::*;

#[derive(Properties, PartialEq)]
pub struct CalendarProps {
    #[prop_or_default]
    pub user_info: Option<UserInfo>,
    #[prop_or_default]
    pub external_calendar_events: Vec<VEvent>,
    #[prop_or_default]
    pub external_calendars: Vec<ExternalCalendar>,
    #[prop_or_default]
    pub on_event_context_menu: Option<Callback<(web_sys::MouseEvent, VEvent)>>,
    #[prop_or_default]
    pub on_calendar_context_menu: Option<Callback<(web_sys::MouseEvent, NaiveDate)>>,
    #[prop_or_default]
    pub view: ViewMode,
    #[prop_or_default]
    pub on_create_event_request: Option<Callback<EventCreationData>>,
    #[prop_or_default]
    pub on_event_update_request: Option<
        Callback<(
            VEvent,
            chrono::NaiveDateTime,
            chrono::NaiveDateTime,
            bool,
            Option<chrono::NaiveDateTime>,
            Option<String>,
            Option<String>,
        )>,
    >,
    #[prop_or_default]
    pub context_menus_open: bool,
}

#[function_component]
pub fn Calendar(props: &CalendarProps) -> Html {
    let today = Local::now().date_naive();
    
    // Event management state
    let events = use_state(|| HashMap::<NaiveDate, Vec<VEvent>>::new());
    let loading = use_state(|| true);
    let error = use_state(|| None::<String>);
    let refreshing_event_uid = use_state(|| None::<String>);
    // Track the currently selected date (the actual day the user has selected)
    let selected_date = use_state(|| {
        // Try to load saved selected date from localStorage
        if let Ok(saved_date_str) = LocalStorage::get::<String>("calendar_selected_date") {
            if let Ok(saved_date) = NaiveDate::parse_from_str(&saved_date_str, "%Y-%m-%d") {
                saved_date
            } else {
                today
            }
        } else {
            // Check for old key for backward compatibility
            if let Ok(saved_date_str) = LocalStorage::get::<String>("calendar_current_month") {
                if let Ok(saved_date) = NaiveDate::parse_from_str(&saved_date_str, "%Y-%m-%d") {
                    saved_date
                } else {
                    today
                }
            } else {
                today
            }
        }
    });

    // Track the display date (what to show in the view)
    let current_date = use_state(|| match props.view {
        ViewMode::Month => selected_date.with_day(1).unwrap_or(*selected_date),
        ViewMode::Week => *selected_date,
    });
    let selected_event = use_state(|| None::<VEvent>);

    // State for create event modal
    let show_create_modal = use_state(|| false);
    let create_event_data =
        use_state(|| None::<(chrono::NaiveDate, chrono::NaiveTime, chrono::NaiveTime)>);

    // State for time increment snapping (15 or 30 minutes)
    let time_increment = use_state(|| {
        // Try to load saved time increment from localStorage
        if let Ok(saved_increment) = LocalStorage::get::<u32>("calendar_time_increment") {
            if saved_increment == 15 || saved_increment == 30 {
                saved_increment
            } else {
                15
            }
        } else {
            15
        }
    });

    // Fetch events when current_date changes
    {
        let events = events.clone();
        let loading = loading.clone();
        let error = error.clone();
        let current_date = current_date.clone();
        let external_events = props.external_calendar_events.clone(); // Clone before the effect
        let view = props.view.clone(); // Clone before the effect
        
        use_effect_with((*current_date, view.clone(), external_events.len(), props.user_info.clone()), move |(date, _view, _external_len, user_info)| {
            let auth_token: Option<String> = LocalStorage::get("auth_token").ok();
            let date = *date; // Clone the date to avoid lifetime issues
            let view_mode = _view.clone(); // Clone the view mode to avoid lifetime issues
            let external_events = external_events.clone(); // Clone external events to avoid lifetime issues
            let user_info = user_info.clone(); // Clone user_info to avoid lifetime issues
            
            if let Some(token) = auth_token {
                let events = events.clone();
                let loading = loading.clone();
                let error = error.clone();
                
                wasm_bindgen_futures::spawn_local(async move {
                    let calendar_service = CalendarService::new();

                    let password = if let Ok(credentials_str) =
                        LocalStorage::get::<String>("caldav_credentials")
                    {
                        if let Ok(credentials) =
                            serde_json::from_str::<serde_json::Value>(&credentials_str)
                        {
                            credentials["password"].as_str().unwrap_or("").to_string()
                        } else {
                            String::new()
                        }
                    } else {
                        String::new()
                    };

                    // Determine which months to fetch based on view mode
                    let months_to_fetch = match view_mode {
                        ViewMode::Month => {
                            // For month view, just fetch the current month
                            vec![(date.year(), date.month())]
                        }
                        ViewMode::Week => {
                            // For week view, calculate the week bounds and fetch all months that intersect
                            let start_of_week = get_start_of_week(date);
                            let end_of_week = start_of_week + Duration::days(6);
                            
                            let mut months = vec![(start_of_week.year(), start_of_week.month())];
                            
                            // If the week spans into a different month, add that month too
                            if end_of_week.month() != start_of_week.month() || end_of_week.year() != start_of_week.year() {
                                months.push((end_of_week.year(), end_of_week.month()));
                            }
                            
                            months
                        }
                    };

                    // Fetch events for all required months
                    let mut all_events = Vec::new();
                    for (year, month) in months_to_fetch {
                        match calendar_service
                            .fetch_events_for_month_vevent(
                                &token,
                                &password,
                                year,
                                month,
                            )
                            .await
                        {
                            Ok(mut month_events) => {
                                all_events.append(&mut month_events);
                            }
                            Err(err) => {
                                error.set(Some(format!("Failed to load events for {}-{}: {}", year, month, err)));
                                loading.set(false);
                                return;
                            }
                        }
                    }
                    
                    // Deduplicate events that may appear in multiple month fetches
                    // This happens when a recurring event spans across month boundaries
                    all_events.sort_by(|a, b| {
                        // Sort by UID first, then by start time
                        match a.uid.cmp(&b.uid) {
                            std::cmp::Ordering::Equal => a.dtstart.cmp(&b.dtstart),
                            other => other,
                        }
                    });
                    all_events.dedup_by(|a, b| {
                        // Remove duplicates with same UID and start time
                        a.uid == b.uid && a.dtstart == b.dtstart
                    });

                    // Process the combined events
                    match Ok(all_events) as Result<Vec<VEvent>, String>
                    {
                        Ok(vevents) => {
                            // Filter CalDAV events based on calendar visibility
                            let mut filtered_events = if let Some(user_info) = user_info.as_ref() {
                                vevents.into_iter()
                                    .filter(|event| {
                                        if let Some(calendar_path) = event.calendar_path.as_ref() {
                                            // Find the calendar info for this event
                                            user_info.calendars.iter()
                                                .find(|cal| &cal.path == calendar_path)
                                                .map(|cal| cal.is_visible)
                                                .unwrap_or(true) // Default to visible if not found
                                        } else {
                                            true // Show events without calendar path
                                        }
                                    })
                                    .collect()
                            } else {
                                vevents // Show all events if no user info
                            };
                            
                            // Mark external events as external by adding a special category
                            let marked_external_events: Vec<VEvent> = external_events
                                .into_iter()
                                .map(|mut event| {
                                    // Add a special category to identify external events
                                    event.categories.push("__EXTERNAL_CALENDAR__".to_string());
                                    event
                                })
                                .collect();
                            
                            filtered_events.extend(marked_external_events);
                            
                            let grouped_events = CalendarService::group_events_by_date(filtered_events);
                            events.set(grouped_events);
                            loading.set(false);
                        }
                        Err(err) => {
                            error.set(Some(format!("Failed to load events: {}", err)));
                            loading.set(false);
                        }
                    }
                });
            } else {
                loading.set(false);
                error.set(Some("No authentication token found".to_string()));
            }

            || ()
        });
    }

    // Handle event click to refresh individual events
    let on_event_click = {
        let events = events.clone();
        let refreshing_event_uid = refreshing_event_uid.clone();
        
        Callback::from(move |event: VEvent| {
            let auth_token: Option<String> = LocalStorage::get("auth_token").ok();
            
            if let Some(token) = auth_token {
                let events = events.clone();
                let refreshing_event_uid = refreshing_event_uid.clone();
                let uid = event.uid.clone();

                refreshing_event_uid.set(Some(uid.clone()));

                wasm_bindgen_futures::spawn_local(async move {
                    let calendar_service = CalendarService::new();

                    let password = if let Ok(credentials_str) =
                        LocalStorage::get::<String>("caldav_credentials")
                    {
                        if let Ok(credentials) =
                            serde_json::from_str::<serde_json::Value>(&credentials_str)
                        {
                            credentials["password"].as_str().unwrap_or("").to_string()
                        } else {
                            String::new()
                        }
                    } else {
                        String::new()
                    };

                    match calendar_service
                        .refresh_event(&token, &password, &uid)
                        .await
                    {
                        Ok(Some(refreshed_event)) => {
                            let refreshed_vevent = refreshed_event;
                            let mut updated_events = (*events).clone();

                            for (_, day_events) in updated_events.iter_mut() {
                                day_events.retain(|e| e.uid != uid);
                            }

                            if refreshed_vevent.rrule.is_some() {
                                let new_occurrences =
                                    CalendarService::expand_recurring_events(vec![
                                        refreshed_vevent.clone(),
                                    ]);

                                for occurrence in new_occurrences {
                                    let date = occurrence.get_date();
                                    updated_events
                                        .entry(date)
                                        .or_insert_with(Vec::new)
                                        .push(occurrence);
                                }
                            } else {
                                let date = refreshed_vevent.get_date();
                                updated_events
                                    .entry(date)
                                    .or_insert_with(Vec::new)
                                    .push(refreshed_vevent);
                            }

                            events.set(updated_events);
                        }
                        Ok(None) => {
                            let mut updated_events = (*events).clone();
                            for (_, day_events) in updated_events.iter_mut() {
                                day_events.retain(|e| e.uid != uid);
                            }
                            events.set(updated_events);
                        }
                        Err(_err) => {}
                    }

                    refreshing_event_uid.set(None);
                });
            }
        })
    };

    // Handle view mode changes - adjust current_date format when switching between month/week
    {
        let current_date = current_date.clone();
        let selected_date = selected_date.clone();
        let view = props.view.clone();
        use_effect_with(view, move |view_mode| {
            let selected = *selected_date;
            let new_display_date = match view_mode {
                ViewMode::Month => selected.with_day(1).unwrap_or(selected),
                ViewMode::Week => selected, // Show the week containing the selected date
            };
            current_date.set(new_display_date);
            || {}
        });
    }

    let on_prev = {
        let current_date = current_date.clone();
        let selected_date = selected_date.clone();
        let view = props.view.clone();
        Callback::from(move |_: MouseEvent| {
            let (new_selected, new_display) = match view {
                ViewMode::Month => {
                    // Go to previous month, select the 1st day
                    let prev_month = *current_date - Duration::days(1);
                    let first_of_prev = prev_month.with_day(1).unwrap();
                    (first_of_prev, first_of_prev)
                }
                ViewMode::Week => {
                    // Go to previous week
                    let prev_week = *selected_date - Duration::weeks(1);
                    (prev_week, prev_week)
                }
            };
            selected_date.set(new_selected);
            current_date.set(new_display);
            let _ = LocalStorage::set(
                "calendar_selected_date",
                new_selected.format("%Y-%m-%d").to_string(),
            );
        })
    };

    let on_next = {
        let current_date = current_date.clone();
        let selected_date = selected_date.clone();
        let view = props.view.clone();
        Callback::from(move |_: MouseEvent| {
            let (new_selected, new_display) = match view {
                ViewMode::Month => {
                    // Go to next month, select the 1st day
                    let next_month = if current_date.month() == 12 {
                        NaiveDate::from_ymd_opt(current_date.year() + 1, 1, 1).unwrap()
                    } else {
                        NaiveDate::from_ymd_opt(current_date.year(), current_date.month() + 1, 1)
                            .unwrap()
                    };
                    (next_month, next_month)
                }
                ViewMode::Week => {
                    // Go to next week
                    let next_week = *selected_date + Duration::weeks(1);
                    (next_week, next_week)
                }
            };
            selected_date.set(new_selected);
            current_date.set(new_display);
            let _ = LocalStorage::set(
                "calendar_selected_date",
                new_selected.format("%Y-%m-%d").to_string(),
            );
        })
    };

    let on_today = {
        let current_date = current_date.clone();
        let selected_date = selected_date.clone();
        let view = props.view.clone();
        Callback::from(move |_| {
            let today = Local::now().date_naive();
            let (new_selected, new_display) = match view {
                ViewMode::Month => {
                    let first_of_today = today.with_day(1).unwrap();
                    (today, first_of_today) // Select today, but display the month
                }
                ViewMode::Week => (today, today), // Select and display today
            };
            selected_date.set(new_selected);
            current_date.set(new_display);
            let _ = LocalStorage::set(
                "calendar_selected_date",
                new_selected.format("%Y-%m-%d").to_string(),
            );
        })
    };

    // Handle time increment toggle
    let on_time_increment_toggle = {
        let time_increment = time_increment.clone();
        Callback::from(move |_: MouseEvent| {
            let current = *time_increment;
            let next = if current == 15 { 30 } else { 15 };
            time_increment.set(next);
            let _ = LocalStorage::set("calendar_time_increment", next);
        })
    };

    // Handle print calendar preview
    let show_print_preview = use_state(|| false);
    let on_print = {
        let show_print_preview = show_print_preview.clone();
        Callback::from(move |_: MouseEvent| {
            show_print_preview.set(true);
        })
    };

    // Handle drag-to-create event
    let on_create_event = {
        let show_create_modal = show_create_modal.clone();
        let create_event_data = create_event_data.clone();
        Callback::from(
            move |(_date, start_datetime, end_datetime): (
                NaiveDate,
                chrono::NaiveDateTime,
                chrono::NaiveDateTime,
            )| {
                // For drag-to-create, we don't need the temporary event approach
                // Instead, just pass the local times directly via initial_time props
                create_event_data.set(Some((
                    start_datetime.date(),
                    start_datetime.time(),
                    end_datetime.time(),
                )));
                show_create_modal.set(true);
            },
        )
    };

    // Handle drag-to-move event
    let on_event_update = {
        let on_event_update_request = props.on_event_update_request.clone();
        Callback::from(
            move |(
                event,
                new_start,
                new_end,
                preserve_rrule,
                until_date,
                update_scope,
                occurrence_date,
            ): (
                VEvent,
                chrono::NaiveDateTime,
                chrono::NaiveDateTime,
                bool,
                Option<chrono::NaiveDateTime>,
                Option<String>,
                Option<String>,
            )| {
                if let Some(callback) = &on_event_update_request {
                    callback.emit((
                        event,
                        new_start,
                        new_end,
                        preserve_rrule,
                        until_date,
                        update_scope,
                        occurrence_date,
                    ));
                }
            },
        )
    };

    html! {
        <div class={classes!("calendar", match props.view { ViewMode::Week => Some("week-view"), _ => None })}>
            <CalendarHeader
                current_date={*current_date}
                view_mode={props.view.clone()}
                on_prev={on_prev}
                on_next={on_next}
                on_today={on_today}
                time_increment={Some(*time_increment)}
                on_time_increment_toggle={Some(on_time_increment_toggle)}
                on_print={Some(on_print)}
            />

            {
                if *loading {
                    html! {
                        <div class="calendar-loading">
                            <p>{"Loading calendar events..."}</p>
                        </div>
                    }
                } else if let Some(err) = (*error).clone() {
                    html! {
                        <div class="calendar-error">
                            <p>{format!("Error: {}", err)}</p>
                        </div>
                    }
                } else {
                    match props.view {
                    ViewMode::Month => {
                        let on_day_select = {
                            let selected_date = selected_date.clone();
                            Callback::from(move |date: NaiveDate| {
                                selected_date.set(date);
                                let _ = LocalStorage::set("calendar_selected_date", date.format("%Y-%m-%d").to_string());
                            })
                        };

                        html! {
                            <MonthView
                                current_month={*current_date}
                                today={today}
                                events={(*events).clone()}
                                on_event_click={on_event_click.clone()}
                                refreshing_event_uid={(*refreshing_event_uid).clone()}
                                user_info={props.user_info.clone()}
                                external_calendars={props.external_calendars.clone()}
                                on_event_context_menu={props.on_event_context_menu.clone()}
                                on_calendar_context_menu={props.on_calendar_context_menu.clone()}
                                selected_date={Some(*selected_date)}
                                on_day_select={Some(on_day_select)}
                            />
                        }
                    },
                    ViewMode::Week => html! {
                        <WeekView
                            current_date={*current_date}
                            today={today}
                            events={(*events).clone()}
                            on_event_click={on_event_click.clone()}
                            refreshing_event_uid={(*refreshing_event_uid).clone()}
                            user_info={props.user_info.clone()}
                            external_calendars={props.external_calendars.clone()}
                            on_event_context_menu={props.on_event_context_menu.clone()}
                            on_calendar_context_menu={props.on_calendar_context_menu.clone()}
                            on_create_event={Some(on_create_event)}
                            on_create_event_request={props.on_create_event_request.clone()}
                            on_event_update={Some(on_event_update)}
                            context_menus_open={props.context_menus_open}
                            time_increment={*time_increment}
                        />
                    },
                    }
                }
            }

            // Event details modal
            <EventModal
                event={(*selected_event).clone()}
                on_close={{
                    let selected_event_clone = selected_event.clone();
                    Callback::from(move |_| {
                        selected_event_clone.set(None);
                    })
                }}
            />

            // Create event modal
            <CreateEventModal
                is_open={*show_create_modal}
                selected_date={create_event_data.as_ref().map(|(date, _, _)| *date)}
                event_to_edit={None}
                available_calendars={props.user_info.as_ref().map(|info| info.calendars.clone()).unwrap_or_default()}
                initial_start_time={create_event_data.as_ref().map(|(_, start_time, _)| *start_time)}
                initial_end_time={create_event_data.as_ref().map(|(_, _, end_time)| *end_time)}
                on_close={{
                    let show_create_modal = show_create_modal.clone();
                    let create_event_data = create_event_data.clone();
                    Callback::from(move |_| {
                        show_create_modal.set(false);
                        create_event_data.set(None);
                    })
                }}
                on_create={{
                    let show_create_modal = show_create_modal.clone();
                    let create_event_data = create_event_data.clone();
                    let on_create_event_request = props.on_create_event_request.clone();
                    Callback::from(move |event_data: EventCreationData| {
                        show_create_modal.set(false);
                        create_event_data.set(None);

                        // Emit the create event request to parent
                        if let Some(callback) = &on_create_event_request {
                            callback.emit(event_data);
                        }
                    })
                }}
            />

            // Print preview modal
            {
                if *show_print_preview {
                    html! {
                        <PrintPreviewModal
                            on_close={{
                                let show_print_preview = show_print_preview.clone();
                                Callback::from(move |_| {
                                    show_print_preview.set(false);
                                })
                            }}
                            view_mode={props.view.clone()}
                            current_date={*current_date}
                            selected_date={*selected_date}
                            events={(*events).clone()}
                            user_info={props.user_info.clone()}
                            external_calendars={props.external_calendars.clone()}
                            time_increment={*time_increment}
                            today={today}
                        />
                    }
                } else {
                    html! {}
                }
            }
        </div>
    }
}

// Helper function to calculate the start of the week (Sunday) for a given date
fn get_start_of_week(date: NaiveDate) -> NaiveDate {
    let weekday = date.weekday();
    let days_from_sunday = match weekday {
        Weekday::Sun => 0,
        Weekday::Mon => 1,
        Weekday::Tue => 2,
        Weekday::Wed => 3,
        Weekday::Thu => 4,
        Weekday::Fri => 5,
        Weekday::Sat => 6,
    };
    date - Duration::days(days_from_sunday)
}
