use crate::models::ical::VEvent;
use crate::services::calendar_service::{UserInfo, ExternalCalendar};
use chrono::{Datelike, NaiveDate, Weekday};
use std::collections::HashMap;
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::window;
use yew::prelude::*;

#[derive(Properties, PartialEq)]
pub struct MonthViewProps {
    pub current_month: NaiveDate,
    pub today: NaiveDate,
    pub events: HashMap<NaiveDate, Vec<VEvent>>,
    pub on_event_click: Callback<VEvent>,
    #[prop_or_default]
    pub refreshing_event_uid: Option<String>,
    #[prop_or_default]
    pub user_info: Option<UserInfo>,
    #[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 selected_date: Option<NaiveDate>,
    #[prop_or_default]
    pub on_day_select: Option<Callback<NaiveDate>>,
}

#[function_component(MonthView)]
pub fn month_view(props: &MonthViewProps) -> Html {
    let max_events_per_day = use_state(|| 4); // Default to 4 events max
    let first_day_of_month = props.current_month.with_day(1).unwrap();
    let days_in_month = get_days_in_month(props.current_month);
    let first_weekday = first_day_of_month.weekday();
    let days_from_prev_month = get_days_from_previous_month(props.current_month, first_weekday);

    // Calculate maximum events that can fit based on available height
    let calculate_max_events = {
        let max_events_per_day = max_events_per_day.clone();
        move || {
            // Since we're using CSS Grid with equal row heights,
            // we can estimate based on typical calendar dimensions
            // Typical calendar height is around 600-800px for 6 rows
            // Each row gets ~100-133px, minus day number and padding leaves ~70-100px
            // Each event is ~18px, so we can fit ~3-4 events + "+n more" indicator
            max_events_per_day.set(3);
        }
    };

    // Setup resize handler and initial calculation
    {
        let calculate_max_events = calculate_max_events.clone();
        use_effect_with((), move |_| {
            let calculate_max_events_clone = calculate_max_events.clone();

            // Initial calculation with a slight delay to ensure DOM is ready
            if let Some(window) = window() {
                let timeout_closure = Closure::wrap(Box::new(move || {
                    calculate_max_events_clone();
                }) as Box<dyn FnMut()>);

                let _ = window.set_timeout_with_callback_and_timeout_and_arguments_0(
                    timeout_closure.as_ref().unchecked_ref(),
                    100,
                );
                timeout_closure.forget();
            }

            // Setup resize listener
            let resize_closure = Closure::wrap(Box::new(move || {
                calculate_max_events();
            }) as Box<dyn Fn()>);

            if let Some(window) = window() {
                let _ = window.add_event_listener_with_callback(
                    "resize",
                    resize_closure.as_ref().unchecked_ref(),
                );
                resize_closure.forget(); // Keep the closure alive
            }

            || {}
        });
    }

    // Helper function to get calendar color for an event
    let get_event_color = |event: &VEvent| -> String {
        if let Some(calendar_path) = &event.calendar_path {
            // Check external calendars first (path format: "external_{id}")
            if calendar_path.starts_with("external_") {
                if let Ok(id_str) = calendar_path.strip_prefix("external_").unwrap_or("").parse::<i32>() {
                    if let Some(external_calendar) = props.external_calendars
                        .iter()
                        .find(|cal| cal.id == id_str)
                    {
                        return external_calendar.color.clone();
                    }
                }
            }
            // Check regular calendars
            else if let Some(user_info) = &props.user_info {
                if let Some(calendar) = user_info
                    .calendars
                    .iter()
                    .find(|cal| &cal.path == calendar_path)
                {
                    return calendar.color.clone();
                }
            }
        }
        "#3B82F6".to_string()
    };

    
    let weeks_needed = calculate_minimum_weeks_needed(first_weekday, days_in_month);
    
    // Use calculated weeks with height-based container sizing for proper fit
    let dynamic_style = format!("grid-template-rows: var(--weekday-header-height, 50px) repeat({}, 1fr);", weeks_needed);
    
    html! {
        <div class="calendar-grid" style={dynamic_style}>
            // Weekday headers
            <div class="weekday-header">{"Sun"}</div>
            <div class="weekday-header">{"Mon"}</div>
            <div class="weekday-header">{"Tue"}</div>
            <div class="weekday-header">{"Wed"}</div>
            <div class="weekday-header">{"Thu"}</div>
            <div class="weekday-header">{"Fri"}</div>
            <div class="weekday-header">{"Sat"}</div>

            // Days from previous month (grayed out)
            {
                days_from_prev_month.iter().map(|day| {
                    html! {
                        <div class="calendar-day prev-month">{*day}</div>
                    }
                }).collect::<Html>()
            }

            // Days of the current month
            {
                (1..=days_in_month).map(|day| {
                    let date = props.current_month.with_day(day).unwrap();
                    let is_today = date == props.today;
                    let is_selected = props.selected_date == Some(date);
                    let day_events = props.events.get(&date).cloned().unwrap_or_default();

                    // Calculate visible events and overflow
                    let max_events = *max_events_per_day as usize;
                    let visible_events: Vec<_> = day_events.iter().take(max_events).collect();
                    let hidden_count = day_events.len().saturating_sub(max_events);

                    html! {
                        <div
                            class={classes!(
                                "calendar-day",
                                if is_today { Some("today") } else { None },
                                if is_selected { Some("selected") } else { None }
                            )}
                            onclick={
                                if let Some(callback) = &props.on_day_select {
                                    let callback = callback.clone();
                                    Some(Callback::from(move |e: web_sys::MouseEvent| {
                                        e.stop_propagation(); // Prevent other handlers
                                        callback.emit(date);
                                    }))
                                } else {
                                    None
                                }
                            }
                            oncontextmenu={
                                if let Some(callback) = &props.on_calendar_context_menu {
                                    let callback = callback.clone();
                                    Some(Callback::from(move |e: web_sys::MouseEvent| {
                                        e.prevent_default();
                                        callback.emit((e, date));
                                    }))
                                } else {
                                    None
                                }
                            }
                        >
                            <div class="day-number">{day}</div>
                            <div class="day-events">
                                {
                                    visible_events.iter().map(|event| {
                                        let event_color = get_event_color(event);
                                        let is_refreshing = props.refreshing_event_uid.as_ref() == Some(&event.uid);

                                        let onclick = {
                                            let on_event_click = props.on_event_click.clone();
                                            let event = (*event).clone();
                                            Callback::from(move |_: web_sys::MouseEvent| {
                                                on_event_click.emit(event.clone());
                                            })
                                        };

                                        let oncontextmenu = {
                                            if let Some(callback) = &props.on_event_context_menu {
                                                let callback = callback.clone();
                                                let event = (*event).clone();
                                                Some(Callback::from(move |e: web_sys::MouseEvent| {
                                                    e.prevent_default();
                                                    callback.emit((e, event.clone()));
                                                }))
                                            } else {
                                                None
                                            }
                                        };

                                        html! {
                                            <div
                                                class={classes!("event-box", if is_refreshing { Some("refreshing") } else { None })}
                                                style={format!("background-color: {}", event_color)}
                                                data-external={event.categories.contains(&"__EXTERNAL_CALENDAR__".to_string()).to_string()}
                                                {onclick}
                                                {oncontextmenu}
                                            >
                                                <span class="event-title">{event.summary.as_ref().unwrap_or(&"Untitled".to_string())}</span>
                                                if !event.alarms.is_empty() {
                                                    <i class="fas fa-bell event-reminder-icon" title="Has reminders"></i>
                                                }
                                            </div>
                                        }
                                    }).collect::<Html>()
                                }
                                {
                                    if hidden_count > 0 {
                                        html! {
                                            <div class="more-events-indicator">
                                                {format!("+{} more", hidden_count)}
                                            </div>
                                        }
                                    } else {
                                        html! {}
                                    }
                                }
                            </div>
                        </div>
                    }
                }).collect::<Html>()
            }

            { render_next_month_days(days_from_prev_month.len(), days_in_month, calculate_minimum_weeks_needed(first_weekday, days_in_month)) }
        </div>
    }
}

fn calculate_minimum_weeks_needed(first_weekday: Weekday, days_in_month: u32) -> u32 {
    let days_before = match first_weekday {
        Weekday::Sun => 0,
        Weekday::Mon => 1,
        Weekday::Tue => 2,
        Weekday::Wed => 3,
        Weekday::Thu => 4,
        Weekday::Fri => 5,
        Weekday::Sat => 6,
    };
    let total_days_needed = days_before + days_in_month;
    (total_days_needed + 6) / 7 // Round up to get number of weeks
}

fn render_next_month_days(prev_days_count: usize, current_days_count: u32, weeks_needed: u32) -> Html {
    let total_slots = (weeks_needed * 7) as usize; // Dynamic based on weeks needed
    let used_slots = prev_days_count + current_days_count as usize;
    let remaining_slots = if used_slots < total_slots {
        total_slots - used_slots
    } else {
        0
    };

    (1..=remaining_slots)
        .map(|day| {
            html! {
                <div class="calendar-day next-month">{day}</div>
            }
        })
        .collect::<Html>()
}

fn get_days_in_month(date: NaiveDate) -> u32 {
    NaiveDate::from_ymd_opt(
        if date.month() == 12 {
            date.year() + 1
        } else {
            date.year()
        },
        if date.month() == 12 {
            1
        } else {
            date.month() + 1
        },
        1,
    )
    .unwrap()
    .pred_opt()
    .unwrap()
    .day()
}

fn get_days_from_previous_month(current_month: NaiveDate, first_weekday: Weekday) -> Vec<u32> {
    let days_before = match first_weekday {
        Weekday::Sun => 0,
        Weekday::Mon => 1,
        Weekday::Tue => 2,
        Weekday::Wed => 3,
        Weekday::Thu => 4,
        Weekday::Fri => 5,
        Weekday::Sat => 6,
    };

    if days_before == 0 {
        vec![]
    } else {
        let prev_month = if current_month.month() == 1 {
            NaiveDate::from_ymd_opt(current_month.year() - 1, 12, 1).unwrap()
        } else {
            NaiveDate::from_ymd_opt(current_month.year(), current_month.month() - 1, 1).unwrap()
        };

        let prev_month_days = get_days_in_month(prev_month);
        ((prev_month_days - days_before as u32 + 1)..=prev_month_days).collect()
    }
}
