#![deny(warnings)]

use std::env;
use std::fs;
use std::path::PathBuf;

struct ProviderDef {
    id: &'static str,
    module: &'static str,
    ty: &'static str,
}

const PROVIDERS: &[ProviderDef] = &[
    ProviderDef {
        id: "pornxp",
        module: "pornxp",
        ty: "PornxpProvider",
    },
    ProviderDef {
        id: "animeidhentai",
        module: "animeidhentai",
        ty: "AnimeidhentaiProvider",
    },
    ProviderDef {
        id: "all",
        module: "all",
        ty: "AllProvider",
    },
    ProviderDef {
        id: "perverzija",
        module: "perverzija",
        ty: "PerverzijaProvider",
    },
    ProviderDef {
        id: "hanime",
        module: "hanime",
        ty: "HanimeProvider",
    },
    ProviderDef {
        id: "pornhub",
        module: "pornhub",
        ty: "PornhubProvider",
    },
    ProviderDef {
        id: "ph-shorties",
        module: "pornhub_shorties",
        ty: "PornhubShortiesProvider",
    },
    ProviderDef {
        id: "tikporn",
        module: "tikporn",
        ty: "TikpornProvider",
    },
    ProviderDef {
        id: "youporn",
        module: "youporn",
        ty: "YoupornProvider",
    },
    ProviderDef {
        id: "pornhd3x",
        module: "pornhd3x",
        ty: "Pornhd3xProvider",
    },
    ProviderDef {
        id: "spankbang",
        module: "spankbang",
        ty: "SpankbangProvider",
    },
    ProviderDef {
        id: "rule34video",
        module: "rule34video",
        ty: "Rule34videoProvider",
    },
    ProviderDef {
        id: "redtube",
        module: "redtube",
        ty: "RedtubeProvider",
    },
    ProviderDef {
        id: "okporn",
        module: "okporn",
        ty: "OkpornProvider",
    },
    ProviderDef {
        id: "pornhat",
        module: "pornhat",
        ty: "PornhatProvider",
    },
    ProviderDef {
        id: "perfectgirls",
        module: "perfectgirls",
        ty: "PerfectgirlsProvider",
    },
    ProviderDef {
        id: "okxxx",
        module: "okxxx",
        ty: "OkxxxProvider",
    },
    ProviderDef {
        id: "homoxxx",
        module: "homoxxx",
        ty: "HomoxxxProvider",
    },
    ProviderDef {
        id: "missav",
        module: "missav",
        ty: "MissavProvider",
    },
    ProviderDef {
        id: "xxthots",
        module: "xxthots",
        ty: "XxthotsProvider",
    },
    ProviderDef {
        id: "yesporn",
        module: "yesporn",
        ty: "YespornProvider",
    },
    ProviderDef {
        id: "porntrex",
        module: "porntrex",
        ty: "PorntrexProvider",
    },
    ProviderDef {
        id: "sxyprn",
        module: "sxyprn",
        ty: "SxyprnProvider",
    },
    ProviderDef {
        id: "porn00",
        module: "porn00",
        ty: "Porn00Provider",
    },
    ProviderDef {
        id: "youjizz",
        module: "youjizz",
        ty: "YoujizzProvider",
    },
    ProviderDef {
        id: "paradisehill",
        module: "paradisehill",
        ty: "ParadisehillProvider",
    },
    ProviderDef {
        id: "porn4fans",
        module: "porn4fans",
        ty: "Porn4fansProvider",
    },
    ProviderDef {
        id: "pornmz",
        module: "pornmz",
        ty: "PornmzProvider",
    },
    ProviderDef {
        id: "porndish",
        module: "porndish",
        ty: "PorndishProvider",
    },
    ProviderDef {
        id: "shooshtime",
        module: "shooshtime",
        ty: "ShooshtimeProvider",
    },
    ProviderDef {
        id: "pornzog",
        module: "pornzog",
        ty: "PornzogProvider",
    },
    ProviderDef {
        id: "omgxxx",
        module: "omgxxx",
        ty: "OmgxxxProvider",
    },
    ProviderDef {
        id: "beeg",
        module: "beeg",
        ty: "BeegProvider",
    },
    ProviderDef {
        id: "tnaflix",
        module: "tnaflix",
        ty: "TnaflixProvider",
    },
    ProviderDef {
        id: "tokyomotion",
        module: "tokyomotion",
        ty: "TokyomotionProvider",
    },
    ProviderDef {
        id: "viralxxxporn",
        module: "viralxxxporn",
        ty: "ViralxxxpornProvider",
    },
    ProviderDef {
        id: "vrporn",
        module: "vrporn",
        ty: "VrpornProvider",
    },
    ProviderDef {
        id: "rule34gen",
        module: "rule34gen",
        ty: "Rule34genProvider",
    },
    ProviderDef {
        id: "xxdbx",
        module: "xxdbx",
        ty: "XxdbxProvider",
    },
    ProviderDef {
        id: "xfree",
        module: "xfree",
        ty: "XfreeProvider",
    },
    ProviderDef {
        id: "hqporner",
        module: "hqporner",
        ty: "HqpornerProvider",
    },
    ProviderDef {
        id: "pmvhaven",
        module: "pmvhaven",
        ty: "PmvhavenProvider",
    },
    ProviderDef {
        id: "noodlemagazine",
        module: "noodlemagazine",
        ty: "NoodlemagazineProvider",
    },
    ProviderDef {
        id: "pimpbunny",
        module: "pimpbunny",
        ty: "PimpbunnyProvider",
    },
    ProviderDef {
        id: "javtiful",
        module: "javtiful",
        ty: "JavtifulProvider",
    },
    ProviderDef {
        id: "supjav",
        module: "supjav",
        ty: "SupjavProvider",
    },
    ProviderDef {
        id: "vjav",
        module: "vjav",
        ty: "VjavProvider",
    },
    ProviderDef {
        id: "hypnotube",
        module: "hypnotube",
        ty: "HypnotubeProvider",
    },
    ProviderDef {
        id: "freepornvideosxxx",
        module: "freepornvideosxxx",
        ty: "FreepornvideosxxxProvider",
    },
    ProviderDef {
        id: "freeuseporn",
        module: "freeuseporn",
        ty: "FreeusepornProvider",
    },
    ProviderDef {
        id: "heavyfetish",
        module: "heavyfetish",
        ty: "HeavyfetishProvider",
    },
    ProviderDef {
        id: "hsex",
        module: "hsex",
        ty: "HsexProvider",
    },
    ProviderDef {
        id: "blowjobspro",
        module: "blowjobspro",
        ty: "BlowjobsproProvider",
    },
    ProviderDef {
        id: "erome",
        module: "erome",
        ty: "EromeProvider",
    },
    ProviderDef {
        id: "sextb",
        module: "sextb",
        ty: "SextbProvider",
    },
    ProviderDef {
        id: "hentaihaven",
        module: "hentaihaven",
        ty: "HentaihavenProvider",
    },
    ProviderDef {
        id: "fikfap",
        module: "fikfap",
        ty: "FikfapProvider",
    },
    ProviderDef {
        id: "chaturbate",
        module: "chaturbate",
        ty: "ChaturbateProvider",
    },
    ProviderDef {
        id: "clapdat",
        module: "clapdat",
        ty: "ClapdatProvider",
    },
    ProviderDef {
        id: "archivebate",
        module: "archivebate",
        ty: "ArchivebateProvider",
    },
    ProviderDef {
        id: "thaiporntv",
        module: "thaiporntv",
        ty: "ThaipornTvProvider",
    },
    ProviderDef {
        id: "xlecx",
        module: "xlecx",
        ty: "XlecxProvider",
    },
    ProviderDef {
        id: "aps",
        module: "allpornstream",
        ty: "AllPornStreamProvider",
    },
    ProviderDef {
        id: "melonstube",
        module: "melonstube",
        ty: "MelonstubeProvider",
    },
    ProviderDef {
        id: "tube8",
        module: "tube8",
        ty: "Tube8Provider",
    },
    ProviderDef {
        id: "jable",
        module: "jable",
        ty: "JableProvider",
    },
    ProviderDef {
        id: "fullporner",
        module: "fullporner",
        ty: "FullpornerProvider",
    },
    ProviderDef {
        id: "thepornbunny",
        module: "thepornbunny",
        ty: "ThepornbunnyProvider",
    },
    ProviderDef {
        id: "eporner",
        module: "eporner",
        ty: "EpornerProvider",
    },
    ProviderDef {
        id: "xnxx",
        module: "xnxx",
        ty: "XnxxProvider",
    },
    ProviderDef {
        id: "xhamster",
        module: "xhamster",
        ty: "XhamsterProvider",
    },
    ProviderDef {
        id: "xgroovy",
        module: "xgroovy",
        ty: "XgroovyProvider",
    },
    ProviderDef {
        id: "xvideos",
        module: "xvideos",
        ty: "XvideosProvider",
    },
    ProviderDef {
        id: "wowxxx",
        module: "wowxxx",
        ty: "WowxxxProvider",
    },
    ProviderDef {
        id: "camsoda",
        module: "camsoda",
        ty: "CamsodaProvider",
    },
    ProviderDef {
        id: "fyptt",
        module: "fyptt",
        ty: "FypttProvider",
    },
    ProviderDef {
        id: "hentaitv",
        module: "hentaitv",
        ty: "HentaitvProvider",
    },
    ProviderDef {
        id: "xxxtik",
        module: "xxxtik",
        ty: "XxxtikProvider",
    },
    ProviderDef {
        id: "kwiky",
        module: "kwiky",
        ty: "KwikyProvider",
    },
    ProviderDef {
        id: "hotbunny",
        module: "hotbunny",
        ty: "HotbunnyProvider",
    },
    ProviderDef {
        id: "redgifs",
        module: "redgifs",
        ty: "RedgifsProvider",
    },
];

fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-env-changed=HOT_TUB_PROVIDER");
    println!("cargo:rerun-if-env-changed=HOTTUB_PROVIDER");
    println!("cargo:rustc-check-cfg=cfg(hottub_single_provider)");
    let provider_cfg_values = PROVIDERS
        .iter()
        .map(|provider| format!("\"{}\"", provider.id))
        .collect::<Vec<_>>()
        .join(", ");
    println!("cargo:rustc-check-cfg=cfg(hottub_provider, values({provider_cfg_values}))");

    let selected = env::var("HOT_TUB_PROVIDER")
        .or_else(|_| env::var("HOTTUB_PROVIDER"))
        .ok()
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty());

    let providers = match selected.as_deref() {
        Some(selected_id) => {
            let provider = PROVIDERS
                .iter()
                .find(|provider| provider.id == selected_id)
                .unwrap_or_else(|| {
                    panic!("Unknown provider `{selected_id}` from HOT_TUB_PROVIDER/HOTTUB_PROVIDER")
                });
            println!("cargo:rustc-cfg=hottub_single_provider");
            println!("cargo:rustc-cfg=hottub_provider=\"{selected_id}\"");
            vec![provider]
        }
        None => PROVIDERS.iter().collect(),
    };

    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR"));
    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));

    let modules = providers
        .iter()
        .map(|provider| {
            let module_path = manifest_dir
                .join("src/providers")
                .join(format!("{}.rs", provider.module));
            format!(
                "#[path = r#\"{}\"#]\npub mod {};",
                module_path.display(),
                provider.module
            )
        })
        .collect::<Vec<_>>()
        .join("\n");
    fs::write(out_dir.join("provider_modules.rs"), format!("{modules}\n"))
        .expect("write provider_modules.rs");

    let registry = providers
        .iter()
        .map(|provider| {
            format!(
                "m.insert(\"{id}\", Arc::new({module}::{ty}::new()) as DynProvider);",
                id = provider.id,
                module = provider.module,
                ty = provider.ty
            )
        })
        .collect::<Vec<_>>()
        .join("\n");
    fs::write(
        out_dir.join("provider_registry.rs"),
        format!("{{\n{registry}\n}}\n"),
    )
    .expect("write provider_registry.rs");

    let metadata_arms = providers
        .iter()
        .map(|provider| {
            if provider.id == "all" {
                format!(
                    "\"all\" | \"hottub\" => Some({module}::CHANNEL_METADATA),",
                    module = provider.module
                )
            } else {
                format!(
                    "\"{id}\" => Some({module}::CHANNEL_METADATA),",
                    id = provider.id,
                    module = provider.module
                )
            }
        })
        .collect::<Vec<_>>()
        .join("\n");
    fs::write(
        out_dir.join("provider_metadata_fn.rs"),
        format!("match id {{\n{metadata_arms}\n_ => None,\n}}\n"),
    )
    .expect("write provider_metadata_fn.rs");

    let selection = match selected.as_deref() {
        Some(selected_id) => format!(
            "pub const COMPILE_TIME_SELECTED_PROVIDER: Option<&str> = Some(\"{selected_id}\");",
        ),
        None => "pub const COMPILE_TIME_SELECTED_PROVIDER: Option<&str> = None;".to_string(),
    };
    fs::write(
        out_dir.join("provider_selection.rs"),
        format!("{selection}\n"),
    )
    .expect("write provider_selection.rs");
}
