use dioxus::prelude::*;

mod mesh;
mod viewer;

// Frontend compile-time configuration, generated by build.rs from POLYMODEL_*.
mod env;
// Server-only indexing infrastructure (Hydrant firehose + SQLite projection).
#[cfg(feature = "server")]
mod indexing;

use viewer::{ViewerPage, demo_model_for_route, demo_models};

const FAVICON: Asset = asset!("/assets/favicon.jpg");
const THEME_CSS: Asset = asset!("/assets/styling/theme.css");
const BASE_CSS: Asset = asset!("/assets/styling/base.css");
const CARDS_CSS: Asset = asset!("/assets/styling/cards.css");
const BROWSE_CSS: Asset = asset!("/assets/styling/browse.css");
const POLYMODEL_CSS: Asset = asset!("/assets/styling/polymodel.css");

#[cfg(feature = "server")]
fn main() {
    // The closure runs inside the server's Tokio runtime at startup, so the
    // indexing tasks spawned here live for the process lifetime alongside Axum.
    dioxus::serve(|| async {
        // Server runtime config: load .env, then read DATABASE_URL (and any
        // future server env) in one place. Hydrant reads its own HYDRANT_*.
        let _ = dotenvy::dotenv();
        let _ = tracing_subscriber::fmt()
            .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
            .try_init();

        let cfg = indexing::config::ServerConfig::load();
        let db = indexing::db::init_db(&cfg)
            .await
            .expect("failed to initialize sqlite projection database");
        indexing::start_indexing(db)
            .await
            .expect("failed to start indexing pipeline");

        Ok(dioxus::server::router(App))
    });
}

#[cfg(not(feature = "server"))]
fn main() {
    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
    {
        console_error_panic_hook::set_once();
        tracing_wasm::set_as_global_default();
    }

    dioxus::launch(App);
}

#[derive(Clone, Debug, PartialEq, Routable)]
enum Route {
    #[route("/")]
    Browse {},
    #[route("/viewer/:model_id")]
    Viewer { model_id: String },
}

#[component]
fn App() -> Element {
    rsx! {
        document::Meta { name: "viewport", content: "width=device-width, initial-scale=1" }
        document::Title { "Polymodel" }
        document::Link { rel: "icon", href: FAVICON }
        document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" }
        document::Link { rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: "anonymous" }
        document::Link { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Capriola&family=DM+Sans:opsz,wght@9..40,300..800&display=swap" }
        Stylesheet { href: THEME_CSS }
        Stylesheet { href: BASE_CSS }
        Stylesheet { href: CARDS_CSS }
        Stylesheet { href: BROWSE_CSS }
        Stylesheet { href: POLYMODEL_CSS }

        Router::<Route> {}
    }
}

#[component]
fn Browse() -> Element {
    rsx! {
        main { class: "browse-page",
            header { class: "browse-header",
                h1 { "Polymodel" }
                div { class: "demo-context",
                    strong { "Thingiverse on atproto. " }
                    "Project/profile/like lexicons, social feeds, and a browser STL viewer. "
                    strong { "Workflow: " }
                    "Jira + Confluence + jj + Polytoken."
                }
                Link {
                    class: "viewer-hero-link",
                    to: Route::Viewer { model_id: demo_models()[0].id.to_string() },
                    "Open viewer"
                }
            }
            section { class: "browse-grid",
                for card in roadmap_cards() {
                    RoadmapCard { card }
                }
            }
        }
    }
}

#[component]
fn Viewer(model_id: String) -> Element {
    let model = demo_model_for_route(&model_id);

    rsx! {
        main { class: "viewer-route-page",
            nav { class: "viewer-route-nav", aria_label: "Viewer navigation",
                Link { to: Route::Browse {}, "← Back to browse" }
            }
            ViewerPage { key: "{model.id}", initial_model: model }
        }
    }
}

#[derive(Clone, Copy, PartialEq)]
struct RoadmapCardData {
    token: &'static str,
    title: &'static str,
    body: &'static str,
    viewer_link: Option<&'static str>,
}

fn roadmap_cards() -> [RoadmapCardData; 6] {
    [
        RoadmapCardData {
            token: "NSID",
            title: "Project lexicon",
            body: "Define the model/project record: title, description, files, preview assets, tags, license, and attribution.",
            viewer_link: None,
        },
        RoadmapCardData {
            token: "DID",
            title: "Profile publishing",
            body: "Use ATProto identity and profile-oriented feeds so makers can publish and browse from their own repos.",
            viewer_link: None,
        },
        RoadmapCardData {
            token: "★",
            title: "Likes and saves",
            body: "Start with lightweight feedback records that can drive hot/recent discovery without inventing a heavyweight backend.",
            viewer_link: None,
        },
        RoadmapCardData {
            token: "STL",
            title: "Model viewer",
            body: "Spike three-d and kiss3d as WASM viewer candidates; choose based on STL loading, orbit controls, rendering quality, and Dioxus integration friction.",
            viewer_link: Some("body-f-chest-v4"),
        },
        RoadmapCardData {
            token: "PM",
            title: "Jira and Confluence",
            body: "Use Jira and Confluence as both planning/demo narrative and agent-readable workflow state.",
            viewer_link: None,
        },
        RoadmapCardData {
            token: "pt",
            title: "Polytoken workflow",
            body: "Use facets, skills, hooks, project vars, jj workspaces, and review gates to make the demo workflow real.",
            viewer_link: None,
        },
    ]
}

#[component]
fn RoadmapCard(card: RoadmapCardData) -> Element {
    rsx! {
        article { class: "video-card card-surface demo-card",
            div { class: "video-card-thumbnail",
                span { class: "demo-card-token", "{card.token}" }
            }
            div { class: "video-card-body demo-card-body",
                h2 { class: "card-title", "{card.title}" }
                p { "{card.body}" }
                if let Some(model_id) = card.viewer_link {
                    Link {
                        class: "demo-card-action",
                        to: Route::Viewer { model_id: model_id.to_string() },
                        "Open viewer"
                    }
                }
            }
        }
    }
}

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

    #[test]
    fn roadmap_includes_stl_viewer() {
        assert!(roadmap_cards().iter().any(|card| card.token == "STL"));
    }

    #[test]
    fn roadmap_links_to_viewer() {
        assert!(
            roadmap_cards()
                .iter()
                .any(|card| card.viewer_link.is_some())
        );
    }
}
