use leptos::prelude::*;
use leptos::task::spawn_local;
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc, Duration};
use uuid::Uuid;
use std::collections::VecDeque;
use gloo_timers::callback::Timeout;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum NotificationLevel {
    Info,
    Success,
    Warning,
    Error,
    Critical,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NotificationData {
    pub id: String,
    pub title: Option<String>,
    pub message: String,
    pub level: NotificationLevel,
    pub timestamp: DateTime<Utc>,
    pub duration: Option<u32>, // seconds, None = persistent
    pub action: Option<NotificationAction>,
    pub dismissible: bool,
    pub progress: Option<f32>, // 0.0 to 1.0 for progress notifications
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NotificationAction {
    pub label: String,
    pub action_type: String,
    pub data: Option<serde_json::Value>,
}

impl NotificationData {
    pub fn new(message: &str, level: NotificationLevel) -> Self {
        Self {
            id: Uuid::new_v4().to_string(),
            title: None,
            message: message.to_string(),
            level,
            timestamp: Utc::now(),
            duration: Some(match level {
                NotificationLevel::Error | NotificationLevel::Critical => 10,
                NotificationLevel::Warning => 7,
                NotificationLevel::Success => 5,
                NotificationLevel::Info => 4,
            }),
            action: None,
            dismissible: true,
            progress: None,
        }
    }

    pub fn with_title(mut self, title: &str) -> Self {
        self.title = Some(title.to_string());
        self
    }

    pub fn with_action(mut self, label: &str, action_type: &str) -> Self {
        self.action = Some(NotificationAction {
            label: label.to_string(),
            action_type: action_type.to_string(),
            data: None,
        });
        self
    }

    pub fn persistent(mut self) -> Self {
        self.duration = None;
        self
    }

    pub fn with_progress(mut self, progress: f32) -> Self {
        self.progress = Some(progress.clamp(0.0, 1.0));
        self
    }

    pub fn non_dismissible(mut self) -> Self {
        self.dismissible = false;
        self
    }
}

#[component]
pub fn NotificationToast(
    notification: NotificationData,
    #[prop(optional)] on_dismiss: Option<Box<dyn Fn(String) + 'static + Send + Sync>>,
    #[prop(optional)] on_action: Option<Box<dyn Fn(NotificationAction) + 'static + Send + Sync>>,
) -> impl IntoView {
    let (visible, set_visible) = signal(true);
    let (progress, set_progress) = signal(notification.progress.unwrap_or(0.0));
    let (is_hovered, set_is_hovered) = signal(false);

    let notification_id = notification.id.clone();
    let dismiss_handler = on_dismiss.clone();

    // Auto-dismiss timer
    if let Some(duration) = notification.duration {
        let dismiss_timer = Duration::seconds(duration as i64);
        let notification_id_clone = notification_id.clone();
        let set_visible_clone = set_visible;

        spawn_local(async move {
            if !is_hovered.get() {
                gloo_timers::future::TimeoutFuture::new(duration * 1000).await;
                set_visible_clone.set(false);
            }
        });
    }

    // Progress animation for progress notifications
    if notification.progress.is_some() {
        Effect::new(move |_| {
            if let Some(prog) = notification.progress {
                set_progress.set(prog);
            }
        });
    }

    let toast_class = move || {
        format!(
            "toast-notification {} {} {}",
            match notification.level {
                NotificationLevel::Info => "toast-info",
                NotificationLevel::Success => "toast-success",
                NotificationLevel::Warning => "toast-warning",
                NotificationLevel::Error => "toast-error",
                NotificationLevel::Critical => "toast-critical",
            },
            if visible.get() { "toast-visible" } else { "toast-hidden" },
            if is_hovered.get() { "toast-hovered" } else { "" }
        )
    };

    let icon_class = move || match notification.level {
        NotificationLevel::Info => "bi-info-circle",
        NotificationLevel::Success => "bi-check-circle",
        NotificationLevel::Warning => "bi-exclamation-triangle",
        NotificationLevel::Error => "bi-x-circle",
        NotificationLevel::Critical => "bi-exclamation-octagon",
    };

    let dismiss_notification = move |_| {
        set_visible.set(false);
        if let Some(handler) = &on_dismiss {
            handler(notification_id.clone());
        }
    };

    let handle_action = move |action: NotificationAction| {
        if let Some(handler) = &on_action {
            handler(action);
        }
    };

    view! {
        <div
            class=toast_class
            on:mouseenter=move |_| set_is_hovered.set(true)
            on:mouseleave=move |_| set_is_hovered.set(false)
        >
            <div class="toast-content">
                <div class="toast-icon">
                    <i class=icon_class></i>
                </div>

                <div class="toast-body">
                    <Show when=move || notification.title.is_some()>
                        <div class="toast-title">
                            {notification.title.clone().unwrap_or_default()}
                        </div>
                    </Show>

                    <div class="toast-message">
                        {notification.message.clone()}
                    </div>

                    <Show when=move || notification.progress.is_some()>
                        <div class="toast-progress">
                            <div class="progress-bar">
                                <div
                                    class="progress-fill"
                                    style=move || format!("width: {}%", progress.get() * 100.0)
                                ></div>
                            </div>
                            <span class="progress-text">
                                {move || format!("{:.0}%", progress.get() * 100.0)}
                            </span>
                        </div>
                    </Show>

                    <div class="toast-timestamp">
                        {notification.timestamp.format("%H:%M:%S").to_string()}
                    </div>
                </div>

                <div class="toast-actions">
                    <Show when=move || notification.action.is_some()>
                        <button
                            class="toast-action-btn"
                            on:click=move |_| {
                                if let Some(action) = &notification.action {
                                    handle_action(action.clone());
                                }
                            }
                        >
                            {notification.action.as_ref().map(|a| a.label.clone()).unwrap_or_default()}
                        </button>
                    </Show>

                    <Show when=move || notification.dismissible>
                        <button
                            class="toast-dismiss-btn"
                            on:click=dismiss_notification
                            title="Dismiss"
                        >
                            <i class="bi-x"></i>
                        </button>
                    </Show>
                </div>
            </div>

            // Auto-dismiss progress indicator
            <Show when=move || notification.duration.is_some() && !is_hovered.get()>
                <div class="toast-timer">
                    <div class="timer-progress"></div>
                </div>
            </Show>
        </div>
    }
}

#[component]
pub fn NotificationContainer(
    notifications: ReadSignal<Vec<NotificationData>>,
    #[prop(optional)] position: NotificationPosition,
    #[prop(optional)] max_visible: Option<usize>,
    #[prop(optional)] on_dismiss: Option<Box<dyn Fn(String) + 'static + Send + Sync>>,
    #[prop(optional)] on_action: Option<Box<dyn Fn(NotificationAction) + 'static + Send + Sync>>,
) -> impl IntoView {
    let max_visible = max_visible.unwrap_or(5);

    let container_class = move || {
        format!(
            "notification-container {}",
            match position {
                NotificationPosition::TopRight => "position-top-right",
                NotificationPosition::TopLeft => "position-top-left",
                NotificationPosition::BottomRight => "position-bottom-right",
                NotificationPosition::BottomLeft => "position-bottom-left",
                NotificationPosition::TopCenter => "position-top-center",
                NotificationPosition::BottomCenter => "position-bottom-center",
            }
        )
    };

    let visible_notifications = Memo::new(move |_| {
        let mut notifications = notifications.get();

        // Sort by timestamp (newest first for top positions, oldest first for bottom positions)
        match position {
            NotificationPosition::TopRight | NotificationPosition::TopLeft | NotificationPosition::TopCenter => {
                notifications.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
            }
            NotificationPosition::BottomRight | NotificationPosition::BottomLeft | NotificationPosition::BottomCenter => {
                notifications.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
            }
        }

        notifications.into_iter().take(max_visible).collect::<Vec<_>>()
    });

    view! {
        <div class=container_class>
            <For
                each=move || visible_notifications.get()
                key=|notification| notification.id.clone()
                children=move |notification| {
                    view! {
                        <NotificationToast
                            notification=notification
                            on_dismiss=on_dismiss.clone()
                            on_action=on_action.clone()
                        />
                    }
                }
            />
        </div>
    }
}

#[derive(Debug, Clone, Copy)]
pub enum NotificationPosition {
    TopRight,
    TopLeft,
    BottomRight,
    BottomLeft,
    TopCenter,
    BottomCenter,
}

impl Default for NotificationPosition {
    fn default() -> Self {
        NotificationPosition::TopRight
    }
}

// Notification manager for global state
#[derive(Debug, Clone)]
pub struct NotificationManager {
    notifications: RwSignal<VecDeque<NotificationData>>,
    max_notifications: usize,
}

impl NotificationManager {
    pub fn new(max_notifications: usize) -> Self {
        Self {
            notifications: RwSignal::new(VecDeque::new()),
            max_notifications,
        }
    }

    pub fn show(&self, notification: NotificationData) {
        self.notifications.update(|notifications| {
            notifications.push_front(notification);

            // Limit the number of notifications
            while notifications.len() > self.max_notifications {
                notifications.pop_back();
            }
        });
    }

    pub fn show_info(&self, message: &str) {
        self.show(NotificationData::new(message, NotificationLevel::Info));
    }

    pub fn show_success(&self, message: &str) {
        self.show(NotificationData::new(message, NotificationLevel::Success));
    }

    pub fn show_warning(&self, message: &str) {
        self.show(NotificationData::new(message, NotificationLevel::Warning));
    }

    pub fn show_error(&self, message: &str) {
        self.show(NotificationData::new(message, NotificationLevel::Error));
    }

    pub fn show_critical(&self, message: &str) {
        self.show(NotificationData::new(message, NotificationLevel::Critical).persistent());
    }

    pub fn show_progress(&self, message: &str, progress: f32) {
        let notification = NotificationData::new(message, NotificationLevel::Info)
            .with_progress(progress)
            .non_dismissible();
        self.show(notification);
    }

    pub fn update_progress(&self, notification_id: &str, progress: f32) {
        self.notifications.update(|notifications| {
            if let Some(notification) = notifications.iter_mut().find(|n| n.id == notification_id) {
                notification.progress = Some(progress.clamp(0.0, 1.0));
            }
        });
    }

    pub fn dismiss(&self, notification_id: &str) {
        self.notifications.update(|notifications| {
            notifications.retain(|n| n.id != notification_id);
        });
    }

    pub fn dismiss_all(&self) {
        self.notifications.update(|notifications| {
            notifications.clear();
        });
    }

    pub fn dismiss_by_level(&self, level: NotificationLevel) {
        self.notifications.update(|notifications| {
            notifications.retain(|n| n.level != level);
        });
    }

    pub fn get_notifications(&self) -> ReadSignal<Vec<NotificationData>> {
        Memo::new(move |_| {
            self.notifications.get().iter().cloned().collect()
        }).into()
    }

    pub fn get_count(&self) -> ReadSignal<usize> {
        Memo::new(move |_| {
            self.notifications.get().len()
        }).into()
    }

    pub fn get_count_by_level(&self, level: NotificationLevel) -> ReadSignal<usize> {
        Memo::new(move |_| {
            self.notifications.get().iter().filter(|n| n.level == level).count()
        }).into()
    }
}

// Global notification context
#[derive(Clone)]
pub struct NotificationContext {
    manager: NotificationManager,
}

impl NotificationContext {
    pub fn new() -> Self {
        Self {
            manager: NotificationManager::new(50),
        }
    }

    pub fn provide() {
        provide_context(Self::new());
    }

    pub fn use_notifications() -> NotificationManager {
        use_context::<NotificationContext>()
            .expect("NotificationContext not provided")
            .manager
    }
}

// Notification hook for easy usage
pub fn use_notifications() -> NotificationManager {
    NotificationContext::use_notifications()
}

// Predefined notification templates
pub mod templates {
    use super::*;

    pub fn save_success() -> NotificationData {
        NotificationData::new("Changes saved successfully", NotificationLevel::Success)
            .with_title("Saved")
    }

    pub fn save_error(error: &str) -> NotificationData {
        NotificationData::new(&format!("Failed to save: {}", error), NotificationLevel::Error)
            .with_title("Save Error")
            .with_action("Retry", "retry_save")
    }

    pub fn network_error() -> NotificationData {
        NotificationData::new("Network connection error. Please check your internet connection.", NotificationLevel::Error)
            .with_title("Connection Error")
            .with_action("Retry", "retry_connection")
    }

    pub fn unauthorized() -> NotificationData {
        NotificationData::new("Your session has expired. Please log in again.", NotificationLevel::Warning)
            .with_title("Session Expired")
            .with_action("Login", "redirect_login")
            .persistent()
    }

    pub fn websocket_connected() -> NotificationData {
        NotificationData::new("Real-time connection established", NotificationLevel::Success)
            .with_title("Connected")
    }

    pub fn websocket_disconnected() -> NotificationData {
        NotificationData::new("Real-time connection lost. Attempting to reconnect...", NotificationLevel::Warning)
            .with_title("Disconnected")
            .persistent()
    }

    pub fn export_started(format: &str) -> NotificationData {
        NotificationData::new(&format!("Starting {} export...", format), NotificationLevel::Info)
            .with_title("Export Started")
            .with_progress(0.0)
    }

    pub fn export_completed(format: &str) -> NotificationData {
        NotificationData::new(&format!("{} export completed successfully", format), NotificationLevel::Success)
            .with_title("Export Complete")
            .with_action("Download", "download_export")
    }

    pub fn widget_added(widget_type: &str) -> NotificationData {
        NotificationData::new(&format!("{} widget added to dashboard", widget_type), NotificationLevel::Info)
    }

    pub fn widget_removed(widget_type: &str) -> NotificationData {
        NotificationData::new(&format!("{} widget removed from dashboard", widget_type), NotificationLevel::Info)
    }

    pub fn dashboard_saved() -> NotificationData {
        NotificationData::new("Dashboard layout saved", NotificationLevel::Success)
    }

    pub fn theme_changed(theme: &str) -> NotificationData {
        NotificationData::new(&format!("Theme changed to {}", theme), NotificationLevel::Info)
    }
}
