use web_sys::{window, Notification, NotificationOptions, NotificationPermission};
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct NotificationManager {
    // Track displayed notifications to prevent duplicates
    active_notifications: HashMap<String, Notification>,
}

#[derive(Debug, Clone)]
pub struct AlarmNotification {
    pub event_uid: String,
    pub event_summary: String,
    pub event_location: Option<String>,
    pub alarm_time: chrono::NaiveDateTime,
}

impl NotificationManager {
    pub fn new() -> Self {
        Self {
            active_notifications: HashMap::new(),
        }
    }

    /// Check if the browser supports notifications
    pub fn is_supported() -> bool {
        // Check if the Notification constructor exists on the window
        if let Some(window) = window() {
            let has_notification = js_sys::Reflect::has(&window, &"Notification".into()).unwrap_or(false);
            
            // Additional check - try to access Notification directly via JsValue
            let window_js: &wasm_bindgen::JsValue = window.as_ref();
            let direct_check = js_sys::Reflect::get(window_js, &"Notification".into()).unwrap_or(wasm_bindgen::JsValue::UNDEFINED);
            let has_direct = !direct_check.is_undefined();
            
            // Use either check
            has_notification || has_direct
        } else {
            false
        }
    }

    /// Get current notification permission status
    pub fn get_permission() -> NotificationPermission {
        if Self::is_supported() {
            Notification::permission()
        } else {
            NotificationPermission::Denied
        }
    }

    /// Force request notification permission (even if previously denied)
    pub async fn force_request_permission() -> Result<NotificationPermission, JsValue> {
        if !Self::is_supported() {
            return Ok(NotificationPermission::Denied);
        }

        // Always request permission, regardless of current status
        let promise = Notification::request_permission()?;
        let js_value = JsFuture::from(promise).await?;
        
        // Convert JS string back to NotificationPermission
        if let Some(permission_str) = js_value.as_string() {
            match permission_str.as_str() {
                "granted" => Ok(NotificationPermission::Granted),
                "denied" => Ok(NotificationPermission::Denied),
                _ => Ok(NotificationPermission::Default),
            }
        } else {
            Ok(NotificationPermission::Denied)
        }
    }

    /// Request notification permission from the user
    pub async fn request_permission() -> Result<NotificationPermission, JsValue> {
        if !Self::is_supported() {
            return Ok(NotificationPermission::Denied);
        }

        // Check current permission status
        let current_permission = Notification::permission();
        if current_permission != NotificationPermission::Default {
            return Ok(current_permission);
        }

        // Request permission
        let promise = Notification::request_permission()?;
        let js_value = JsFuture::from(promise).await?;
        
        // Convert JS string back to NotificationPermission
        if let Some(permission_str) = js_value.as_string() {
            match permission_str.as_str() {
                "granted" => Ok(NotificationPermission::Granted),
                "denied" => Ok(NotificationPermission::Denied),
                _ => Ok(NotificationPermission::Default),
            }
        } else {
            Ok(NotificationPermission::Denied)
        }
    }

    /// Display a notification for an alarm
    pub fn show_alarm_notification(&mut self, alarm: AlarmNotification) -> Result<(), JsValue> {
        // Check permission
        if Self::get_permission() != NotificationPermission::Granted {
            return Ok(()); // Don't error, just skip
        }

        // Check if notification already exists for this event
        if self.active_notifications.contains_key(&alarm.event_uid) {
            return Ok(()); // Already showing notification for this event
        }

        // Create notification options
        let options = NotificationOptions::new();
        
        // Set notification body with time and location
        let body = if let Some(location) = &alarm.event_location {
            format!("📅 {}\n📍 {}", 
                alarm.alarm_time.format("%H:%M"), 
                location
            )
        } else {
            format!("📅 {}", alarm.alarm_time.format("%H:%M"))
        };
        options.set_body(&body);

        // Set icon
        options.set_icon("/favicon.ico");
        
        // Set tag to prevent duplicates
        options.set_tag(&alarm.event_uid);
        
        // Set require interaction to keep notification visible
        options.set_require_interaction(true);

        // Create and show notification
        let notification = Notification::new_with_options(&alarm.event_summary, &options)?;
        
        // Store reference to track active notifications
        self.active_notifications.insert(alarm.event_uid.clone(), notification.clone());

        // Set up click handler to focus the calendar app
        let _event_uid = alarm.event_uid.clone();
        let onclick_closure = Closure::wrap(Box::new(move |_event: web_sys::Event| {
            // Focus the window when notification is clicked
            if let Some(window) = window() {
                let _ = window.focus();
            }
            
        }) as Box<dyn FnMut(_)>);
        
        notification.set_onclick(Some(onclick_closure.as_ref().unchecked_ref()));
        onclick_closure.forget(); // Keep closure alive

        // Set up close handler to clean up tracking
        let event_uid_close = alarm.event_uid.clone();
        let mut active_notifications_close = self.active_notifications.clone();
        let onclose_closure = Closure::wrap(Box::new(move |_event: web_sys::Event| {
            active_notifications_close.remove(&event_uid_close);
        }) as Box<dyn FnMut(_)>);
        
        notification.set_onclose(Some(onclose_closure.as_ref().unchecked_ref()));
        onclose_closure.forget(); // Keep closure alive

        Ok(())
    }

    /// Close notification for a specific event
    pub fn close_notification(&mut self, event_uid: &str) {
        if let Some(notification) = self.active_notifications.remove(event_uid) {
            notification.close();
        }
    }


    /// Check if notification exists for event
    pub fn has_notification(&self, event_uid: &str) -> bool {
        self.active_notifications.contains_key(event_uid)
    }
}

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