use std::{env, fs, path::PathBuf};

const ROOT_CA_ENV: &str = "DHTTP_ROOT_CA";
const STUN_SERVER_ENV: &str = "DHTTP_STUN_SERVER";

const DEFAULT_STUN_SERVER: &str = "stun.dhttp.example.net";
const DEFAULT_ROOT_CA_PEM: &str = "\
-----BEGIN CERTIFICATE-----\n\
MIIDKTCCAhGgAwIBAgIUHNScq6R2U5QYUzxkEkNDaOJt4yMwDQYJKoZIhvcNAQEL\n\
BQAwHDEaMBgGA1UEAwwRZGh0dHAuZXhhbXBsZS5uZXQwHhcNMjYwNjA0MTE0NjI1\n\
WhcNMzYwNjAxMTE0NjI1WjAcMRowGAYDVQQDDBFkaHR0cC5leGFtcGxlLm5ldDCC\n\
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANCHw/okfV4RDIN5ws0IMGi1\n\
c1TTQAklaBynw8neIM+ZniGLOluRLvYATY4xla/ayjYdzXmel+4hdaG5pN6Ggdgm\n\
PhIj9hpsNKOjjkBHtdnDg/Dfc/L+ElRDPMNosvVN2L/G4yDBso5SGJUlFE7Fww3x\n\
izrTCCbR4lofwZAnUZAtvnX3KGgvgRtg0VdgvpWJ1JOnwQYm1qU6ljFG+72418SW\n\
Htn2A8SRGSO1Im1W1QMY4OJtAVwfi/XwwN9ifWWPwcl4OGV/MXBLUQRF4gzgFC+1\n\
8EG5jKCZtoJcY3FY+LF1DMkdbmE6TOddycev0HWNBPQtSMsxTo9aYrApKnOk5ekC\n\
AwEAAaNjMGEwHQYDVR0OBBYEFDe4aYWivTEYa1hkBy7WaZOVY/S6MB8GA1UdIwQY\n\
MBaAFDe4aYWivTEYa1hkBy7WaZOVY/S6MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P\n\
AQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAEpkndNISW40WGLJa9QVYcBAyP\n\
Ou+vdoRuch+/jeIyM4Lau2yBYu1nyneLmVpJGTCFHxcTrYCmUqJKnkWbHPXoDY27\n\
MPGih8GkiIW057JUbcyktKZqzzmsAoCDz6kLp76BpJiLViiSctufWKeHt8Rm/n66\n\
dI2XmJcJxJ9Xs+QkziinZbiUKQxiDBTgkTT4kxhMESlb4iV9YNpIvW4m8fqCaxtl\n\
ECyJBmqcGIp3bEYchL4hs8I3jbT3VXykjCK/FU/wLWSCIMzhsV4U7JxI0xKGziPU\n\
p83DC7/YpVF7avt05Mwb0n3RhKSmDMLnfWs61zTDQu0T/IYBbr+PvVMsw+W4\n\
-----END CERTIFICATE-----\n";

fn main() {
    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR is set by cargo"));

    let root_ca_dest = out_dir.join("root.crt");
    let root_ca = root_ca_bytes();
    fs::write(&root_ca_dest, &root_ca).unwrap_or_else(|error| {
        panic!(
            "failed to write generated DHTTP root CA to {}: {error}",
            root_ca_dest.display()
        )
    });

    let stun_server = env_or_default(STUN_SERVER_ENV, DEFAULT_STUN_SERVER);
    let bootstrap = format!(
        "// @generated by build.rs; do not edit.\n\
         pub const DHTTP_STUN_SERVER: &str = {stun_server:?};\n\
         pub const DHTTP_ROOT_CA: &[u8] = &{root_ca:?};\n"
    );
    fs::write(out_dir.join("bootstrap.rs"), bootstrap)
        .expect("failed to write generated DHTTP bootstrap constants");

    println!("cargo::rerun-if-env-changed={ROOT_CA_ENV}");
    println!("cargo::rerun-if-env-changed={STUN_SERVER_ENV}");
    if let Some(root_ca) = optional_env_path(ROOT_CA_ENV) {
        println!("cargo::rerun-if-changed={}", root_ca.display());
    }
}

fn env_or_default(name: &str, default: &str) -> String {
    env::var(name).unwrap_or_else(|_| default.to_owned())
}

fn optional_env_path(name: &str) -> Option<PathBuf> {
    env::var_os(name).map(PathBuf::from)
}

fn root_ca_bytes() -> Vec<u8> {
    let Some(root_ca) = optional_env_path(ROOT_CA_ENV) else {
        return DEFAULT_ROOT_CA_PEM.as_bytes().to_vec();
    };

    fs::read(&root_ca).unwrap_or_else(|error| {
        panic!(
            "failed to read DHTTP root CA from {}: {error}",
            root_ca.display()
        )
    })
}

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

    #[test]
    fn missing_stun_env_uses_dhttp_example_net_placeholder() {
        let name = format!("__DHTTP_MISSING_STUN_{}", std::process::id());

        assert_eq!(
            env_or_default(&name, DEFAULT_STUN_SERVER),
            "stun.dhttp.example.net"
        );
    }

    #[test]
    fn placeholder_root_ca_is_pem_certificate() {
        assert!(DEFAULT_ROOT_CA_PEM.starts_with("-----BEGIN CERTIFICATE-----"));
        assert!(DEFAULT_ROOT_CA_PEM.ends_with("-----END CERTIFICATE-----\n"));
    }
}
