#!/usr/bin/env -S uv run --script
# /// script
# dependencies = [
#   "nicegui==3.16.0",
# ]
# ///

"""Demonstrate URL-backed tabs with persistent parameterized-route state."""

from __future__ import annotations

from collections.abc import Callable
from urllib.parse import urlsplit

from nicegui import binding
from nicegui import events
from nicegui import ui
from nicegui.elements.tabs import Tab
from nicegui.elements.tabs import TabPanel

type PageBuilder = Callable[..., None]

DEFAULT_REPORT_PATH = "/reports/a"
REPORTS_TAB = "reports"
TAB_ROUTES = frozenset({"/", "/projects", "/settings"})


def page_heading(title: str, description: str) -> None:
    """Render a shared heading for sub-page content."""
    with ui.column().classes("w-full gap-1"):
        ui.label(title).classes("text-3xl font-semibold text-stone-900")
        ui.label(description).classes("text-base text-stone-600")


def overview_page() -> None:
    """Render the overview sub-page."""
    with ui.column().classes("w-full max-w-6xl mx-auto gap-6 px-4 py-8"):
        page_heading("Overview", "A quick read on the workspace today.")

        metrics = (
            ("Active projects", "8", "folder_open", "primary"),
            ("Tasks completed", "24", "task_alt", "positive"),
            ("Needs attention", "3", "error_outline", "warning"),
        )
        with ui.grid().classes("w-full grid-cols-1 gap-4 md:grid-cols-3"):
            for label, value, icon, color in metrics:
                with ui.card().classes("w-full p-5 gap-3"):
                    with ui.row().classes("w-full items-center justify-between"):
                        ui.label(label).classes("text-sm font-medium text-stone-600")
                        ui.icon(icon, color=color).classes("text-2xl")
                    ui.label(value).classes("text-3xl font-semibold text-stone-900")


def projects_page() -> None:
    """Render the projects sub-page."""
    with ui.column().classes("w-full max-w-6xl mx-auto gap-6 px-4 py-8"):
        page_heading("Projects", "Each route builds its own content inside the shared shell.")

        with ui.list().props("bordered separator").classes("w-full bg-white rounded"):
            for name, status, color in (
                ("Client portal", "On track", "positive"),
                ("Mobile refresh", "In review", "primary"),
                ("Data migration", "Blocked", "negative"),
            ):
                with ui.item():
                    with ui.item_section().props("avatar"):
                        ui.icon("folder", color=color)
                    with ui.item_section():
                        ui.item_label(name)
                        ui.item_label(status).props("caption")
                    with ui.item_section().props("side"):
                        ui.badge(status, color=color)


def report_page(state: NavigationState) -> None:
    """Render report content bound to the active route parameter."""
    with ui.column().classes("w-full max-w-6xl mx-auto gap-6 px-4 py-8"):
        with ui.column().classes("w-full gap-1"):
            ui.label().bind_text_from(
                state,
                "active_report_path",
                backward=lambda path: f"Report {report_id_from_path(path).upper()}",
            ).classes("text-3xl font-semibold text-stone-900")
            ui.label("The report ID is injected from the URL path.").classes("text-base text-stone-600")

        with ui.card().classes("w-full max-w-2xl p-5 gap-4"):
            ui.label("Parameterized route").classes("text-xl font-semibold text-stone-900")
            ui.label().bind_text_from(
                state,
                "active_report_path",
                backward=lambda path: f"Loaded {path}",
            ).classes("text-stone-600")
            with ui.row().classes("gap-2"):
                ui.button("Report A", on_click=lambda: ui.navigate.to("/reports/a")).props("outline")
                ui.button("Report B", on_click=lambda: ui.navigate.to("/reports/b")).props("outline")


def settings_page() -> None:
    """Render the settings sub-page."""
    with ui.column().classes("w-full max-w-6xl mx-auto gap-6 px-4 py-8"):
        page_heading("Settings", "Controls here are recreated when this sub-page is opened.")

        with ui.card().classes("w-full max-w-2xl p-5 gap-4"):
            ui.label("Notifications").classes("text-xl font-semibold text-stone-900")
            ui.switch("Weekly summary", value=True)
            ui.switch("Project status changes", value=True)
            ui.switch("Product announcements", value=False)


def normalize_route(path: str) -> str:
    """Extract and normalize the path portion of a route."""
    return urlsplit(path).path.rstrip("/") or "/"


def tab_name_for_route(route: str) -> str:
    """Return the tab name associated with a concrete route."""
    if route.startswith("/reports/"):
        return REPORTS_TAB
    return route if route in TAB_ROUTES else "/"


@binding.bindable_dataclass
class NavigationState:
    """Store client-local navigation state for parameterized tabs."""

    active_report_path: str = DEFAULT_REPORT_PATH


type TabHandler = events.ValueChangeEventArguments[str | Tab | TabPanel | None]


def report_id_from_path(path: str) -> str:
    """Extract the report ID from a normalized report route."""
    return path.rsplit("/", maxsplit=1)[-1]


def create_tabs(state: NavigationState, initial_route: str) -> ui.tabs:
    """Create route-aware tabs and retain the last selected report."""

    def navigate(event: TabHandler) -> None:
        """Navigate to the route represented by the selected tab."""
        match event.value:
            case str(tabname):
                destination = state.active_report_path if tabname == REPORTS_TAB else tabname
                ui.navigate.to(destination)
            case _:
                return

    with ui.column().classes("mx-auto"), ui.tabs() as tabs:
        ui.tab("/", label="Overview", icon="space_dashboard")
        ui.tab("/projects", label="Projects", icon="folder_open")
        ui.tab(REPORTS_TAB, label="Reports", icon="summarize")
        ui.tab("/settings", label="Settings", icon="settings")

    tabs.set_value(tab_name_for_route(initial_route))
    tabs.on_value_change(navigate)

    return tabs


def render_tab_panels(tabs: ui.tabs, state: NavigationState, active_tab: str) -> None:
    """Render all tabbed page content inside a tab panels container."""
    with ui.tab_panels(tabs, value=active_tab, animated=True).classes("w-full"):
        with ui.tab_panel("/"):
            overview_page()
        with ui.tab_panel("/projects"):
            projects_page()
        with ui.tab_panel(REPORTS_TAB):
            report_page(state)
        with ui.tab_panel("/settings"):
            settings_page()


def root() -> None:
    """Build the persistent application shell and sub-page container."""
    initial_route = normalize_route(ui.context.client.sub_pages_router.current_path)
    state = NavigationState()
    if tab_name_for_route(initial_route) == REPORTS_TAB:
        state.active_report_path = initial_route

    with ui.header(elevated=True).classes("py-0 items-center"):
        tabs = create_tabs(state, initial_route)
        ui.button(icon="settings").classes("text-white").props("round flat").tooltip("Settings")

    render_tab_panels(tabs, state, tab_name_for_route(initial_route))

    def route_overview() -> None:
        tabs.set_value("/")

    def route_projects() -> None:
        tabs.set_value("/projects")

    def route_reports(report_id: str) -> None:
        state.active_report_path = f"/reports/{report_id}"
        tabs.set_value(REPORTS_TAB)

    def route_settings() -> None:
        tabs.set_value("/settings")

    routes: dict[str, PageBuilder] = {
        "/": route_overview,
        "/projects": route_projects,
        "/reports/{report_id}": route_reports,
        "/settings": route_settings,
    }
    ui.sub_pages(routes).classes("hidden")


if __name__ in {"__main__", "__mp_main__"}:
    ui.run(root, title="Northstar", port=8888, reload=True)
