// SPDX-FileCopyrightText: 2026 Stan Grams <sjg@haxx.space>
//
// SPDX-License-Identifier: GPL-2.0-or-later

use std::sync::OnceLock;

const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
const CLIENT_BUILD_DATE: &str = env!("TRX_CLIENT_BUILD_DATE");

const INDEX_HTML: &str = include_str!("../assets/web/index.html");
pub const STYLE_CSS: &str = include_str!("../assets/web/style.css");
pub const THEMES_CSS: &str = include_str!("../assets/web/themes.css");
pub const BANDPLAN_JSON: &str = include_str!("../assets/web/bandplan.json");

include!(concat!(env!("OUT_DIR"), "/generated_assets.rs"));

pub fn generated_asset(name: &str) -> Option<&'static [u8]> {
    GENERATED_ASSETS
        .iter()
        .find_map(|(candidate, bytes)| (*candidate == name).then_some(*bytes))
}

// Vendored DSEG14 Classic font
pub const DSEG14_CLASSIC_WOFF2: &[u8] =
    include_bytes!("../assets/web/vendor/dseg14-classic-latin-400-normal.woff2");

// Vendored opus-decoder 0.7.11 browser build (WebAssembly embedded in JS)
pub const OPUS_DECODER_JS: &str = include_str!("../assets/web/vendor/opus-decoder-0.7.11.min.js");

// Vendored Leaflet 1.9.4
pub const LEAFLET_JS: &str = include_str!("../assets/web/vendor/leaflet.js");
pub const LEAFLET_CSS: &str = include_str!("../assets/web/vendor/leaflet.css");
pub const LEAFLET_MARKER_ICON: &[u8] = include_bytes!("../assets/web/vendor/marker-icon.png");
pub const LEAFLET_MARKER_ICON_2X: &[u8] = include_bytes!("../assets/web/vendor/marker-icon-2x.png");
pub const LEAFLET_MARKER_SHADOW: &[u8] = include_bytes!("../assets/web/vendor/marker-shadow.png");
pub const LEAFLET_LAYERS: &[u8] = include_bytes!("../assets/web/vendor/layers.png");
pub const LEAFLET_LAYERS_2X: &[u8] = include_bytes!("../assets/web/vendor/layers-2x.png");

// Vendored APRS symbol sprites (https://github.com/hessu/aprs-symbols).
// Each sheet is a 16x6 grid of 24px cells indexed by `symbol code - 0x21`:
// table 0 is the primary ('/') set, table 1 the alternate ('\') set, and
// table 2 the overlay characters drawn on top of an alternate symbol.
pub const APRS_SYMBOLS_PRIMARY: &[u8] =
    include_bytes!("../assets/web/vendor/aprs-symbols-24-0.png");
pub const APRS_SYMBOLS_PRIMARY_2X: &[u8] =
    include_bytes!("../assets/web/vendor/aprs-symbols-24-0-2x.png");
pub const APRS_SYMBOLS_ALTERNATE: &[u8] =
    include_bytes!("../assets/web/vendor/aprs-symbols-24-1.png");
pub const APRS_SYMBOLS_ALTERNATE_2X: &[u8] =
    include_bytes!("../assets/web/vendor/aprs-symbols-24-1-2x.png");
pub const APRS_SYMBOLS_OVERLAY: &[u8] =
    include_bytes!("../assets/web/vendor/aprs-symbols-24-2.png");
pub const APRS_SYMBOLS_OVERLAY_2X: &[u8] =
    include_bytes!("../assets/web/vendor/aprs-symbols-24-2-2x.png");

/// Build version tag used for cache-busting asset URLs and ETag headers.
/// Computed once from `PKG_VERSION` + `CLIENT_BUILD_DATE`.
pub fn build_version_tag() -> &'static str {
    static TAG: OnceLock<String> = OnceLock::new();
    TAG.get_or_init(|| format!("{PKG_VERSION}-{CLIENT_BUILD_DATE}"))
}

/// Pre-computed index HTML with version/date placeholders resolved.
/// Computed once on first access, avoiding three `.replace()` calls per
/// request on the ~50 KB HTML template.
pub fn index_html() -> &'static str {
    static HTML: OnceLock<String> = OnceLock::new();
    HTML.get_or_init(|| {
        INDEX_HTML
            .replace("{pkg}", PKG_NAME)
            .replace("{ver}", PKG_VERSION)
            .replace("{client_build_date}", CLIENT_BUILD_DATE)
    })
}

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

    #[test]
    fn typed_application_bundle_contains_shared_runtime() {
        let html = index_html();
        assert_eq!(html.matches("/app.js").count(), 1);
        assert!(!html.contains("/ui-core.js"));
        assert!(!html.contains("/plugin-runtime.js"));
        assert!(!html.contains("/plugin-loader.js"));
        assert!(generated_asset("app.js").is_some_and(|asset| {
            std::str::from_utf8(asset).is_ok_and(|script| {
                script.contains("trxUi")
                    && script.contains("registerDecoder")
                    && script.contains("TrxAisTrackSymbol")
            })
        }));
        assert!(html.contains("<summary>Scheduler controls</summary>"));
        assert!(html.contains("<summary>Audio controls</summary>"));
    }
}
