use apple_bundles::MacOsApplicationBundleBuilder;
use apple_codesign::{SigningSettings, UnifiedSigner};
use flate2::{Compression, write::GzEncoder};
use icns::{IconFamily, Image};
use plist::{Dictionary, Value};
use sha2::{Digest, Sha256};
use simple_file_manifest::FileEntry;
use std::{
    error::Error,
    ffi::OsStr,
    fs::{self, File},
    io::{self, BufReader, Read, Seek},
    path::{Path, PathBuf},
};
use zip::{CompressionMethod, ZipWriter, write::SimpleFileOptions};

const APP_NAME: &str = "Blogging Desktop Server";
const APP_EXECUTABLE: &str = "bds-ui";
const BUNDLE_ID: &str = "de.rfc1437.ruds";
const MIB: u64 = 1024 * 1024;
const LC_LOAD_DYLIB: u32 = 0x0c;
const LC_ID_DYLIB: u32 = 0x0d;
const LC_LOAD_WEAK_DYLIB: u32 = 0x8000_0018;
const LC_REEXPORT_DYLIB: u32 = 0x8000_001f;
const LC_LAZY_LOAD_DYLIB: u32 = 0x20;
const LC_LOAD_UPWARD_DYLIB: u32 = 0x8000_0023;
const DIRECTML_VERSION: &str = "1.15.4";
const DIRECTML_SHA256: &str = "4e7cb7ddce8cf837a7a75dc029209b520ca0101470fcdf275c1f49736a3615b9";
const MACOS_X64_ORT_VERSION: &str = "1.23.2";
const MACOS_X64_ORT_SHA256: &str =
    "d10359e16347b57d9959f7e80a225a5b4a66ed7d7e007274a15cae86836485a6";

pub(crate) fn prepare_macos_linker_alias(directory: &Path) -> Result<(), Box<dyn Error>> {
    fs::create_dir_all(directory)?;
    fs::write(directory.join("libclang_rt.osx.a"), b"!<arch>\n")?;
    Ok(())
}

pub(crate) fn prepare_macos_x64_runtime(directory: &Path) -> Result<(), Box<dyn Error>> {
    fs::create_dir_all(directory)?;
    let archive_path = directory.join(format!(
        "onnxruntime-osx-x86_64-{MACOS_X64_ORT_VERSION}.tgz"
    ));
    let result = (|| {
        let url = format!(
            "https://github.com/microsoft/onnxruntime/releases/download/v{MACOS_X64_ORT_VERSION}/onnxruntime-osx-x86_64-{MACOS_X64_ORT_VERSION}.tgz"
        );
        let mut response = reqwest::blocking::get(url)?.error_for_status()?;
        let mut archive = File::create(&archive_path)?;
        io::copy(&mut response, &mut archive)?;
        drop(archive);

        let mut archive = BufReader::new(File::open(&archive_path)?);
        let mut digest = Sha256::new();
        let mut buffer = [0_u8; 64 * 1024];
        loop {
            let read = archive.read(&mut buffer)?;
            if read == 0 {
                break;
            }
            digest.update(&buffer[..read]);
        }
        if format!("{:x}", digest.finalize()) != MACOS_X64_ORT_SHA256 {
            return Err("macOS x64 ONNX Runtime archive checksum mismatch".into());
        }

        prepare_macos_x64_runtime_from_archive(File::open(&archive_path)?, directory)
    })();
    if archive_path.try_exists()? {
        fs::remove_file(archive_path)?;
    }
    result
}

fn prepare_macos_x64_runtime_from_archive(
    archive: impl Read,
    directory: &Path,
) -> Result<(), Box<dyn Error>> {
    fs::create_dir_all(directory)?;
    let decoder = flate2::read::GzDecoder::new(archive);
    let mut archive = tar::Archive::new(decoder);
    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        let destination =
            if path.ends_with(format!("lib/libonnxruntime.{MACOS_X64_ORT_VERSION}.dylib")) {
                Some(format!("libonnxruntime.{MACOS_X64_ORT_VERSION}.dylib"))
            } else if path.ends_with("LICENSE") {
                Some("ONNXRuntime-LICENSE.txt".to_owned())
            } else {
                None
            };
        if let Some(destination) = destination {
            io::copy(&mut entry, &mut File::create(directory.join(destination))?)?;
        }
    }
    let versioned = directory.join(format!("libonnxruntime.{MACOS_X64_ORT_VERSION}.dylib"));
    require_file(&versioned)?;
    require_file(&directory.join("ONNXRuntime-LICENSE.txt"))?;
    fs::copy(versioned, directory.join("libonnxruntime.dylib"))?;
    Ok(())
}

pub(crate) fn prepare_windows_runtime(
    directory: &Path,
    sdk_libraries: &Path,
) -> Result<(), Box<dyn Error>> {
    fs::create_dir_all(directory)?;
    let package_path = directory.join(format!("microsoft.ai.directml.{DIRECTML_VERSION}.nupkg"));
    let result = (|| {
        let url = format!(
            "https://api.nuget.org/v3-flatcontainer/microsoft.ai.directml/{DIRECTML_VERSION}/microsoft.ai.directml.{DIRECTML_VERSION}.nupkg"
        );
        let mut response = reqwest::blocking::get(url)?.error_for_status()?;
        let mut package = File::create(&package_path)?;
        io::copy(&mut response, &mut package)?;
        drop(package);

        let mut package = BufReader::new(File::open(&package_path)?);
        let mut digest = Sha256::new();
        let mut buffer = [0_u8; 64 * 1024];
        loop {
            let read = package.read(&mut buffer)?;
            if read == 0 {
                break;
            }
            digest.update(&buffer[..read]);
        }
        let digest = digest.finalize();
        if format!("{digest:x}") != DIRECTML_SHA256 {
            return Err("DirectML package checksum mismatch".into());
        }
        prepare_windows_runtime_from_archive(File::open(&package_path)?, directory)?;
        prepare_windows_sdk_linker_aliases(sdk_libraries, directory)
    })();
    if package_path.try_exists()? {
        fs::remove_file(package_path)?;
    }
    result
}

fn prepare_windows_sdk_linker_aliases(
    sdk_libraries: &Path,
    directory: &Path,
) -> Result<(), Box<dyn Error>> {
    let source = fs::read_dir(sdk_libraries)?
        .filter_map(Result::ok)
        .find(|entry| {
            entry
                .file_name()
                .to_str()
                .is_some_and(|name| name.eq_ignore_ascii_case("pathcch.lib"))
        })
        .ok_or_else(|| {
            format!(
                "PathCch.lib is missing from Windows SDK directory: {}",
                sdk_libraries.display()
            )
        })?
        .path();
    fs::copy(source, directory.join("PathCch.lib"))?;
    Ok(())
}

fn prepare_windows_runtime_from_archive(
    archive: impl Read + Seek,
    directory: &Path,
) -> Result<(), Box<dyn Error>> {
    fs::create_dir_all(directory)?;
    let mut archive = zip::ZipArchive::new(archive)?;
    for (source, destination) in [
        ("bin/x64-win/DirectML.lib", "DirectML.lib"),
        ("bin/x64-win/DirectML.dll", "DirectML.dll"),
        ("LICENSE.txt", "DirectML-LICENSE.txt"),
    ] {
        let mut source = archive.by_name(source)?;
        io::copy(&mut source, &mut File::create(directory.join(destination))?)?;
    }
    Ok(())
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Platform {
    MacOs,
    Linux,
    Windows,
}

impl Platform {
    fn from_label(label: &str) -> Result<Self, Box<dyn Error>> {
        if label.starts_with("darwin-") {
            Ok(Self::MacOs)
        } else if label.starts_with("linux-") {
            Ok(Self::Linux)
        } else if label.starts_with("windows-") {
            Ok(Self::Windows)
        } else {
            Err(format!("unsupported release platform: {label}").into())
        }
    }

    fn executable(self, name: &str) -> String {
        if self == Self::Windows {
            format!("{name}.exe")
        } else {
            name.to_owned()
        }
    }

    fn shared_libraries(self) -> [&'static str; 2] {
        match self {
            Self::MacOs => ["libbds_core.dylib", "libbds_server.dylib"],
            Self::Linux => ["libbds_core.so", "libbds_server.so"],
            Self::Windows => ["bds_core.dll", "bds_server.dll"],
        }
    }

    fn is_dynamic_std(self, path: &Path) -> bool {
        let name = path.file_name().and_then(OsStr::to_str).unwrap_or_default();
        match self {
            Self::MacOs => name.starts_with("libstd-") && name.ends_with(".dylib"),
            Self::Linux => name.starts_with("libstd-") && name.ends_with(".so"),
            Self::Windows => name.starts_with("std-") && name.ends_with(".dll"),
        }
    }
}

pub(crate) fn package(
    release_tag: &str,
    release_dir: &Path,
    rust_lib_dir: &Path,
    platform_label: &str,
    dist_dir: &Path,
) -> Result<Vec<PathBuf>, Box<dyn Error>> {
    let platform = Platform::from_label(platform_label)?;
    fs::create_dir_all(dist_dir)?;
    let version = safe_tag(release_tag);
    let runtime = runtime_files(platform, release_dir, rust_lib_dir)?;

    let cli_files = executable_files(platform, release_dir, &["bds-cli", "bds-mcp"])?;
    let cli_name = format!("bds-cli-{version}-{platform_label}");
    let desktop_name = format!("bds-desktop-{version}-{platform_label}");
    let cli_asset = match platform {
        Platform::Windows => {
            let path = dist_dir.join(format!("{cli_name}.zip"));
            zip_archive(&path, &cli_name, cli_files.iter().chain(&runtime))?;
            path
        }
        Platform::MacOs | Platform::Linux => {
            let path = dist_dir.join(format!("{cli_name}.tar.gz"));
            tar_gz_archive(&path, &cli_name, cli_files.iter().chain(&runtime))?;
            path
        }
    };

    let desktop_files = executable_files(
        platform,
        release_dir,
        &[APP_EXECUTABLE, "bds-cli", "bds-mcp"],
    )?;

    let desktop_asset = match platform {
        Platform::MacOs => {
            let path = dist_dir.join(format!("{desktop_name}.dmg"));
            macos_dmg(release_tag, &path, desktop_files.iter().chain(&runtime))?;
            path
        }
        Platform::Linux => {
            let path = dist_dir.join(format!("{desktop_name}.tar.gz"));
            tar_gz_archive(&path, &desktop_name, desktop_files.iter().chain(&runtime))?;
            path
        }
        Platform::Windows => {
            let path = dist_dir.join(format!("{desktop_name}.zip"));
            zip_archive(&path, &desktop_name, desktop_files.iter().chain(&runtime))?;
            path
        }
    };

    println!("created {}", cli_asset.display());
    println!("created {}", desktop_asset.display());
    Ok(vec![cli_asset, desktop_asset])
}

fn executable_files(
    platform: Platform,
    release_dir: &Path,
    names: &[&str],
) -> Result<Vec<(PathBuf, String)>, Box<dyn Error>> {
    names
        .iter()
        .map(|name| {
            let file_name = platform.executable(name);
            let path = release_dir.join(&file_name);
            require_file(&path)?;
            Ok((path, file_name))
        })
        .collect()
}

fn runtime_files(
    platform: Platform,
    release_dir: &Path,
    rust_lib_dir: &Path,
) -> Result<Vec<(PathBuf, String)>, Box<dyn Error>> {
    let mut files = platform
        .shared_libraries()
        .into_iter()
        .map(|name| {
            let path = release_dir.join(name);
            require_file(&path)?;
            Ok((path, name.to_owned()))
        })
        .collect::<Result<Vec<_>, Box<dyn Error>>>()?;
    let mut std_libraries = fs::read_dir(rust_lib_dir)?
        .filter_map(Result::ok)
        .map(|entry| entry.path())
        .filter(|path| platform.is_dynamic_std(path))
        .collect::<Vec<_>>();
    std_libraries.sort();
    for path in std_libraries {
        let name = path
            .file_name()
            .and_then(OsStr::to_str)
            .ok_or_else(|| format!("invalid runtime filename: {}", path.display()))?
            .to_owned();
        files.push((path, name));
    }
    let mut external_names = match platform {
        Platform::MacOs => {
            let mut names = fs::read_dir(release_dir)?
                .filter_map(Result::ok)
                .map(|entry| entry.file_name())
                .filter_map(|name| name.into_string().ok())
                .filter(|name| name.starts_with("libonnxruntime") && name.contains(".dylib"))
                .collect::<Vec<_>>();
            let has_versioned = names.iter().any(|name| name != "libonnxruntime.dylib");
            if has_versioned {
                names.retain(|name| name != "libonnxruntime.dylib");
            }
            if release_dir.join("ONNXRuntime-LICENSE.txt").is_file() {
                names.push("ONNXRuntime-LICENSE.txt".to_owned());
            }
            names
        }
        Platform::Linux => fs::read_dir(release_dir)?
            .filter_map(Result::ok)
            .map(|entry| entry.file_name())
            .filter_map(|name| name.into_string().ok())
            .filter(|name| name.starts_with("libonnxruntime.so"))
            .collect::<Vec<_>>(),
        Platform::Windows => ["DirectML.dll", "DirectML-LICENSE.txt"]
            .into_iter()
            .map(str::to_owned)
            .collect(),
    };
    external_names.sort();
    for name in external_names {
        let path = release_dir.join(&name);
        require_file(&path)?;
        files.push((path, name));
    }
    Ok(files)
}

fn require_file(path: &Path) -> Result<(), Box<dyn Error>> {
    if path.is_file() {
        Ok(())
    } else {
        Err(format!("release file is missing: {}", path.display()).into())
    }
}

fn ensure_new(path: &Path) -> Result<(), Box<dyn Error>> {
    if path.try_exists()? {
        Err(format!("output already exists: {}", path.display()).into())
    } else {
        Ok(())
    }
}

fn tar_gz_archive<'a>(
    output: &Path,
    root: &str,
    files: impl Iterator<Item = &'a (PathBuf, String)>,
) -> Result<(), Box<dyn Error>> {
    ensure_new(output)?;
    let encoder = GzEncoder::new(File::create(output)?, Compression::best());
    let mut archive = tar::Builder::new(encoder);
    for (source, name) in files {
        archive.append_path_with_name(source, Path::new(root).join(name))?;
    }
    archive.into_inner()?.finish()?;
    Ok(())
}

fn zip_archive<'a>(
    output: &Path,
    root: &str,
    files: impl Iterator<Item = &'a (PathBuf, String)>,
) -> Result<(), Box<dyn Error>> {
    ensure_new(output)?;
    let mut archive = ZipWriter::new(File::create(output)?);
    let options = SimpleFileOptions::default()
        .compression_method(CompressionMethod::Deflated)
        .unix_permissions(0o755);
    for (source, name) in files {
        archive.start_file(format!("{root}/{name}"), options)?;
        io::copy(&mut File::open(source)?, &mut archive)?;
    }
    archive.finish()?;
    Ok(())
}

fn macos_dmg<'a>(
    release_tag: &str,
    output: &Path,
    files: impl Iterator<Item = &'a (PathBuf, String)>,
) -> Result<(), Box<dyn Error>> {
    let files = files.collect::<Vec<_>>();
    ensure_new(output)?;
    let version = bundle_version(release_tag)?;
    let output_dir = output.parent().unwrap_or_else(|| Path::new("."));
    let bundle_path = output_dir.join(format!("{APP_NAME}.app"));
    ensure_new(&bundle_path)?;

    let mut bundle = MacOsApplicationBundleBuilder::new(APP_NAME)?;
    bundle.set_info_plist_required_keys(APP_NAME, BUNDLE_ID, version, "RuDS", APP_EXECUTABLE)?;
    bundle.set_info_plist_key("CFBundleShortVersionString", version)?;
    bundle.set_info_plist_key("CFBundleIconFile", format!("{APP_NAME}.icns"))?;
    bundle.set_info_plist_key("CFBundlePackageType", "APPL")?;
    bundle.set_info_plist_key(
        "LSApplicationCategoryType",
        "public.app-category.productivity",
    )?;
    bundle.set_info_plist_key("LSMinimumSystemVersion", "26.0")?;
    bundle.set_info_plist_key("NSHighResolutionCapable", true)?;
    bundle.set_info_plist_key("CFBundleURLTypes", url_types())?;
    bundle.add_icon(icns()?)?;

    for (source, name) in &files {
        let entry = FileEntry::new_from_path(source, true);
        if is_macos_bundle_resource(name) {
            bundle.add_file_resources(name, entry)?;
        } else {
            bundle.add_file_macos(name, entry)?;
        }
    }
    let materialized = bundle.materialize_bundle(output_dir)?;
    let runtime_names = files
        .iter()
        .filter(|(_, name)| name.ends_with(".dylib"))
        .map(|(_, name)| name.clone())
        .collect::<Vec<_>>();
    for (_, name) in &files {
        let staged = if is_macos_bundle_resource(name) {
            materialized.join("Contents/Resources").join(name)
        } else {
            materialized.join("Contents/MacOS").join(name)
        };
        if is_macos_macho(name) {
            rewrite_macho_rpaths(&staged, &runtime_names)?;
        }
    }
    let signer = UnifiedSigner::new(SigningSettings::default());
    signer.sign_path_in_place(&materialized)?;
    apple_dmg::create_dmg(
        &materialized,
        output,
        APP_NAME,
        dmg_sectors(directory_size(&materialized)?)?,
    )?;
    signer.sign_path_in_place(output)?;
    fs::remove_dir_all(materialized)?;
    Ok(())
}

fn is_macos_bundle_resource(name: &str) -> bool {
    name.ends_with(".dylib") || name.ends_with(".txt")
}

fn is_macos_macho(name: &str) -> bool {
    !name.ends_with(".txt")
}

fn rewrite_macho_rpaths(path: &Path, runtime_names: &[String]) -> Result<(), Box<dyn Error>> {
    let mut bytes = fs::read(path)?;
    if read_u32(&bytes, 0)? != 0xfeed_facf {
        return Err(format!(
            "release file is not a 64-bit little-endian Mach-O: {}",
            path.display()
        )
        .into());
    }
    let command_count = read_u32(&bytes, 16)? as usize;
    let commands_size = read_u32(&bytes, 20)? as usize;
    let commands_end = 32_usize
        .checked_add(commands_size)
        .filter(|end| *end <= bytes.len())
        .ok_or_else(|| format!("invalid Mach-O load commands in {}", path.display()))?;
    let mut command_start = 32_usize;
    for _ in 0..command_count {
        let command = read_u32(&bytes, command_start)?;
        let command_size = read_u32(&bytes, command_start + 4)? as usize;
        let command_end = command_start
            .checked_add(command_size)
            .filter(|end| command_size >= 8 && *end <= commands_end)
            .ok_or_else(|| format!("invalid Mach-O load command in {}", path.display()))?;
        if is_dylib_command(command) {
            if command_size < 24 {
                return Err(format!("invalid Mach-O dylib command in {}", path.display()).into());
            }
            let name_offset = read_u32(&bytes, command_start + 8)? as usize;
            let name_start = command_start
                .checked_add(name_offset)
                .filter(|start| name_offset >= 24 && *start < command_end)
                .ok_or_else(|| format!("invalid Mach-O dylib name in {}", path.display()))?;
            let name_length = bytes[name_start..command_end]
                .iter()
                .position(|byte| *byte == 0)
                .ok_or_else(|| format!("unterminated Mach-O dylib name in {}", path.display()))?;
            let current = std::str::from_utf8(&bytes[name_start..name_start + name_length])?;
            let file_name = current.rsplit('/').next().unwrap_or(current);
            if runtime_names.iter().any(|runtime| runtime == file_name) {
                let replacement = format!("@rpath/{file_name}");
                let capacity = command_end - name_start;
                if replacement.len() + 1 > capacity {
                    return Err(format!(
                        "Mach-O load command has no room for {replacement} in {}",
                        path.display()
                    )
                    .into());
                }
                bytes[name_start..command_end].fill(0);
                bytes[name_start..name_start + replacement.len()]
                    .copy_from_slice(replacement.as_bytes());
            }
        }
        command_start = command_end;
    }
    if command_start != commands_end {
        return Err(format!("inconsistent Mach-O load commands in {}", path.display()).into());
    }
    fs::write(path, bytes)?;
    Ok(())
}

fn read_u32(bytes: &[u8], offset: usize) -> Result<u32, Box<dyn Error>> {
    let value = bytes
        .get(offset..offset + 4)
        .ok_or("truncated Mach-O data")?;
    Ok(u32::from_le_bytes(value.try_into()?))
}

fn is_dylib_command(command: u32) -> bool {
    matches!(
        command,
        LC_LOAD_DYLIB
            | LC_ID_DYLIB
            | LC_LOAD_WEAK_DYLIB
            | LC_REEXPORT_DYLIB
            | LC_LAZY_LOAD_DYLIB
            | LC_LOAD_UPWARD_DYLIB
    )
}

fn url_types() -> Value {
    let mut url_type = Dictionary::new();
    url_type.insert("CFBundleURLName".to_owned(), BUNDLE_ID.into());
    url_type.insert(
        "CFBundleURLSchemes".to_owned(),
        Value::Array(vec!["ruds".into()]),
    );
    Value::Array(vec![Value::Dictionary(url_type)])
}

fn icns() -> Result<Vec<u8>, Box<dyn Error>> {
    let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../bds-ui/assets/app-icons/bds.png");
    let image = Image::read_png(BufReader::new(File::open(path)?))?;
    let mut family = IconFamily::new();
    family.add_icon(&image)?;
    let mut bytes = Vec::new();
    family.write(&mut bytes)?;
    Ok(bytes)
}

fn bundle_version(tag: &str) -> Result<&str, Box<dyn Error>> {
    let version = tag.strip_prefix('v').unwrap_or(tag);
    let version = version.split(['-', '+']).next().unwrap_or_default();
    let parts = version.split('.').collect::<Vec<_>>();
    if !(1..=3).contains(&parts.len())
        || parts
            .iter()
            .any(|part| part.is_empty() || !part.bytes().all(|byte| byte.is_ascii_digit()))
    {
        return Err(format!("release tag is not a numeric Apple bundle version: {tag}").into());
    }
    Ok(version)
}

fn safe_tag(tag: &str) -> String {
    tag.chars()
        .map(|character| {
            if character.is_ascii_alphanumeric() || matches!(character, '.' | '_' | '-') {
                character
            } else {
                '-'
            }
        })
        .collect()
}

fn directory_size(path: &Path) -> io::Result<u64> {
    fs::read_dir(path)?.try_fold(0_u64, |total, entry| {
        let entry = entry?;
        let metadata = entry.metadata()?;
        let size = if metadata.is_dir() {
            directory_size(&entry.path())?
        } else {
            metadata.len()
        };
        total
            .checked_add(size)
            .ok_or_else(|| io::Error::other("bundle size overflow"))
    })
}

fn dmg_sectors(bundle_bytes: u64) -> Result<u32, Box<dyn Error>> {
    let capacity = bundle_bytes
        .checked_add((bundle_bytes / 10).max(16 * MIB))
        .ok_or("DMG capacity overflow")?
        .max(64 * MIB);
    Ok(u32::try_from(capacity.div_ceil(512))?)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::{Read, Write};
    use tempfile::tempdir;

    #[test]
    fn apple_bundle_version_comes_from_semver_tag() {
        assert_eq!(bundle_version("v1.2.3").unwrap(), "1.2.3");
        assert_eq!(bundle_version("2.0.0-rc.1").unwrap(), "2.0.0");
        assert!(bundle_version("nightly").is_err());
        assert!(bundle_version("1.2.3.4").is_err());
    }

    #[test]
    fn asset_tag_is_safe_for_paths_and_urls() {
        assert_eq!(safe_tag("release/1 + beta"), "release-1---beta");
    }

    #[test]
    fn dmg_capacity_has_filesystem_headroom() {
        let sectors = dmg_sectors(100 * MIB).unwrap();
        assert!(u64::from(sectors) * 512 >= 116 * MIB);
        assert_eq!(dmg_sectors(1).unwrap(), 131_072);
    }

    #[test]
    fn macos_cross_linker_alias_is_an_empty_static_archive() {
        let temp = tempdir().unwrap();
        prepare_macos_linker_alias(temp.path()).unwrap();

        assert_eq!(
            fs::read(temp.path().join("libclang_rt.osx.a")).unwrap(),
            b"!<arch>\n"
        );
    }

    #[test]
    fn macos_x64_runtime_extracts_the_versioned_library_and_license() {
        let temp = tempdir().unwrap();
        let runtime = temp.path().join("runtime");
        let archive = Vec::new();
        let encoder = GzEncoder::new(archive, Compression::default());
        let mut archive = tar::Builder::new(encoder);
        for (name, contents) in [
            (
                "onnxruntime-osx-x86_64-1.23.2/lib/libonnxruntime.1.23.2.dylib",
                b"runtime".as_slice(),
            ),
            (
                "onnxruntime-osx-x86_64-1.23.2/LICENSE",
                b"license".as_slice(),
            ),
            (
                "onnxruntime-osx-x86_64-1.23.2/include/onnxruntime.h",
                b"header".as_slice(),
            ),
        ] {
            let mut header = tar::Header::new_gnu();
            header.set_size(contents.len() as u64);
            header.set_mode(0o644);
            header.set_cksum();
            archive.append_data(&mut header, name, contents).unwrap();
        }
        let encoder = archive.into_inner().unwrap();
        let archive = std::io::Cursor::new(encoder.finish().unwrap());

        prepare_macos_x64_runtime_from_archive(archive, &runtime).unwrap();

        assert_eq!(
            fs::read(runtime.join("libonnxruntime.1.23.2.dylib")).unwrap(),
            b"runtime"
        );
        assert_eq!(
            fs::read(runtime.join("libonnxruntime.dylib")).unwrap(),
            b"runtime"
        );
        assert_eq!(
            fs::read(runtime.join("ONNXRuntime-LICENSE.txt")).unwrap(),
            b"license"
        );
        assert_eq!(fs::read_dir(runtime).unwrap().count(), 3);
    }

    #[test]
    fn macos_runtime_files_package_one_versioned_ort_library_and_its_license() {
        let temp = tempdir().unwrap();
        let release = temp.path().join("release");
        let rust_libs = temp.path().join("rust-libs");
        fs::create_dir(&release).unwrap();
        fs::create_dir(&rust_libs).unwrap();
        for file in [
            "libbds_core.dylib",
            "libbds_server.dylib",
            "libonnxruntime.dylib",
            "libonnxruntime.1.23.2.dylib",
            "ONNXRuntime-LICENSE.txt",
        ] {
            fs::write(release.join(file), file).unwrap();
        }

        let files = runtime_files(Platform::MacOs, &release, &rust_libs).unwrap();
        let names = files.into_iter().map(|(_, name)| name).collect::<Vec<_>>();

        assert!(names.contains(&"libonnxruntime.1.23.2.dylib".to_owned()));
        assert!(names.contains(&"ONNXRuntime-LICENSE.txt".to_owned()));
        assert!(!names.contains(&"libonnxruntime.dylib".to_owned()));
    }

    #[test]
    fn macos_license_is_a_non_macho_bundle_resource() {
        assert!(is_macos_bundle_resource("ONNXRuntime-LICENSE.txt"));
        assert!(!is_macos_macho("ONNXRuntime-LICENSE.txt"));
        assert!(is_macos_bundle_resource("libonnxruntime.1.23.2.dylib"));
        assert!(is_macos_macho("libonnxruntime.1.23.2.dylib"));
        assert!(!is_macos_bundle_resource("bds-ui"));
        assert!(is_macos_macho("bds-ui"));
    }

    #[test]
    fn windows_runtime_extracts_only_redistributable_files() {
        let temp = tempdir().unwrap();
        let sdk = temp.path().join("sdk");
        let runtime = temp.path().join("runtime");
        fs::create_dir(&sdk).unwrap();
        fs::create_dir(&runtime).unwrap();
        fs::write(sdk.join("pathcch.lib"), b"sdk-import-library").unwrap();
        let archive = std::io::Cursor::new(Vec::new());
        let mut archive = ZipWriter::new(archive);
        let options = SimpleFileOptions::default();
        for (name, contents) in [
            ("bin/x64-win/DirectML.lib", b"import-library".as_slice()),
            ("bin/x64-win/DirectML.dll", b"runtime".as_slice()),
            ("LICENSE.txt", b"license".as_slice()),
            (
                "bin/arm64-win/DirectML.dll",
                b"wrong-architecture".as_slice(),
            ),
        ] {
            archive.start_file(name, options).unwrap();
            archive.write_all(contents).unwrap();
        }
        let archive = archive.finish().unwrap();

        prepare_windows_runtime_from_archive(archive, &runtime).unwrap();
        prepare_windows_sdk_linker_aliases(&sdk, &runtime).unwrap();

        assert_eq!(
            fs::read(runtime.join("DirectML.lib")).unwrap(),
            b"import-library"
        );
        assert_eq!(fs::read(runtime.join("DirectML.dll")).unwrap(), b"runtime");
        assert_eq!(
            fs::read(runtime.join("DirectML-LICENSE.txt")).unwrap(),
            b"license"
        );
        assert_eq!(
            fs::read(runtime.join("PathCch.lib")).unwrap(),
            b"sdk-import-library"
        );
        assert_eq!(fs::read_dir(runtime).unwrap().count(), 4);
    }

    #[test]
    fn windows_package_contains_the_app_without_dynamic_std() {
        let temp = tempdir().unwrap();
        let release = temp.path().join("release");
        let rust_libs = temp.path().join("rust-libs");
        let dist = temp.path().join("dist");
        fs::create_dir(&release).unwrap();
        fs::create_dir(&rust_libs).unwrap();
        for file in [
            "bds-ui.exe",
            "bds-cli.exe",
            "bds-mcp.exe",
            "bds_core.dll",
            "bds_server.dll",
            "DirectML.dll",
            "DirectML-LICENSE.txt",
        ] {
            fs::write(release.join(file), format!("MZ-{file}")).unwrap();
        }
        let assets = package("v1.2.3", &release, &rust_libs, "windows-x64", &dist).unwrap();
        assert_eq!(assets.len(), 2);

        let desktop = dist.join("bds-desktop-v1.2.3-windows-x64.zip");
        let mut archive = zip::ZipArchive::new(File::open(desktop).unwrap()).unwrap();
        let mut contents = Vec::new();
        archive
            .by_name("bds-desktop-v1.2.3-windows-x64/bds-ui.exe")
            .unwrap()
            .read_to_end(&mut contents)
            .unwrap();
        assert_eq!(contents, b"MZ-bds-ui.exe");
        assert!(
            archive
                .by_name("bds-desktop-v1.2.3-windows-x64/bds_core.dll")
                .is_ok()
        );
        for runtime in ["DirectML.dll", "DirectML-LICENSE.txt"] {
            assert!(
                archive
                    .by_name(&format!("bds-desktop-v1.2.3-windows-x64/{runtime}"))
                    .is_ok(),
                "missing {runtime}"
            );
        }
    }

    #[test]
    fn linux_package_contains_the_desktop_application() {
        let temp = tempdir().unwrap();
        let release = temp.path().join("release");
        let rust_libs = temp.path().join("rust-libs");
        let dist = temp.path().join("dist");
        fs::create_dir(&release).unwrap();
        fs::create_dir(&rust_libs).unwrap();
        for file in [
            "bds-ui",
            "bds-cli",
            "bds-mcp",
            "libbds_core.so",
            "libbds_server.so",
        ] {
            fs::write(release.join(file), file).unwrap();
        }

        let assets = package("v1.2.3", &release, &rust_libs, "linux-arm64", &dist).unwrap();

        assert_eq!(
            assets,
            [
                dist.join("bds-cli-v1.2.3-linux-arm64.tar.gz"),
                dist.join("bds-desktop-v1.2.3-linux-arm64.tar.gz"),
            ]
        );

        let desktop = File::open(dist.join("bds-desktop-v1.2.3-linux-arm64.tar.gz")).unwrap();
        let decoder = flate2::read::GzDecoder::new(desktop);
        let mut archive = tar::Archive::new(decoder);
        let names = archive
            .entries()
            .unwrap()
            .map(|entry| entry.unwrap().path().unwrap().into_owned())
            .collect::<Vec<_>>();
        assert!(
            !names
                .iter()
                .any(|name| name.to_string_lossy().contains("onnxruntime")),
            "the statically linked Linux runtime must not require an external ORT library"
        );
        assert!(names.iter().any(|name| name.ends_with("bds-ui")));
        assert!(names.iter().any(|name| name.ends_with("libbds_core.so")));
    }

    #[test]
    fn macho_runtime_install_names_are_rewritten_to_rpath() {
        let temp = tempdir().unwrap();
        let macho = temp.path().join("libbds_core.dylib");
        let old_name = b"/build/target/release/libbds_core.dylib\0";
        let command_size = (24 + old_name.len()).div_ceil(8) * 8;
        let mut bytes = vec![0_u8; 32 + command_size];
        bytes[0..4].copy_from_slice(&0xfeedfacf_u32.to_le_bytes());
        bytes[16..20].copy_from_slice(&1_u32.to_le_bytes());
        bytes[20..24].copy_from_slice(&(command_size as u32).to_le_bytes());
        bytes[32..36].copy_from_slice(&LC_ID_DYLIB.to_le_bytes());
        bytes[36..40].copy_from_slice(&(command_size as u32).to_le_bytes());
        bytes[40..44].copy_from_slice(&24_u32.to_le_bytes());
        bytes[56..56 + old_name.len()].copy_from_slice(old_name);
        fs::write(&macho, bytes).unwrap();

        rewrite_macho_rpaths(&macho, &["libbds_core.dylib".to_owned()]).unwrap();

        let rewritten = fs::read(macho).unwrap();
        let name_end = rewritten[56..].iter().position(|byte| *byte == 0).unwrap();
        assert_eq!(&rewritten[56..56 + name_end], b"@rpath/libbds_core.dylib");
    }
}
