use ruin_app::prelude::*;

#[ruin_runtime::async_main]
async fn main() -> ruin_app::Result<()> {
    App::new()
        .window(
            Window::new()
                .title("RUIN Workspace")
                .app_id("dev.ruin.composition-context")
                .size(1280.0, 840.0),
        )
        .mount(view! {
            WorkspaceRoot() {}
        })
        .run()
        .await
}

#[component]
fn WorkspaceRoot() -> impl IntoView {
    let notifications = Notifications::new(use_signal(|| {
        "No workspace actions yet. Click a navigation button to emit a context-driven update."
            .to_string()
    }));
    let session = SessionInfo::guest();

    view! {
        provide::<NotificationsContext>(value = notifications.clone()) {
            provide::<SessionContext>(value = session.clone()) {
                provide::<AppThemeContext>(value = ThemePalette::workspace()) {
                    WorkspaceShell() {}
                }
            }
        }
    }
}

#[component]
fn WorkspaceShell() -> impl IntoView {
    let session = use_context::<SessionContext>();
    let notifications = use_context::<NotificationsContext>();
    let theme = use_context::<AppThemeContext>();

    use_window_title({
        let session = session.clone();
        move || format!("RUIN Workspace - {}", session.workspace)
    });

    view! {
        column(gap = 16.0, padding = 20.0, background = theme.canvas) {
            block(
                padding = 14.0,
                gap = 8.0,
                background = theme.panel,
                border_radius = 12.0,
                border = (1.0, theme.accent),
            ) {
                text(role = TextRole::Heading(1), size = 30.0, weight = FontWeight::Semibold) {
                    "Composition and typed context"
                }
                text(color = theme.muted, wrap = TextWrap::Word) {
                    "This example implements real marker-keyed context providers. `AppThemeContext` \
                     and `SidebarThemeContext` both carry the same `ThemePalette` value type, but \
                     descendants resolve them independently by marker type. Nested providers of the \
                     same marker also shadow outer values as you would expect."
                }
                text(color = theme.muted) {
                    "Last notification: ";
                    notifications.last_message.clone()
                }
            }

            row(gap = 16.0) {
                provide::<SidebarThemeContext>(value = ThemePalette::sidebar()) {
                    Sidebar() {}
                }

                Dashboard() {}
            }
        }
    }
}

#[component]
fn Sidebar() -> impl IntoView {
    let theme = use_context::<SidebarThemeContext>();
    let session = use_context::<SessionContext>();
    let notifications = use_context::<NotificationsContext>();

    view! {
        block(
            width = 300.0,
            padding = 16.0,
            gap = 12.0,
            background = theme.panel,
            border_radius = 14.0,
            border = (1.0, theme.accent),
        ) {
            text(size = 24.0, weight = FontWeight::Semibold, color = theme.text) {
                "Workspace nav"
            }

            text(color = theme.muted, wrap = TextWrap::Word) {
                "Signed in as ";
                session.user.clone();
                " in ";
                session.workspace.clone();
            }

            button(on_press = {
                let notifications = notifications.clone();
                move |_| notifications.info("Opened the Home workspace panel")
            }) { "Home" }

            button(on_press = {
                let notifications = notifications.clone();
                move |_| notifications.info("Opened the Projects workspace panel")
            }) { "Projects" }

            button(on_press = {
                let notifications = notifications.clone();
                move |_| notifications.info("Opened the Settings workspace panel")
            }) { "Settings" }

            block(
                padding = 12.0,
                gap = 8.0,
                background = theme.canvas,
                border_radius = 10.0,
            ) {
                text(weight = FontWeight::Semibold, color = theme.accent) { "Sidebar theme" }
                text(color = theme.muted, wrap = TextWrap::Word) {
                    "This panel resolves `SidebarThemeContext`, even though the rest of the \
                     workspace uses `AppThemeContext` with the same `ThemePalette` value type."
                }
            }
        }
    }
}

#[component]
fn Dashboard() -> impl IntoView {
    let theme = use_context::<AppThemeContext>();
    let session = use_context::<SessionContext>();

    view! {
        column(flex = 1.0, gap = 16.0) {
            block(
                padding = 16.0,
                gap = 10.0,
                background = theme.panel,
                border_radius = 14.0,
                border = (1.0, theme.accent),
            ) {
                text(size = 24.0, weight = FontWeight::Semibold, color = theme.text) {
                    "Workspace dashboard"
                }
                text(color = theme.muted, wrap = TextWrap::Word) {
                    "The main content resolves `AppThemeContext` and `SessionContext` from the \
                     outer providers. These values are regular cloned Rust values; the context \
                     system is responsible only for scoping and lookup by marker type."
                }
                text(color = theme.muted) {
                    "Active workspace: ";
                    session.workspace.clone()
                }
            }

            column(gap = 16.0) {
                ThemeCard(
                    title = "App theme".to_string(),
                    subtitle = "Resolved from the outer `AppThemeContext` provider.".to_string(),
                    theme = theme.clone(),
                ) {}

                provide::<AppThemeContext>(value = ThemePalette::inspector()) {
                    InspectorPanel() {}
                }
            }
        }
    }
}

#[component]
fn ThemeCard(title: String, subtitle: String, theme: ThemePalette) -> impl IntoView {
    view! {
        block(
            padding = 16.0,
            gap = 10.0,
            background = theme.panel,
            border_radius = 14.0,
            border = (1.0, theme.accent),
        ) {
            text(size = 20.0, weight = FontWeight::Semibold, color = theme.text) { title }
            text(color = theme.muted, wrap = TextWrap::Word) { subtitle }
            block(
                height = 56.0,
                background = theme.canvas,
                border_radius = 10.0,
                border = (2.0, theme.accent),
            ) {}
        }
    }
}

#[component]
fn InspectorPanel() -> impl IntoView {
    let theme = use_context::<AppThemeContext>();
    let notifications = use_context::<NotificationsContext>();

    view! {
        block(
            padding = 16.0,
            gap = 12.0,
            background = theme.canvas,
            border_radius = 14.0,
            border = (1.0, theme.accent),
        ) {
            text(size = 18.0, weight = FontWeight::Semibold, color = theme.accent) {
                "Inspector panel"
            }

            ThemeCard(
                title = "Nested override".to_string(),
                subtitle = "This panel shadows the outer `AppThemeContext` with a nearer provider of the same marker type.".to_string(),
                theme = theme.clone(),
            ) {}

            button(on_press = {
                let notifications = notifications.clone();
                move |_| notifications.info("Inspector theme override is active")
            }) { "Emit inspector notification" }
        }
    }
}

#[context_provider(ThemePalette)]
struct AppThemeContext;

#[context_provider(ThemePalette)]
struct SidebarThemeContext;

#[context_provider(SessionInfo)]
struct SessionContext;

#[context_provider(Notifications)]
struct NotificationsContext;

#[derive(Clone)]
struct ThemePalette {
    canvas: Color,
    panel: Color,
    accent: Color,
    text: Color,
    muted: Color,
}

impl ThemePalette {
    fn workspace() -> Self {
        Self {
            canvas: Color::rgb(0x0E, 0x14, 0x20),
            panel: Color::rgb(0x16, 0x1F, 0x30),
            accent: Color::rgb(0x71, 0xA7, 0xF7),
            text: Color::rgb(0xF5, 0xF7, 0xFB),
            muted: Color::rgb(0x9A, 0xA9, 0xC2),
        }
    }

    fn sidebar() -> Self {
        Self {
            canvas: Color::rgb(0x0F, 0x17, 0x24),
            panel: Color::rgb(0x1A, 0x24, 0x36),
            accent: Color::rgb(0x8B, 0xC3, 0x7E),
            text: Color::rgb(0xF3, 0xF7, 0xEF),
            muted: Color::rgb(0xA7, 0xB5, 0xA4),
        }
    }

    fn inspector() -> Self {
        Self {
            canvas: Color::rgb(0x22, 0x17, 0x0F),
            panel: Color::rgb(0x35, 0x24, 0x16),
            accent: Color::rgb(0xF4, 0xB7, 0x56),
            text: Color::rgb(0xFC, 0xF6, 0xEB),
            muted: Color::rgb(0xCF, 0xB8, 0x98),
        }
    }
}

#[derive(Clone)]
struct SessionInfo {
    user: String,
    workspace: String,
}

impl SessionInfo {
    fn guest() -> Self {
        Self {
            user: "guest".to_string(),
            workspace: "ruin".to_string(),
        }
    }
}

#[derive(Clone)]
struct Notifications {
    last_message: Signal<String>,
}

impl Notifications {
    fn new(last_message: Signal<String>) -> Self {
        Self { last_message }
    }

    fn info(&self, message: impl Into<String>) {
        self.last_message.replace(message.into());
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn inspector_panel_text_reaches_the_scene() {
        let root = WorkspaceRoot::builder().children(());
        let view = ruin_app::__render_mountable_for_test(&root);
        let snapshot = ruin_ui::layout_snapshot(1, UiSize::new(1280.0, 840.0), view.element());

        assert!(
            snapshot.scene.items.iter().any(|item| matches!(
                item,
                ruin_ui::DisplayItem::Text(text)
                    if text.text.contains("Nested override")
                        || text.text.contains("Inspector panel")
            )),
            "expected InspectorPanel text to appear in the rendered scene",
        );
    }
}
