//! The daemon's site registration list. Lives in the library (not `main.rs`) so
//! tests can assert which sites are descriptor-backed.

use std::sync::LazyLock;

use crate::driver::{LaunchPolicy, SiteEntry};

/// All sites this daemon serves, built once and cached. A single
/// `tfd <site> <cmd>` invocation reads this ~3× (clap build, route check,
/// dispatch), so returning the cached slice avoids rebuilding ~25 `SiteEntry`
/// objects each time. Order is irrelevant (keyed by `Site::id()`).
pub fn all_site_entries() -> &'static [SiteEntry] {
    static SITE_ENTRIES: LazyLock<Vec<SiteEntry>> = LazyLock::new(build_site_entries);
    &SITE_ENTRIES
}

fn build_site_entries() -> Vec<SiteEntry> {
    vec![
        // sa is dual-mode (Transport::Either): the daemon serves it over HTTP
        // (reads ~/.tail-fin/sa-cookies.txt, no Chrome) via from_http_module —
        // its primary mode. The browser path (--connect) stays in-process
        // (route_decision forces Route::Local for --connect/--cookies).
        SiteEntry::from_http_module::<tail_fin_sa::command_module::SaModule>(
            tail_fin_sa::SeekingAlphaSite,
        ),
        SiteEntry::from_module::<tail_fin_twitter::command_module::TwitterModule>(
            tail_fin_twitter::TwitterSite,
        ),
        SiteEntry::from_module::<tail_fin_grok::command_module::GrokModule>(
            tail_fin_grok::GrokSite,
        )
        .with_launch_policy(LaunchPolicy::AttachOnly {
            url_fragment: "grok.com",
        }),
        SiteEntry::from_module::<tail_fin_reddit::command_module::RedditModule>(
            tail_fin_reddit::RedditSite,
        ),
        SiteEntry::from_module::<tail_fin_instagram::command_module::InstagramModule>(
            tail_fin_instagram::InstagramSite,
        ),
        SiteEntry::from_module::<tail_fin_xhs::command_module::XhsModule>(tail_fin_xhs::XhsSite),
        SiteEntry::from_module::<tail_fin_coupang::command_module::CoupangModule>(
            tail_fin_coupang::CoupangSite,
        ),
        // Shopee — single region-agnostic descriptor entry (was 8 per-region
        // entries). Region is a runtime `--region` flag; the daemon
        // serves the browser path (features=["browser"]). Registry/parity-only:
        // CLI routing is local_attach_host-pinned, so no `tfd shopee` dispatch.
        SiteEntry::from_module::<tail_fin_shopee::command_module::ShopeeModule>(
            tail_fin_shopee::ShopeeSite,
        ),
        SiteEntry::from_module::<tail_fin_spotify::command_module::SpotifyModule>(
            tail_fin_spotify::SpotifySite,
        ),
        SiteEntry::from_module::<tail_fin_youtube::command_module::YoutubeModule>(
            tail_fin_youtube::YoutubeSite,
        ),
        SiteEntry::from_module::<tail_fin_bloomberg::command_module::BloombergModule>(
            tail_fin_bloomberg::BloombergSite,
        ),
        SiteEntry::from_http_module::<tail_fin_591::command_module::FiveNineOneModule>(
            tail_fin_591::FiveNineOneSite,
        ),
        SiteEntry::from_module::<tail_fin_pcc::command_module::PccModule>(tail_fin_pcc::PccSite),
        // gmaps — pure-browser CDP capture, daemon-served headless (launch_url ==
        // refresh_url == https://www.google.com/maps). Auto-routes from the CLI.
        SiteEntry::from_module::<tail_fin_gmaps::command_module::GMapsModule>(
            tail_fin_gmaps::GMapsSite,
        ),
        SiteEntry::from_http_module::<tail_fin_tradingview::command_module::TradingViewModule>(
            tail_fin_tradingview::TradingViewSite,
        ),
        SiteEntry::from_http_module::<tail_fin_arkham::command_module::ArkhamModule>(
            tail_fin_arkham::ArkhamSite,
        ),
        SiteEntry::from_http_module::<tail_fin_alphora::command_module::AlphoraModule>(
            tail_fin_alphora::AlphoraSite,
        ),
        SiteEntry::from_http_module::<tail_fin_gemini::command_module::GeminiModule>(
            tail_fin_gemini::GeminiSite,
        ),
        SiteEntry::from_http_module::<tail_fin_nansen::command_module::NansenModule>(
            tail_fin_nansen::NansenSite,
        ),
        SiteEntry::from_http_module::<tail_fin_macromicro::command_module::MacroMicroModule>(
            tail_fin_macromicro::MacroMicroSite,
        ),
    ]
}

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

    #[test]
    fn all_site_entries_is_cached_not_rebuilt() {
        let a = all_site_entries();
        let b = all_site_entries();
        // The same backing allocation each call — proves it's the cached slice,
        // not a freshly-built Vec (which a `tfd <site> <cmd>` reads ~3×).
        assert!(
            std::ptr::eq(a.as_ptr(), b.as_ptr()),
            "all_site_entries must return the cached slice, not rebuild"
        );
        assert!(!a.is_empty());
    }

    #[test]
    fn grok_entry_uses_attach_only_launch_policy() {
        let grok = all_site_entries()
            .iter()
            .find(|entry| entry.site.id() == "grok")
            .expect("grok entry is registered");

        assert_eq!(
            grok.launch_policy(),
            LaunchPolicy::AttachOnly {
                url_fragment: "grok.com"
            }
        );
    }
}
