use std::env::consts::EXE_EXTENSION;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::LazyLock;

use anyhow::{Context, Result, bail};
use http::header::ACCEPT;
use semver::{Version, VersionReq};
use target_lexicon::{Architecture, ArmArchitecture, Environment, HOST, OperatingSystem};
use tracing::{debug, trace, warn};

use prek_consts::env_vars::{EnvVars, EnvVarsRead};

use crate::archive;
use crate::checksum::{Sha256Digest, digest_from_sha256sums};
use crate::fs::LockedFile;
use crate::http::{
    DownloadChecksumPolicy, REQWEST_CLIENT, download_artifact, download_artifact_with,
};
use crate::process::Cmd;
use crate::store::{CacheBucket, Store};
use crate::warn_user;

// The version range of `uv` we will install. Should update periodically.
const CUR_UV_VERSION: &str = "0.12.3";
static UV_VERSION_RANGE: LazyLock<VersionReq> =
    LazyLock::new(|| VersionReq::parse(">=0.7.0").unwrap());

// Base URLs for the uv release archive. Astral's CDN mirrors GitHub release assets under
// `/github/<repo>/releases/download/...` and is the default; GitHub stays reachable via
// `PREK_UV_SOURCE=github`.
const ASTRAL_UV_RELEASE_BASE: &str = "https://releases.astral.sh/github/uv/releases/download";
const GITHUB_UV_RELEASE_BASE: &str = "https://github.com/astral-sh/uv/releases/download";

fn release_archive_url(base: &str, version: &str, archive_name: &str) -> String {
    format!("{base}/{version}/{archive_name}")
}

fn wheel_platform_tag_for_host(
    operating_system: OperatingSystem,
    architecture: Architecture,
    environment: Environment,
) -> Result<&'static str> {
    let platform_tag = match (operating_system, architecture, environment) {
        // Linux platforms
        (OperatingSystem::Linux, Architecture::X86_64, Environment::Musl) => "musllinux_1_1_x86_64",
        (OperatingSystem::Linux, Architecture::X86_64, _) => {
            "manylinux_2_17_x86_64.manylinux2014_x86_64"
        }
        (OperatingSystem::Linux, Architecture::Aarch64(_), _) => {
            "manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64"
        }
        (OperatingSystem::Linux, Architecture::Arm(ArmArchitecture::Armv7), Environment::Musl) => {
            "manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l"
        }
        (OperatingSystem::Linux, Architecture::Arm(ArmArchitecture::Armv7), _) => {
            "manylinux_2_17_armv7l.manylinux2014_armv7l"
        }
        (OperatingSystem::Linux, Architecture::Arm(ArmArchitecture::Armv6), _) => "linux_armv6l", // Raspberry Pi Zero/1
        (OperatingSystem::Linux, Architecture::X86_32(_), Environment::Musl) => {
            "musllinux_1_1_i686"
        }
        (OperatingSystem::Linux, Architecture::X86_32(_), _) => {
            "manylinux_2_17_i686.manylinux2014_i686"
        }
        (OperatingSystem::Linux, Architecture::Powerpc64, _) => {
            "manylinux_2_17_ppc64.manylinux2014_ppc64"
        }
        (OperatingSystem::Linux, Architecture::Powerpc64le, _) => {
            "manylinux_2_17_ppc64le.manylinux2014_ppc64le"
        }
        (OperatingSystem::Linux, Architecture::S390x, _) => {
            "manylinux_2_17_s390x.manylinux2014_s390x"
        }
        (OperatingSystem::Linux, Architecture::Riscv64(_), _) => "manylinux_2_31_riscv64",

        // macOS platforms
        (OperatingSystem::Darwin(_), Architecture::X86_64, _) => "macosx_10_12_x86_64",
        (OperatingSystem::Darwin(_), Architecture::Aarch64(_), _) => "macosx_11_0_arm64",

        // Windows platforms
        (OperatingSystem::Windows, Architecture::X86_64, _) => "win_amd64",
        (OperatingSystem::Windows, Architecture::X86_32(_), _) => "win32",
        (OperatingSystem::Windows, Architecture::Aarch64(_), _) => "win_arm64",

        _ => bail!(
            "Unsupported platform: operating_system={operating_system:?}, architecture={architecture:?}, environment={environment:?}"
        ),
    };

    Ok(platform_tag)
}

// Get the uv wheel platform tag for the current host.
fn get_wheel_platform_tag() -> Result<String> {
    wheel_platform_tag_for_host(HOST.operating_system, HOST.architecture, HOST.environment)
        .map(ToString::to_string)
}

fn get_uv_version(uv_path: &Path) -> Result<Version> {
    let output = Command::new(uv_path)
        .arg("--version")
        .output()
        .context("Failed to execute uv")?;

    if !output.status.success() {
        bail!("Failed to get uv version");
    }

    let version_output = String::from_utf8_lossy(&output.stdout);
    let version_str = version_output
        .split_whitespace()
        .nth(1)
        .context("Invalid version output format")?;

    Version::parse(version_str).map_err(Into::into)
}

fn validate_uv_binary(uv_path: &Path) -> Result<Version> {
    let version = get_uv_version(uv_path)?;
    if !UV_VERSION_RANGE.matches(&version) {
        bail!(
            "uv version `{version}` does not satisfy required range `{}`",
            *UV_VERSION_RANGE
        );
    }
    Ok(version)
}

async fn replace_uv_binary(source: &Path, target_path: &Path) -> Result<()> {
    if let Some(parent) = target_path.parent() {
        fs_err::tokio::create_dir_all(parent).await?;
    }

    if target_path.exists() {
        debug!(target = %target_path.display(), "Removing existing uv binary");
        fs_err::tokio::remove_file(target_path).await?;
    }

    fs_err::tokio::rename(source, target_path).await?;
    Ok(())
}

static UV_EXE: LazyLock<Option<(PathBuf, Version)>> = LazyLock::new(|| {
    for uv_path in which::which_all("uv").ok()? {
        debug!("Found uv in PATH: {}", uv_path.display());

        match validate_uv_binary(&uv_path) {
            Ok(version) => return Some((uv_path, version)),
            Err(err) => warn!(uv = %uv_path.display(), error = %err, "Skipping incompatible uv"),
        }
    }

    None
});

#[derive(Debug, PartialEq, Eq)]
enum PyPiMirror {
    Pypi,
    Tuna,
    Aliyun,
    Tencent,
    Custom(String),
}

// TODO: support reading pypi source user config, or allow user to set mirror
// TODO: allow opt-out uv

impl PyPiMirror {
    fn url(&self) -> &str {
        match self {
            Self::Pypi => "https://pypi.org/simple/",
            Self::Tuna => "https://pypi.tuna.tsinghua.edu.cn/simple/",
            Self::Aliyun => "https://mirrors.aliyun.com/pypi/simple/",
            Self::Tencent => "https://mirrors.cloud.tencent.com/pypi/simple/",
            Self::Custom(url) => url,
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
enum InstallSource {
    /// Download uv from Astral's CDN (the default).
    Astral,
    /// Download uv from GitHub releases.
    GitHub,
    /// Download uv from `PyPi`.
    PyPi(PyPiMirror),
    /// Install uv by running `pip install uv`.
    Pip,
}

impl InstallSource {
    async fn install(&self, store: &Store, target: &Path) -> Result<Uv> {
        match self {
            Self::Astral => {
                self.install_from_release_archive(store, target, ASTRAL_UV_RELEASE_BASE)
                    .await?;
            }
            Self::GitHub => {
                self.install_from_release_archive(store, target, GITHUB_UV_RELEASE_BASE)
                    .await?;
            }
            Self::PyPi(source) => self.install_from_pypi(store, target, source).await?,
            Self::Pip => self.install_from_pip(target).await?,
        }

        let uv_path = target.join("uv").with_extension(EXE_EXTENSION);
        match validate_uv_binary(&uv_path) {
            Ok(version) => trace!(version = %version, "Successfully installed uv"),
            Err(err) => bail!(
                "Installed uv at `{}` failed validation: {err}. \
                This usually means the downloaded uv binary is incompatible with the \
                current runtime environment, for example due to a libc mismatch or a \
                missing dynamic loader path. If this keeps happening, please report it \
                with details about your environment and the full error output.",
                uv_path.display()
            ),
        }

        Ok(Uv::new(uv_path))
    }

    async fn fetch_release_archive_checksum(
        checksum_url: &str,
        archive_name: &str,
    ) -> Result<Option<Sha256Digest>> {
        let response = REQWEST_CLIENT
            .get(checksum_url)
            .send()
            .await
            .with_context(|| format!("Failed to fetch uv checksum from {checksum_url}"))?;
        if response.status() == reqwest::StatusCode::NOT_FOUND {
            return Ok(None);
        }

        let checksum = response
            .error_for_status()
            .with_context(|| format!("Failed to fetch uv checksum from {checksum_url}"))?
            .text()
            .await
            .with_context(|| format!("Failed to read uv checksum from {checksum_url}"))?;
        digest_from_sha256sums(&checksum, archive_name)
    }

    async fn install_from_release_archive(
        &self,
        store: &Store,
        target: &Path,
        base_url: &str,
    ) -> Result<()> {
        let ext = if cfg!(windows) { "zip" } else { "tar.gz" };
        let archive_name = format!("uv-{HOST}.{ext}");
        let download_url = release_archive_url(base_url, CUR_UV_VERSION, &archive_name);
        let checksum_url = format!("{download_url}.sha256");

        let download = download_artifact(&download_url, &archive_name, store, async || {
            Self::fetch_release_archive_checksum(&checksum_url, &archive_name).await
        })
        .await
        .context("Failed to download uv")?;
        let extracted = archive::extract_archive(download.path())
            .await
            .context("Failed to extract uv")?;
        let source = extracted.join("uv").with_extension(EXE_EXTENSION);
        let target_path = target.join("uv").with_extension(EXE_EXTENSION);

        debug!(?source, target = %target_path.display(), "Moving uv to target");
        // TODO: retry on Windows
        replace_uv_binary(&source, &target_path).await?;

        Ok(())
    }

    async fn install_from_pypi(
        &self,
        store: &Store,
        target: &Path,
        source: &PyPiMirror,
    ) -> Result<()> {
        let platform_tag = get_wheel_platform_tag()?;
        let wheel_name = format!("uv-{CUR_UV_VERSION}-py3-none-{platform_tag}.whl");

        // Use PyPI JSON API instead of parsing HTML
        let api_url = match source {
            PyPiMirror::Pypi => format!("https://pypi.org/pypi/uv/{CUR_UV_VERSION}/json"),
            // For mirrors, we'll fall back to simple API approach
            _ => return self.install_from_simple_api(store, target, source).await,
        };

        debug!("Fetching uv metadata from: {}", api_url);
        let response = REQWEST_CLIENT
            .get(&api_url)
            .header("Accept", "*/*")
            .send()
            .await
            .and_then(reqwest::Response::error_for_status)
            .with_context(|| format!("Failed to fetch uv metadata from PyPI at {api_url}"))?;

        let metadata: serde_json::Value = response.json().await?;
        let files = metadata["urls"]
            .as_array()
            .context("Invalid PyPI response: missing urls")?;

        let wheel_file = files
            .iter()
            .find(|file| {
                file["filename"].as_str() == Some(&wheel_name)
                    && file["packagetype"].as_str() == Some("bdist_wheel")
                    && file["yanked"].as_bool() != Some(true)
            })
            .with_context(|| format!("Could not find wheel for {wheel_name} in PyPI response"))?;

        let download_url = wheel_file["url"]
            .as_str()
            .context("Missing download URL in PyPI response")?;

        self.install_from_wheel_url(store, target, &wheel_name, download_url)
            .await
    }

    async fn install_from_simple_api(
        &self,
        store: &Store,
        target: &Path,
        source: &PyPiMirror,
    ) -> Result<()> {
        // Fallback for mirrors that don't support JSON API
        let platform_tag = get_wheel_platform_tag()?;
        let wheel_name = format!("uv-{CUR_UV_VERSION}-py3-none-{platform_tag}.whl");

        let simple_url = format!("{}uv/", source.url());

        debug!("Fetching from simple API: {}", simple_url);
        let response = REQWEST_CLIENT
            .get(&simple_url)
            .header(ACCEPT, "*/*")
            .send()
            .await?;
        let html = response.text().await?;

        // Simple string search to find the wheel download link
        let search_pattern = r#"href=""#.to_string();

        let download_path = html
            .lines()
            .find(|line| line.contains(&wheel_name))
            .and_then(|line| {
                if let Some(start) = line.find(&search_pattern) {
                    let start = start + search_pattern.len();
                    if let Some(end) = line[start..].find('"') {
                        return Some(&line[start..start + end]);
                    }
                }
                None
            })
            .with_context(|| {
                format!(
                    "Could not find wheel download link for {wheel_name} in simple API response"
                )
            })?;

        // Resolve relative URLs
        let download_url = if download_path.starts_with("http") {
            download_path.to_string()
        } else {
            format!("{simple_url}{download_path}")
        };

        self.install_from_wheel_url(store, target, &wheel_name, &download_url)
            .await
    }

    async fn install_from_wheel_url(
        &self,
        store: &Store,
        target: &Path,
        filename: &str,
        download_url: &str,
    ) -> Result<()> {
        let download = download_artifact_with(
            download_url,
            filename,
            store,
            DownloadChecksumPolicy::Disabled,
            async || Ok(None),
            |req| req,
        )
        .await
        .context("Failed to download uv wheel")?;
        let extracted = archive::extract_archive(download.path())
            .await
            .context("Failed to extract uv wheel")?;

        // Find the uv binary in the extracted contents
        let data_dir = format!("uv-{CUR_UV_VERSION}.data");
        let extracted_uv = extracted
            .join(data_dir)
            .join("scripts")
            .join("uv")
            .with_extension(EXE_EXTENSION);

        // Copy the binary to the target location
        let target_path = target.join("uv").with_extension(EXE_EXTENSION);

        debug!(?extracted_uv, target = %target_path.display(), "Moving uv to target");
        replace_uv_binary(&extracted_uv, &target_path).await?;

        // Set executable permissions on Unix
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            let metadata = fs_err::tokio::metadata(&target_path).await?;
            let mut perms = metadata.permissions();
            perms.set_mode(0o755);
            fs_err::tokio::set_permissions(&target_path, perms).await?;
        }

        Ok(())
    }

    async fn install_from_pip(&self, target: &Path) -> Result<()> {
        // When running `pip install` in multiple threads, it can fail
        // without extracting files properly.
        Cmd::new("python3")
            .arg("-m")
            .arg("pip")
            .arg("install")
            .arg("--prefix")
            .arg(target)
            .arg("--only-binary=:all:")
            .arg("--progress-bar=off")
            .arg("--disable-pip-version-check")
            .arg(format!("uv=={CUR_UV_VERSION}"))
            .check(true)
            .output()
            .await?;

        let local_dir = target.join("local");
        let uv_src = if local_dir.is_dir() {
            &local_dir
        } else {
            target
        };

        let bin_dir = uv_src.join(if cfg!(windows) { "Scripts" } else { "bin" });
        let lib_dir = uv_src.join(if cfg!(windows) { "Lib" } else { "lib" });

        let uv = bin_dir.join("uv").with_extension(EXE_EXTENSION);
        let target_path = target.join("uv").with_extension(EXE_EXTENSION);
        replace_uv_binary(&uv, &target_path).await?;
        fs_err::tokio::remove_dir_all(bin_dir).await?;
        fs_err::tokio::remove_dir_all(lib_dir).await?;

        Ok(())
    }
}

pub(crate) struct Uv {
    path: PathBuf,
}

impl Uv {
    pub(crate) fn new(path: PathBuf) -> Self {
        Self { path }
    }

    pub(crate) fn cmd(&self, store: &Store) -> Cmd {
        let mut cmd = Cmd::new(&self.path);
        cmd.env(EnvVars::UV_CACHE_DIR, store.cache_path(CacheBucket::Uv));
        cmd
    }

    /// Install managed uv, trying each default source in order until one succeeds.
    ///
    /// The order is Astral CDN, `PyPI` and its mirrors, then `pip` as a last resort.
    async fn install_with_fallbacks(store: &Store, uv_dir: &Path) -> Result<Self> {
        for source in [
            InstallSource::Astral,
            InstallSource::PyPi(PyPiMirror::Pypi),
            InstallSource::PyPi(PyPiMirror::Tuna),
            InstallSource::PyPi(PyPiMirror::Aliyun),
            InstallSource::PyPi(PyPiMirror::Tencent),
        ] {
            match source.install(store, uv_dir).await {
                Ok(uv) => return Ok(uv),
                Err(err) => {
                    warn!(?source, %err, "Failed to install uv, trying next source");
                }
            }
        }

        InstallSource::Pip
            .install(store, uv_dir)
            .await
            .context("Failed to install uv from every source")
    }

    pub(crate) async fn find_or_install(store: &Store, uv_dir: &Path) -> Result<Self> {
        // 1) Check `uv` alongside `prek` binary (e.g. `uv tool install prek --with uv`)
        let prek_exe = std::env::current_exe()?.canonicalize()?;
        if let Some(prek_dir) = prek_exe.parent() {
            let uv_path = prek_dir.join("uv").with_extension(EXE_EXTENSION);
            if uv_path.is_file() {
                match validate_uv_binary(&uv_path) {
                    Ok(_) => {
                        trace!(uv = %uv_path.display(), "Found compatible uv alongside prek binary");
                        return Ok(Self::new(uv_path));
                    }
                    Err(err) => {
                        warn!(uv = %uv_path.display(), error = %err, "Skipping incompatible uv");
                    }
                }
            }
        }

        // 2) Check if system `uv` meets minimum version requirement
        if let Some((uv_path, version)) = UV_EXE.as_ref() {
            trace!(
                "Using system uv version {} at {}",
                version,
                uv_path.display()
            );
            return Ok(Self::new(uv_path.clone()));
        }

        // 3) Use or install managed `uv`
        let uv_path = uv_dir.join("uv").with_extension(EXE_EXTENSION);

        if uv_path.is_file() {
            match validate_uv_binary(&uv_path) {
                Ok(_) => {
                    trace!(uv = %uv_path.display(), "Found compatible managed uv");
                    return Ok(Self::new(uv_path));
                }
                Err(err) => {
                    warn!(uv = %uv_path.display(), error = %err, "Skipping incompatible managed uv");
                }
            }
        }

        // Install new managed uv with proper locking
        fs_err::tokio::create_dir_all(&uv_dir).await?;
        let _lock = LockedFile::acquire(uv_dir.join(".lock"), "uv").await?;

        if uv_path.is_file() {
            match validate_uv_binary(&uv_path) {
                Ok(_) => {
                    trace!(uv = %uv_path.display(), "Found compatible managed uv");
                    return Ok(Self::new(uv_path));
                }
                Err(err) => {
                    warn!(uv = %uv_path.display(), error = %err, "Skipping incompatible managed uv");
                }
            }
        }

        if let Some(source) = uv_source_from_env(&EnvVars) {
            source.install(store, uv_dir).await
        } else {
            Self::install_with_fallbacks(store, uv_dir).await
        }
    }
}

fn uv_source_from_env(env_vars: &impl EnvVarsRead) -> Option<InstallSource> {
    let var = env_vars.var(EnvVars::PREK_UV_SOURCE).ok()?;
    match var.as_str() {
        "astral" => Some(InstallSource::Astral),
        "github" => Some(InstallSource::GitHub),
        "pypi" => Some(InstallSource::PyPi(PyPiMirror::Pypi)),
        "tuna" => Some(InstallSource::PyPi(PyPiMirror::Tuna)),
        "aliyun" => Some(InstallSource::PyPi(PyPiMirror::Aliyun)),
        "tencent" => Some(InstallSource::PyPi(PyPiMirror::Tencent)),
        "pip" => Some(InstallSource::Pip),
        custom if custom.starts_with("http") => Some(InstallSource::PyPi(PyPiMirror::Custom(var))),
        _ => {
            warn_user!(
                "Invalid value for {}: {:?}. Expected astral, github, pypi, tuna, aliyun, tencent, pip, or an http(s) URL; using default ({:?})",
                EnvVars::PREK_UV_SOURCE,
                var,
                "auto",
            );
            None
        }
    }
}

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

    #[test]
    fn ensure_cur_uv_version_in_range() {
        let version = Version::parse(CUR_UV_VERSION).expect("Invalid CUR_UV_VERSION");
        assert!(
            UV_VERSION_RANGE.matches(&version),
            "CUR_UV_VERSION {CUR_UV_VERSION} does not satisfy the version requirement {}",
            *UV_VERSION_RANGE
        );
    }

    #[test]
    fn release_archive_url_joins_base_version_and_name() {
        assert_eq!(
            release_archive_url(
                ASTRAL_UV_RELEASE_BASE,
                "0.11.29",
                "uv-x86_64-unknown-linux-gnu.tar.gz",
            ),
            "https://releases.astral.sh/github/uv/releases/download/0.11.29/uv-x86_64-unknown-linux-gnu.tar.gz"
        );
        assert_eq!(
            release_archive_url(
                GITHUB_UV_RELEASE_BASE,
                "0.11.29",
                "uv-x86_64-pc-windows-msvc.zip"
            ),
            "https://github.com/astral-sh/uv/releases/download/0.11.29/uv-x86_64-pc-windows-msvc.zip"
        );
    }

    #[test]
    fn uv_source_from_env_reads_source_override() {
        assert_eq!(uv_source_from_env(&EnvVars::from_map(&[])), None);
        assert_eq!(
            uv_source_from_env(&EnvVars::from_map(&[(EnvVars::PREK_UV_SOURCE, "astral")])),
            Some(InstallSource::Astral)
        );
        assert_eq!(
            uv_source_from_env(&EnvVars::from_map(&[(EnvVars::PREK_UV_SOURCE, "github")])),
            Some(InstallSource::GitHub)
        );
        assert_eq!(
            uv_source_from_env(&EnvVars::from_map(&[(EnvVars::PREK_UV_SOURCE, "pypi")])),
            Some(InstallSource::PyPi(PyPiMirror::Pypi))
        );
        assert_eq!(
            uv_source_from_env(&EnvVars::from_map(&[(EnvVars::PREK_UV_SOURCE, "pip")])),
            Some(InstallSource::Pip)
        );
        assert_eq!(
            uv_source_from_env(&EnvVars::from_map(&[(
                EnvVars::PREK_UV_SOURCE,
                "https://example.com/simple",
            )])),
            Some(InstallSource::PyPi(PyPiMirror::Custom(
                "https://example.com/simple".to_string()
            )))
        );
        assert_eq!(
            uv_source_from_env(&EnvVars::from_map(&[(EnvVars::PREK_UV_SOURCE, "unknown")])),
            None
        );
    }

    #[test]
    fn wheel_platform_tag_x86_64_linux_gnu() -> Result<()> {
        let tag = wheel_platform_tag_for_host(
            OperatingSystem::Linux,
            Architecture::X86_64,
            Environment::Gnu,
        )?;
        assert_eq!(tag, "manylinux_2_17_x86_64.manylinux2014_x86_64");
        Ok(())
    }

    #[test]
    fn wheel_platform_tag_x86_64_linux_musl() -> Result<()> {
        let tag = wheel_platform_tag_for_host(
            OperatingSystem::Linux,
            Architecture::X86_64,
            Environment::Musl,
        )?;
        assert_eq!(tag, "musllinux_1_1_x86_64");
        Ok(())
    }

    #[test]
    fn wheel_platform_tag_i686_linux_gnu() -> Result<()> {
        let tag = wheel_platform_tag_for_host(
            OperatingSystem::Linux,
            Architecture::X86_32(target_lexicon::X86_32Architecture::I686),
            Environment::Gnu,
        )?;
        assert_eq!(tag, "manylinux_2_17_i686.manylinux2014_i686");
        Ok(())
    }

    #[test]
    fn wheel_platform_tag_i686_linux_musl() -> Result<()> {
        let tag = wheel_platform_tag_for_host(
            OperatingSystem::Linux,
            Architecture::X86_32(target_lexicon::X86_32Architecture::I686),
            Environment::Musl,
        )?;
        assert_eq!(tag, "musllinux_1_1_i686");
        Ok(())
    }

    #[test]
    fn wheel_platform_tag_aarch64_linux_gnu() -> Result<()> {
        let tag = wheel_platform_tag_for_host(
            OperatingSystem::Linux,
            Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
            Environment::Gnu,
        )?;
        assert_eq!(
            tag,
            "manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64"
        );
        Ok(())
    }

    #[test]
    fn wheel_platform_tag_aarch64_linux_musl() -> Result<()> {
        let tag = wheel_platform_tag_for_host(
            OperatingSystem::Linux,
            Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
            Environment::Musl,
        )?;
        // aarch64 uses a single dual-tagged wheel for both glibc and musl
        assert_eq!(
            tag,
            "manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64"
        );
        Ok(())
    }

    #[test]
    fn wheel_platform_tag_armv7_linux_gnu() -> Result<()> {
        let tag = wheel_platform_tag_for_host(
            OperatingSystem::Linux,
            Architecture::Arm(ArmArchitecture::Armv7),
            Environment::Gnu,
        )?;
        assert_eq!(tag, "manylinux_2_17_armv7l.manylinux2014_armv7l");
        Ok(())
    }

    #[test]
    fn wheel_platform_tag_armv7_linux_musl() -> Result<()> {
        let tag = wheel_platform_tag_for_host(
            OperatingSystem::Linux,
            Architecture::Arm(ArmArchitecture::Armv7),
            Environment::Musl,
        )?;
        assert_eq!(
            tag,
            "manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l"
        );
        Ok(())
    }

    #[tokio::test]
    async fn replace_uv_binary_overwrites_existing_file() -> Result<()> {
        let temp = tempfile::tempdir()?;
        let source = temp.path().join("source-uv");
        let target_dir = temp.path().join("tools").join("uv");
        let target_path = target_dir.join("uv").with_extension(EXE_EXTENSION);

        fs_err::create_dir_all(&target_dir)?;
        fs_err::write(&source, b"new")?;
        fs_err::write(&target_path, b"old")?;

        replace_uv_binary(&source, &target_path).await?;

        assert!(!source.exists());
        assert_eq!(fs_err::read(&target_path)?, b"new");

        Ok(())
    }

    #[tokio::test]
    async fn replace_uv_binary_recreates_missing_parent_dir() -> Result<()> {
        let temp = tempfile::tempdir()?;
        let source = temp.path().join("source-uv");
        let target_dir = temp.path().join("tools").join("uv");
        let target_path = target_dir.join("uv").with_extension(EXE_EXTENSION);

        fs_err::create_dir_all(&target_dir)?;
        fs_err::write(&target_path, b"old")?;
        fs_err::remove_dir_all(&target_dir)?;
        fs_err::write(&source, b"new")?;

        replace_uv_binary(&source, &target_path).await?;

        assert!(target_dir.exists());
        assert_eq!(fs_err::read(&target_path)?, b"new");

        Ok(())
    }
}
