use calendar_models::{VAlarm, AlarmAction, AlarmTrigger, VEvent};
use chrono::{Duration, Local, NaiveDateTime};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::services::{NotificationManager, AlarmNotification};
use gloo_storage::{LocalStorage, Storage};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScheduledAlarm {
    pub id: String,                    // Unique alarm ID
    pub event_uid: String,             // Event this alarm belongs to
    pub event_summary: String,         // Event title for notification
    pub event_location: Option<String>, // Event location for notification
    pub event_start: NaiveDateTime,    // Event start time (local)
    pub trigger_time: NaiveDateTime,   // When alarm should trigger (local)
    pub alarm_action: AlarmAction,     // Type of alarm
    pub status: AlarmStatus,           // Current status
    pub created_at: NaiveDateTime,     // When alarm was scheduled
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum AlarmStatus {
    Pending,    // Waiting to trigger
    Triggered,  // Has been triggered
    Dismissed,  // User dismissed
    Expired,    // Past due (event ended)
}

#[derive(Debug, Clone)]
pub struct AlarmScheduler {
    scheduled_alarms: HashMap<String, ScheduledAlarm>,
    notification_manager: NotificationManager,
}

const ALARMS_STORAGE_KEY: &str = "scheduled_alarms";

impl AlarmScheduler {
    pub fn new() -> Self {
        let mut scheduler = Self {
            scheduled_alarms: HashMap::new(),
            notification_manager: NotificationManager::new(),
        };
        
        // Load alarms from localStorage
        scheduler.load_alarms_from_storage();
        scheduler
    }
    
    /// Load alarms from localStorage
    fn load_alarms_from_storage(&mut self) {
        if let Ok(alarms) = LocalStorage::get::<HashMap<String, ScheduledAlarm>>(ALARMS_STORAGE_KEY) {
            self.scheduled_alarms = alarms;
        }
    }
    
    /// Save alarms to localStorage
    fn save_alarms_to_storage(&self) {
        if let Err(e) = LocalStorage::set(ALARMS_STORAGE_KEY, &self.scheduled_alarms) {
            web_sys::console::error_1(
                &format!("Failed to save alarms to localStorage: {:?}", e).into()
            );
        }
    }

    /// Schedule alarms for an event
    pub fn schedule_event_alarms(&mut self, event: &VEvent) {
        // Check notification permission before scheduling
        let permission = NotificationManager::get_permission();
        if permission != web_sys::NotificationPermission::Granted && !event.alarms.is_empty() {
            // Try to force request permission asynchronously
            wasm_bindgen_futures::spawn_local(async move {
                let _ = NotificationManager::force_request_permission().await;
            });
        }
        
        // Remove any existing alarms for this event
        self.remove_event_alarms(&event.uid);

        // Get event details
        let event_summary = event.summary.as_ref().unwrap_or(&"Untitled Event".to_string()).clone();
        let event_location = event.location.clone();
        let event_start = event.dtstart;

        // Schedule each alarm
        for alarm in &event.alarms {
            if let Some(scheduled_alarm) = self.create_scheduled_alarm(
                event,
                alarm,
                &event_summary,
                &event_location,
                event_start,
            ) {
                self.scheduled_alarms.insert(scheduled_alarm.id.clone(), scheduled_alarm);
            }
        }

        // Save to localStorage
        self.save_alarms_to_storage();
    }

    /// Create a scheduled alarm from a VAlarm
    fn create_scheduled_alarm(
        &self,
        event: &VEvent,
        valarm: &VAlarm,
        event_summary: &str,
        event_location: &Option<String>,
        event_start: NaiveDateTime,
    ) -> Option<ScheduledAlarm> {
        // Only handle Display alarms for now
        if valarm.action != AlarmAction::Display {
            return None;
        }

        // Calculate trigger time
        let trigger_time = match &valarm.trigger {
            AlarmTrigger::Duration(duration) => {
                // Duration relative to event start
                let trigger_time = event_start + *duration;
                
                // Ensure trigger time is not in the past (with 30 second tolerance)
                let now = Local::now().naive_local();
                if trigger_time < now - Duration::seconds(30) {
                    web_sys::console::warn_1(
                        &format!("Skipping past alarm for event: {} (trigger: {})", 
                            event_summary, 
                            trigger_time.format("%Y-%m-%d %H:%M:%S")
                        ).into()
                    );
                    return None;
                }
                
                trigger_time
            }
            AlarmTrigger::DateTime(datetime) => {
                // Absolute datetime - convert to local time
                let local_trigger = datetime.with_timezone(&Local).naive_local();
                
                // Ensure trigger time is not in the past
                let now = Local::now().naive_local();
                if local_trigger < now - Duration::seconds(30) {
                    web_sys::console::warn_1(
                        &format!("Skipping past absolute alarm for event: {} (trigger: {})", 
                            event_summary, 
                            local_trigger.format("%Y-%m-%d %H:%M:%S")
                        ).into()
                    );
                    return None;
                }
                
                local_trigger
            }
        };

        // Generate unique alarm ID
        let alarm_id = format!("{}_{}", event.uid, trigger_time.and_utc().timestamp());

        Some(ScheduledAlarm {
            id: alarm_id,
            event_uid: event.uid.clone(),
            event_summary: event_summary.to_string(),
            event_location: event_location.clone(),
            event_start,
            trigger_time,
            alarm_action: valarm.action.clone(),
            status: AlarmStatus::Pending,
            created_at: Local::now().naive_local(),
        })
    }

    /// Remove all alarms for an event
    pub fn remove_event_alarms(&mut self, event_uid: &str) {
        let alarm_ids: Vec<String> = self.scheduled_alarms
            .iter()
            .filter(|(_, alarm)| alarm.event_uid == event_uid)
            .map(|(id, _)| id.clone())
            .collect();

        for alarm_id in alarm_ids {
            self.scheduled_alarms.remove(&alarm_id);
        }

        // Also close any active notifications for this event
        self.notification_manager.close_notification(event_uid);
        
        // Save to localStorage
        self.save_alarms_to_storage();
    }

    /// Check for alarms that should trigger now and trigger them
    pub fn check_and_trigger_alarms(&mut self) -> usize {
        // Reload alarms from localStorage to ensure we have the latest data
        self.load_alarms_from_storage();
        
        let now = Local::now().naive_local();
        let mut triggered_count = 0;

        // Find alarms that should trigger (within 30 seconds tolerance)
        let alarms_to_trigger: Vec<ScheduledAlarm> = self.scheduled_alarms
            .values()
            .filter(|alarm| {
                alarm.status == AlarmStatus::Pending && 
                alarm.trigger_time <= now + Duration::seconds(30) &&
                alarm.trigger_time >= now - Duration::seconds(30)
            })
            .cloned()
            .collect();

        for alarm in alarms_to_trigger {
            if self.trigger_alarm(&alarm) {
                // Mark alarm as triggered
                if let Some(scheduled_alarm) = self.scheduled_alarms.get_mut(&alarm.id) {
                    scheduled_alarm.status = AlarmStatus::Triggered;
                }
                triggered_count += 1;
            }
        }

        // Clean up expired alarms (events that ended more than 1 hour ago)
        self.cleanup_expired_alarms();
        
        // Save to localStorage if any changes were made
        if triggered_count > 0 {
            self.save_alarms_to_storage();
        }

        triggered_count
    }

    /// Trigger a specific alarm
    fn trigger_alarm(&mut self, alarm: &ScheduledAlarm) -> bool {
        // Don't trigger if already showing notification for this event
        if self.notification_manager.has_notification(&alarm.event_uid) {
            return false;
        }

        let alarm_notification = AlarmNotification {
            event_uid: alarm.event_uid.clone(),
            event_summary: alarm.event_summary.clone(),
            event_location: alarm.event_location.clone(),
            alarm_time: alarm.event_start,
        };

        match self.notification_manager.show_alarm_notification(alarm_notification) {
            Ok(()) => true,
            Err(err) => {
                web_sys::console::error_1(
                    &format!("Failed to trigger alarm: {:?}", err).into()
                );
                false
            }
        }
    }

    /// Clean up expired alarms
    fn cleanup_expired_alarms(&mut self) {
        let now = Local::now().naive_local();
        let cutoff_time = now - Duration::hours(1);

        let expired_alarm_ids: Vec<String> = self.scheduled_alarms
            .iter()
            .filter(|(_, alarm)| {
                // Mark as expired if event ended more than 1 hour ago
                alarm.event_start < cutoff_time
            })
            .map(|(id, _)| id.clone())
            .collect();

        for alarm_id in &expired_alarm_ids {
            if let Some(alarm) = self.scheduled_alarms.get_mut(alarm_id) {
                alarm.status = AlarmStatus::Expired;
            }
        }

        // Remove expired alarms from memory
        let had_expired = !expired_alarm_ids.is_empty();
        for alarm_id in expired_alarm_ids {
            self.scheduled_alarms.remove(&alarm_id);
        }
        
        // Save to localStorage if any expired alarms were removed
        if had_expired {
            self.save_alarms_to_storage();
        }
    }


    /// Request notification permission
    pub async fn request_notification_permission(&self) -> Result<web_sys::NotificationPermission, wasm_bindgen::JsValue> {
        NotificationManager::request_permission().await
    }

}

impl Default for AlarmScheduler {
    fn default() -> Self {
        Self::new()
    }
}