// SPDX-License-Identifier: Apache-2.0
//! Global app state shared via Leptos context. Holds dummy-data signals
//! for the mockup: language, toasts, upload draft, album/photo store.

use leptos::prelude::*;

use crate::i18n::Lang;
use crate::mock::{Album, Photo, seed_albums, seed_photos};

#[derive(Clone, Debug)]
pub struct Toast {
    pub id: u32,
    pub message: String,
    pub kind: ToastKind,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ToastKind {
    Neutral,
    Success,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CleaningLevel {
    Privacy,
    Standard,
    Detailed,
}

impl CleaningLevel {
    #[must_use]
    pub fn code(self) -> &'static str {
        match self {
            Self::Privacy => "privacy",
            Self::Standard => "standard",
            Self::Detailed => "detailed",
        }
    }
}

#[derive(Clone, Debug)]
pub struct UploadDraft {
    pub selected_count: usize,
    pub level: CleaningLevel,
    pub target_album_id: Option<String>,
}

impl Default for UploadDraft {
    fn default() -> Self {
        Self {
            selected_count: 0,
            level: CleaningLevel::Standard,
            target_album_id: None,
        }
    }
}

/// Application-wide reactive state.
#[derive(Clone, Copy)]
pub struct AppState {
    pub lang: RwSignal<Lang>,
    pub albums: RwSignal<Vec<Album>>,
    pub photos: RwSignal<Vec<Photo>>,
    pub upload: RwSignal<UploadDraft>,
    pub toasts: RwSignal<Vec<Toast>>,
    pub offline: RwSignal<bool>,
    pub high_contrast: RwSignal<bool>,
    pub large_text: RwSignal<bool>,
    pub reduced_motion: RwSignal<bool>,
    /// How many upload flows the user has completed. Used by the upload
    /// start page to decide whether to show the full educational flow
    /// or the compact quick-add panel.
    pub upload_count: RwSignal<u32>,
    next_toast_id: RwSignal<u32>,
}

impl AppState {
    /// Construct fresh state and provide it via Leptos context.
    pub fn provide() {
        let state = Self {
            lang: RwSignal::new(Lang::En),
            albums: RwSignal::new(seed_albums()),
            photos: RwSignal::new(seed_photos()),
            upload: RwSignal::new(UploadDraft::default()),
            toasts: RwSignal::new(Vec::new()),
            offline: RwSignal::new(false),
            high_contrast: RwSignal::new(false),
            large_text: RwSignal::new(false),
            reduced_motion: RwSignal::new(false),
            upload_count: RwSignal::new(0),
            next_toast_id: RwSignal::new(1),
        };
        provide_context(state);
    }

    /// Read the current state from context.
    ///
    /// # Panics
    /// Panics if `AppState` was not provided. This is a programmer error.
    pub fn get() -> Self {
        use_context::<Self>().expect("AppState not provided")
    }

    /// Push a transient toast message. It auto-dismisses after ~3s.
    pub fn toast(&self, message: impl Into<String>, kind: ToastKind) {
        let id = self.next_toast_id.get();
        self.next_toast_id.set(id.wrapping_add(1));
        let message = message.into();
        self.toasts.update(|t| t.push(Toast { id, message, kind }));

        // Auto-dismiss: schedule removal via setTimeout. Only meaningful in the
        // browser — on the server this block is compiled out entirely.
        #[cfg(target_arch = "wasm32")]
        {
            use wasm_bindgen::JsCast as _;
            let toasts = self.toasts;
            let f = wasm_bindgen::closure::Closure::once_into_js(move || {
                toasts.update(|t| t.retain(|x| x.id != id));
            });
            if let Some(w) = web_sys::window() {
                let _ = w.set_timeout_with_callback_and_timeout_and_arguments_0(
                    f.as_ref().unchecked_ref(),
                    3200,
                );
            }
        }
    }
}
