use std::env::VarError;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::process;
use std::str::FromStr;
use std::time::Duration;

use rustc_hash::FxHashSet;
use uv_audit::{VulnerabilityID, VulnerabilityServiceFormat};

use crate::commands::{PythonUpgrade, PythonUpgradeSource};
use uv_auth::Service;
use uv_cache::{CacheArgs, Refresh};
use uv_cli::comma::CommaSeparatedRequirements;
use uv_cli::{
    AddArgs, AuditArgs, AuditOutputFormat, AuthLoginArgs, AuthLogoutArgs, AuthTokenArgs,
    ColorChoice, ExternalCommand, GlobalArgs, InitArgs, ListFormat, LockArgs, Maybe, MetadataArgs,
    PipCheckArgs, PipCompileArgs, PipFreezeArgs, PipInstallArgs, PipListArgs, PipShowArgs,
    PipSyncArgs, PipTreeArgs, PipUninstallArgs, PythonFindArgs, PythonInstallArgs, PythonListArgs,
    PythonListFormat, PythonPinArgs, PythonUninstallArgs, PythonUpgradeArgs, RemoveArgs, RunArgs,
    SyncArgs, SyncFormat, ToolDirArgs, ToolInstallArgs, ToolListArgs, ToolRunArgs,
    ToolUninstallArgs, TreeArgs, VenvArgs, VersionArgs, VersionBumpSpec, VersionFormat,
};
use uv_cli::{
    AuthorFrom, BuildArgs, CheckArgs, ExportArgs, FormatArgs, PublishArgs, PythonDirArgs,
    ResolverInstallerArgs, ToolUpgradeArgs,
    options::{
        Flag, FlagSource, check_conflicts, flag, resolve_flag, resolve_flag_pair,
        resolver_installer_options, resolver_options,
    },
};
use uv_client::Connectivity;
use uv_configuration::{
    BuildIsolation, BuildOptions, Concurrency, DependencyGroups, DryRun, EditableMode, EnvFile,
    ExportFormat, ExtrasSpecification, GitLfsSetting, HashCheckingMode, IndexStrategy,
    InstallOptions, KeyringProviderType, NoBinary, NoBuild, NoSources, PipCompileFormat,
    ProjectBuildBackend, ProxyUrl, Reinstall, RequiredVersion, TargetTriple, TrustedHost,
    TrustedPublishing, Upgrade, VersionControlSystem,
};
use uv_distribution_types::{
    ConfigSettings, DependencyMetadata, ExtraBuildVariables, Index, IndexLocations, IndexUrl,
    PackageConfigSettings, Requirement,
};
use uv_install_wheel::LinkMode;
use uv_normalize::{ExtraName, PackageName, PipGroupName};
use uv_pep508::{MarkerTree, RequirementOrigin};
use uv_preview::Preview;
use uv_pypi_types::SupportedEnvironments;
use uv_python::{Prefix, PythonDownloads, PythonPreference, PythonVersion, Target};
use uv_redacted::DisplaySafeUrl;
use uv_resolver::{
    AnnotationStyle, DependencyMode, ExcludeNewer, ExcludeNewerPackage, ForkStrategy,
    PrereleaseMode, ResolutionMode,
};
use uv_settings::{
    Combine, EnvironmentOptions, FilesystemOptions, MalwareCheckSettings, Options, PipOptions,
    PublishOptions, PythonInstallMirrors, ResolverInstallerOptions, ResolverInstallerSchema,
    ResolverOptions,
};
use uv_static::EnvVars;
use uv_torch::TorchMode;
use uv_warnings::warn_user_once;
use uv_workspace::pyproject::{DependencyType, ExtraBuildDependencies};
use uv_workspace::pyproject_mut::AddBoundsKind;

use crate::commands::ToolRunCommand;
use crate::commands::{InitKind, InitProjectKind, pip::operations::Modifications};

/// The default publish URL.
const PYPI_PUBLISH_URL: &str = "https://upload.pypi.org/legacy/";

/// The resolved global settings to use for any invocation of the CLI.
#[derive(Debug, Clone)]
pub(crate) struct GlobalSettings {
    pub(crate) required_version: Option<RequiredVersion>,
    pub(crate) quiet: u8,
    pub(crate) verbose: u8,
    pub(crate) color: ColorChoice,
    pub(crate) network_settings: NetworkSettings,
    pub(crate) concurrency: Concurrency,
    pub(crate) show_settings: bool,
    pub(crate) preview: Preview,
    pub(crate) python_preference: PythonPreference,
    pub(crate) python_downloads: PythonDownloads,
    pub(crate) no_progress: bool,
    pub(crate) installer_metadata: bool,
}

impl GlobalSettings {
    /// Resolve the [`GlobalSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: &GlobalArgs,
        workspace: Option<&FilesystemOptions>,
        environment: &EnvironmentOptions,
    ) -> Self {
        let network_settings = NetworkSettings::resolve(args, workspace, environment);
        let python_preference = resolve_python_preference(args, workspace, environment);
        let color = resolve_color(args);
        Self {
            required_version: workspace
                .and_then(|workspace| workspace.globals.required_version.clone()),
            quiet: args.quiet,
            verbose: args.verbose,
            color,
            network_settings,
            concurrency: Concurrency::new(
                environment
                    .concurrency
                    .downloads
                    .combine(workspace.and_then(|workspace| workspace.globals.concurrent_downloads))
                    .map(NonZeroUsize::get)
                    .unwrap_or(Concurrency::DEFAULT_DOWNLOADS),
                environment
                    .concurrency
                    .builds
                    .combine(workspace.and_then(|workspace| workspace.globals.concurrent_builds))
                    .map(NonZeroUsize::get)
                    .unwrap_or_else(Concurrency::threads),
                environment
                    .concurrency
                    .installs
                    .combine(workspace.and_then(|workspace| workspace.globals.concurrent_installs))
                    .map(NonZeroUsize::get)
                    .unwrap_or_else(Concurrency::threads),
            ),
            show_settings: args.show_settings,
            preview: Preview::from_args(
                resolve_preview(args, workspace, environment),
                args.no_preview,
                &args.preview_features,
            ),
            python_preference,
            python_downloads: flag(
                args.allow_python_downloads,
                args.no_python_downloads,
                "python-downloads",
            )
            .map(PythonDownloads::from)
            .combine(env(env::UV_PYTHON_DOWNLOADS))
            .combine(workspace.and_then(|workspace| workspace.globals.python_downloads))
            .unwrap_or_default(),
            // Disable the progress bar with `RUST_LOG` to avoid progress fragments interleaving
            // with log messages.
            no_progress: resolve_flag(args.no_progress, "no-progress", environment.no_progress)
                .is_enabled()
                || std::env::var_os(EnvVars::RUST_LOG).is_some(),
            installer_metadata: !resolve_flag(
                args.no_installer_metadata,
                "no-installer-metadata",
                environment.no_installer_metadata,
            )
            .is_enabled(),
        }
    }
}

/// Resolve the color choice from CLI arguments and environment variables.
pub(crate) fn resolve_color(args: &GlobalArgs) -> ColorChoice {
    if let Some(color_choice) = args.color {
        // If `--color` is passed explicitly, use its value.
        color_choice
    } else if args.no_color {
        // If `--no-color` is passed explicitly, disable color output.
        ColorChoice::Never
    } else if std::env::var_os(EnvVars::NO_COLOR)
        .as_ref()
        .is_some_and(|v| !v.is_empty())
    {
        // If the `NO_COLOR` is set, disable color output.
        ColorChoice::Never
    } else if std::env::var_os(EnvVars::FORCE_COLOR)
        .as_ref()
        .is_some_and(|v| !v.is_empty())
        || std::env::var_os(EnvVars::CLICOLOR_FORCE)
            .as_ref()
            .is_some_and(|v| !v.is_empty())
    {
        // If `FORCE_COLOR` or `CLICOLOR_FORCE` is set, always enable color output.
        ColorChoice::Always
    } else {
        ColorChoice::Auto
    }
}

fn resolve_python_preference(
    args: &GlobalArgs,
    workspace: Option<&FilesystemOptions>,
    environment: &EnvironmentOptions,
) -> PythonPreference {
    // Resolve flags from CLI and environment variables.
    let (managed_python, no_managed_python) = resolve_flag_pair(
        args.managed_python,
        args.no_managed_python,
        "managed-python",
        "no-managed-python",
        Some(environment.managed_python),
        Some(environment.no_managed_python),
    );

    // Check for conflicts between managed_python and python_preference.
    if managed_python.is_enabled() && args.python_preference.is_some() {
        check_conflicts(managed_python, Flag::from_cli("python-preference"));
    }

    // Check for conflicts between no_managed_python and python_preference.
    if no_managed_python.is_enabled() && args.python_preference.is_some() {
        check_conflicts(no_managed_python, Flag::from_cli("python-preference"));
    }

    if managed_python.is_enabled() {
        PythonPreference::OnlyManaged
    } else if no_managed_python.is_enabled() {
        PythonPreference::OnlySystem
    } else {
        args.python_preference
            .combine(workspace.and_then(|workspace| workspace.globals.python_preference))
            .unwrap_or_default()
    }
}

/// Resolve the preview setting from CLI, environment, and workspace config.
pub(crate) fn resolve_preview(
    args: &GlobalArgs,
    workspace: Option<&FilesystemOptions>,
    environment: &EnvironmentOptions,
) -> bool {
    // CLI takes precedence
    match flag(args.preview, args.no_preview, "preview") {
        Some(value) => value,
        None => {
            // Check environment variable
            if environment.preview.value == Some(true) {
                true
            } else {
                // Fall back to workspace config
                workspace
                    .and_then(|workspace| workspace.globals.preview)
                    .unwrap_or(false)
            }
        }
    }
}

/// The resolved network settings to use for any invocation of the CLI.
#[derive(Debug, Clone)]
pub(crate) struct NetworkSettings {
    pub(crate) connectivity: Connectivity,
    pub(crate) offline: Flag,
    pub(crate) system_certs: bool,
    pub(crate) http_proxy: Option<ProxyUrl>,
    pub(crate) https_proxy: Option<ProxyUrl>,
    pub(crate) no_proxy: Option<Vec<String>>,
    pub(crate) allow_insecure_host: Vec<TrustedHost>,
    pub(crate) read_timeout: Duration,
    pub(crate) connect_timeout: Duration,
    pub(crate) retries: u32,
}

impl NetworkSettings {
    #[allow(deprecated)]
    pub(crate) fn resolve(
        args: &GlobalArgs,
        workspace: Option<&FilesystemOptions>,
        environment: &EnvironmentOptions,
    ) -> Self {
        // Resolve offline flag from CLI, environment variable, and workspace config.
        // Precedence: CLI > Env var > Workspace config > default (false).
        let offline = match flag(args.offline, args.no_offline, "offline") {
            Some(true) => Flag::from_cli("offline"),
            Some(false) => Flag::disabled(),
            None => {
                // CLI didn't provide a value, check environment variable.
                let env_flag = resolve_flag(false, "offline", environment.offline);
                if env_flag.is_enabled() {
                    env_flag
                } else if workspace
                    .and_then(|workspace| workspace.globals.offline)
                    .unwrap_or(false)
                {
                    // Workspace config enabled offline mode.
                    Flag::from_config("offline")
                } else {
                    Flag::disabled()
                }
            }
        };

        let connectivity = if offline.is_enabled() {
            Connectivity::Offline
        } else {
            Connectivity::Online
        };

        if args.native_tls {
            warn_user_once!(
                "The `--native-tls` flag is deprecated and will be removed in a future release. Use `--system-certs` instead."
            );
        }
        if args.no_native_tls {
            warn_user_once!(
                "The `--no-native-tls` flag is deprecated and will be removed in a future release. Use `--no-system-certs` instead."
            );
        }
        if environment.native_tls.value.is_some() {
            warn_user_once!(
                "The `UV_NATIVE_TLS` environment variable is deprecated and will be removed in a future release. Use `UV_SYSTEM_CERTS` instead."
            );
        }
        if workspace
            .and_then(|workspace| workspace.globals.native_tls)
            .is_some()
        {
            warn_user_once!(
                "The `native-tls` setting is deprecated and will be removed in a future release. Use `system-certs` instead."
            );
        }

        // Resolve whether to use system certificates.
        //
        // `--native-tls` is a legacy alias for `--system-certs` — it enables system certificates
        // but does NOT change the TLS backend. Any explicit CLI setting should take precedence
        // over environment variables and workspace configuration, regardless of which spelling is
        // used.
        let system_certs =
            if let Some(value) = flag(args.system_certs, args.no_system_certs, "system-certs") {
                value
            } else if let Some(value) = flag(args.native_tls, args.no_native_tls, "native-tls") {
                value
            } else if let Some(true) = environment.system_certs.value {
                true
            } else if let Some(true) = environment.native_tls.value {
                true
            } else {
                workspace
                    .and_then(|workspace| {
                        workspace
                            .globals
                            .system_certs
                            .or(workspace.globals.native_tls)
                    })
                    .unwrap_or(false)
            };

        let allow_insecure_host = args
            .allow_insecure_host
            .as_ref()
            .map(|allow_insecure_host| {
                allow_insecure_host
                    .iter()
                    .filter_map(|value| value.clone().into_option())
            })
            .into_iter()
            .flatten()
            .chain(
                workspace
                    .and_then(|workspace| workspace.globals.allow_insecure_host.clone())
                    .into_iter()
                    .flatten(),
            )
            .collect();
        let http_proxy = workspace.and_then(|workspace| workspace.globals.http_proxy.clone());
        let https_proxy = workspace.and_then(|workspace| workspace.globals.https_proxy.clone());
        let no_proxy = workspace.and_then(|workspace| workspace.globals.no_proxy.clone());

        Self {
            connectivity,
            offline,
            system_certs,
            http_proxy,
            https_proxy,
            no_proxy,
            allow_insecure_host,
            read_timeout: environment.http_read_timeout,
            connect_timeout: environment.http_connect_timeout,
            retries: environment.http_retries,
        }
    }

    /// Check if offline mode conflicts with a refresh request.
    ///
    /// This should be called when a command uses refresh functionality to ensure
    /// offline mode and refresh are not both enabled.
    pub(crate) fn check_refresh_conflict(&self, refresh: &Refresh) {
        if !matches!(refresh, Refresh::None(_)) {
            // TODO(charlie): `Refresh` isn't a `Flag`, so we create a synthetic one here
            // (which matches Clap's representation). Consider a dedicated helper for
            // conflicts with CLI-only arguments.
            check_conflicts(self.offline, Flag::from_cli("refresh"));
        }
    }
}

/// The resolved cache settings to use for any invocation of the CLI.
#[derive(Debug, Clone)]
pub(crate) struct CacheSettings {
    pub(crate) no_cache: bool,
    pub(crate) cache_dir: Option<PathBuf>,
}

impl CacheSettings {
    /// Resolve the [`CacheSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(args: CacheArgs, workspace: Option<&FilesystemOptions>) -> Self {
        Self {
            no_cache: args.no_cache
                || workspace
                    .and_then(|workspace| workspace.globals.no_cache)
                    .unwrap_or(false),
            cache_dir: args
                .cache_dir
                .or_else(|| workspace.and_then(|workspace| workspace.globals.cache_dir.clone())),
        }
    }
}

/// The resolved settings to use for a `init` invocation.
#[derive(Debug, Clone)]
pub(crate) struct InitSettings {
    pub(crate) path: Option<PathBuf>,
    pub(crate) name: Option<PackageName>,
    pub(crate) package: bool,
    pub(crate) kind: InitKind,
    pub(crate) bare: bool,
    pub(crate) description: Option<String>,
    pub(crate) no_description: bool,
    pub(crate) vcs: Option<VersionControlSystem>,
    pub(crate) build_backend: Option<ProjectBuildBackend>,
    pub(crate) no_readme: bool,
    pub(crate) author_from: Option<AuthorFrom>,
    pub(crate) pin_python: bool,
    pub(crate) no_workspace: bool,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
}

impl InitSettings {
    /// Resolve the [`InitSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: InitArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let InitArgs {
            path,
            name,
            r#virtual,
            package,
            no_package,
            bare,
            app,
            lib,
            script,
            description,
            no_description,
            vcs,
            build_backend,
            no_readme,
            author_from,
            no_pin_python,
            pin_python,
            no_workspace,
            python,
            ..
        } = args;

        let kind = match (app, lib, script) {
            (true, false, false) => InitKind::Project(InitProjectKind::Application),
            (false, true, false) => InitKind::Project(InitProjectKind::Library),
            (false, false, true) => InitKind::Script,
            (false, false, false) => InitKind::default(),
            (_, _, _) => unreachable!("`app`, `lib`, and `script` are mutually exclusive"),
        };

        let package = flag(
            package || build_backend.is_some(),
            no_package || r#virtual,
            "virtual",
        )
        .unwrap_or(kind.packaged_by_default());

        let bare = resolve_flag(bare, "bare", environment.init_bare).is_enabled();

        let filesystem_install_mirrors = filesystem
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let no_description = no_description || (bare && description.is_none());

        Self {
            path,
            name,
            package,
            kind,
            bare,
            description,
            no_description,
            vcs: vcs.or(bare.then_some(VersionControlSystem::None)),
            build_backend,
            no_readme,
            author_from,
            pin_python: flag(pin_python, no_pin_python, "pin-python").unwrap_or(!bare),
            no_workspace,
            python: python.and_then(Maybe::into_option),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
        }
    }
}

/// The source of a lock check operation.
#[derive(Debug, Clone, Copy)]
pub(crate) enum LockCheckSource {
    /// The user invoked `uv <command> --locked`
    LockedCli,
    /// The `UV_LOCKED` environment variable was set.
    LockedEnv,
    /// The `locked` option was set via workspace configuration.
    LockedConfiguration,
    /// The user invoked `uv <command> --check`
    Check,
}

impl std::fmt::Display for LockCheckSource {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::LockedCli => write!(f, "--locked"),
            Self::LockedEnv => write!(f, "UV_LOCKED=1"),
            Self::LockedConfiguration => write!(f, "locked (workspace configuration)"),
            Self::Check => write!(f, "--check"),
        }
    }
}

// Has lock check been enabled?
#[derive(Debug, Clone, Copy)]
pub(crate) enum LockCheck {
    /// Lockfile check is enabled.
    Enabled(LockCheckSource),
    /// Lockfile check is disabled.
    Disabled,
}

/// The source of the frozen flag.
#[derive(Debug, Clone, Copy)]
pub(crate) enum FrozenSource {
    /// The `--frozen` flag was provided on CLI.
    Cli,
    /// The `UV_FROZEN` environment variable was set.
    Env,
    /// The `frozen` option was set via workspace configuration.
    Configuration,
}

/// Convert a resolved flag to an optional frozen source.
fn resolve_frozen(flag: Flag) -> Option<FrozenSource> {
    if flag.is_enabled() {
        Some(match flag.source() {
            Some(FlagSource::Cli) | None => FrozenSource::Cli,
            Some(FlagSource::Env(_)) => FrozenSource::Env,
            Some(FlagSource::Config) => FrozenSource::Configuration,
        })
    } else {
        None
    }
}

/// Convert a resolved flag to a lock check.
fn resolve_lock_check(flag: Flag) -> LockCheck {
    if flag.is_enabled() {
        LockCheck::Enabled(match flag.source() {
            Some(FlagSource::Cli) | None => LockCheckSource::LockedCli,
            Some(FlagSource::Env(_)) => LockCheckSource::LockedEnv,
            Some(FlagSource::Config) => LockCheckSource::LockedConfiguration,
        })
    } else {
        LockCheck::Disabled
    }
}

/// The resolved settings to use for a `run` invocation.
#[derive(Debug, Clone)]
pub(crate) struct RunSettings {
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) extras: ExtrasSpecification,
    pub(crate) groups: DependencyGroups,
    pub(crate) editable: Option<EditableMode>,
    pub(crate) modifications: Modifications,
    pub(crate) with: Vec<String>,
    pub(crate) with_editable: Vec<String>,
    pub(crate) with_requirements: Vec<PathBuf>,
    pub(crate) isolated: bool,
    pub(crate) show_resolution: bool,
    pub(crate) all_packages: bool,
    pub(crate) package: Option<PackageName>,
    pub(crate) no_project: bool,
    pub(crate) active: Option<bool>,
    pub(crate) no_sync: bool,
    pub(crate) python: Option<String>,
    pub(crate) python_platform: Option<TargetTriple>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) settings: ResolverInstallerSettings,
    pub(crate) env_file: EnvFile,
    pub(crate) max_recursion_depth: u32,
    pub(crate) malware_settings: MalwareCheckSettings,
}

impl RunSettings {
    // Default value for UV_RUN_MAX_RECURSION_DEPTH if unset. This is large
    // enough that it's unlikely a user actually needs this recursion depth,
    // but short enough that we detect recursion quickly enough to avoid OOMing
    // or hanging for a long time.
    const DEFAULT_MAX_RECURSION_DEPTH: u32 = 100;

    /// Resolve the [`RunSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: RunArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let RunArgs {
            extra,
            all_extras,
            no_extra,
            no_all_extras,
            dev,
            no_dev,
            group,
            no_group,
            no_default_groups,
            only_group,
            all_groups,
            module: _,
            only_dev,
            editable,
            no_editable,
            no_editable_package,
            inexact,
            exact,
            script: _,
            gui_script: _,
            command: _,
            with,
            with_editable,
            with_requirements,
            isolated,
            active,
            no_active,
            no_sync,
            locked,
            frozen,
            installer,
            build,
            refresh,
            all_packages,
            package,
            no_project,
            python,
            python_platform,
            show_resolution,
            env_file,
            no_env_file,
            max_recursion_depth,
        } = args;

        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen, "frozen", environment.frozen);
        let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        let (dev, no_dev) = resolve_flag_pair(
            dev,
            no_dev,
            "dev",
            "no-dev",
            Some(environment.dev),
            Some(environment.no_dev),
        );

        let (editable, no_editable) = resolve_flag_pair(
            editable,
            no_editable,
            "editable",
            "no-editable",
            None,
            Some(environment.no_editable),
        );
        let isolated = isolated || environment.isolated.value == Some(true);
        let show_resolution = show_resolution || environment.show_resolution.value == Some(true);
        let no_env_file = no_env_file || environment.no_env_file.value == Some(true);

        let malware_settings = MalwareCheckSettings::from(&environment);

        Self {
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            extras: ExtrasSpecification::from_args(
                extra.unwrap_or_default(),
                no_extra,
                // TODO(blueraft): support no_default_extras
                false,
                // TODO(blueraft): support only_extra
                vec![],
                flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
            ),
            groups: DependencyGroups::from_args(
                dev.into(),
                no_dev.into(),
                only_dev,
                group,
                if no_group.is_empty() {
                    environment.no_group.clone().unwrap_or_default()
                } else {
                    no_group
                },
                no_default_groups,
                only_group,
                all_groups,
            ),
            editable: EditableMode::from_args(
                flag(editable.into(), no_editable.into(), "editable"),
                no_editable_package,
            ),
            modifications: if flag(exact, inexact, "inexact").unwrap_or(false) {
                Modifications::Exact
            } else {
                Modifications::Sufficient
            },
            with: with
                .into_iter()
                .flat_map(CommaSeparatedRequirements::into_iter)
                .collect(),
            with_editable: with_editable
                .into_iter()
                .flat_map(CommaSeparatedRequirements::into_iter)
                .collect(),
            with_requirements: with_requirements
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            isolated,
            show_resolution,
            all_packages,
            package,
            no_project,
            no_sync: no_sync.is_enabled(),
            active: flag(active, no_active, "active"),
            python: python.and_then(Maybe::into_option),
            python_platform,
            refresh: Refresh::from(refresh),
            settings: ResolverInstallerSettings::combine(
                resolver_installer_options(installer, build),
                filesystem,
                &environment,
            ),
            env_file: EnvFile::from_args(env_file, no_env_file),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
            max_recursion_depth: max_recursion_depth.unwrap_or(Self::DEFAULT_MAX_RECURSION_DEPTH),
            malware_settings,
        }
    }
}

/// The resolved settings to use for a `tool run` invocation.
#[derive(Debug, Clone)]
pub(crate) struct ToolRunSettings {
    pub(crate) command: Option<ExternalCommand>,
    pub(crate) from: Option<String>,
    pub(crate) with: Vec<String>,
    pub(crate) with_requirements: Vec<PathBuf>,
    pub(crate) with_editable: Vec<String>,
    pub(crate) constraints: Vec<PathBuf>,
    pub(crate) overrides: Vec<PathBuf>,
    pub(crate) build_constraints: Vec<PathBuf>,
    pub(crate) isolated: bool,
    pub(crate) show_resolution: bool,
    pub(crate) lfs: GitLfsSetting,
    pub(crate) python: Option<String>,
    pub(crate) python_platform: Option<TargetTriple>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) options: ResolverInstallerOptions,
    pub(crate) settings: ResolverInstallerSettings,
    pub(crate) env_file: Vec<PathBuf>,
    pub(crate) no_env_file: bool,
}

impl ToolRunSettings {
    /// Resolve the [`ToolRunSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: ToolRunArgs,
        filesystem: Option<FilesystemOptions>,
        invocation_source: ToolRunCommand,
        environment: EnvironmentOptions,
    ) -> Self {
        let ToolRunArgs {
            command,
            from,
            with,
            with_editable,
            with_requirements,
            constraints,
            overrides,
            build_constraints,
            isolated,
            env_file,
            no_env_file,
            show_resolution,
            installer,
            build,
            refresh,
            lfs,
            python,
            python_platform,
            torch_backend,
            generate_shell_completion: _,
        } = args;

        // If `--upgrade` was passed explicitly, warn.
        if installer.upgrade || !installer.upgrade_package.is_empty() {
            if with.is_empty() && with_requirements.is_empty() {
                warn_user_once!(
                    "Tools cannot be upgraded via `{invocation_source}`; use `uv tool upgrade --all` to upgrade all installed tools, or `{invocation_source} package@latest` to run the latest version of a tool."
                );
            } else {
                warn_user_once!(
                    "Tools cannot be upgraded via `{invocation_source}`; use `uv tool upgrade --all` to upgrade all installed tools, `{invocation_source} package@latest` to run the latest version of a tool, or `{invocation_source} --refresh package` to upgrade any `--with` dependencies."
                );
            }
        }

        // If `--reinstall` was passed explicitly, warn.
        if installer.reinstall || !installer.reinstall_package.is_empty() {
            if with.is_empty() && with_requirements.is_empty() {
                warn_user_once!(
                    "Tools cannot be reinstalled via `{invocation_source}`; use `uv tool upgrade --all --reinstall` to reinstall all installed tools, `{invocation_source} package@latest` to run the latest version of a tool, or `uv cache prune` to clear any cached tool environments."
                );
            } else {
                warn_user_once!(
                    "Tools cannot be reinstalled via `{invocation_source}`; use `uv tool upgrade --all --reinstall` to reinstall all installed tools, `{invocation_source} package@latest` to run the latest version of a tool, `{invocation_source} --refresh package` to reinstall any `--with` dependencies, or `uv cache prune` to clear any cached tool environments."
                );
            }
        }

        let filesystem_options = filesystem.map(FilesystemOptions::into_options);

        let options = resolver_installer_options_with_environment(
            resolver_installer_options(installer, build),
            &environment,
        )
        .combine(ResolverInstallerOptions::from(
            filesystem_options
                .as_ref()
                .map(|options| options.top_level.clone())
                .unwrap_or_default(),
        ));

        let filesystem_install_mirrors = filesystem_options
            .map(|options| options.install_mirrors.clone())
            .unwrap_or_default();

        let mut settings = ResolverInstallerSettings::from(options.clone());
        if torch_backend.is_some() {
            settings.resolver.torch_backend = torch_backend;
        }
        let lfs = GitLfsSetting::new(lfs.then_some(true), environment.lfs);

        // Resolve flags from CLI and environment variables.
        let isolated = isolated || environment.isolated.value == Some(true);
        let show_resolution = show_resolution || environment.show_resolution.value == Some(true);
        let no_env_file = no_env_file || environment.no_env_file.value == Some(true);

        Self {
            command,
            from,
            with: with
                .into_iter()
                .flat_map(CommaSeparatedRequirements::into_iter)
                .collect(),
            with_editable: with_editable
                .into_iter()
                .flat_map(CommaSeparatedRequirements::into_iter)
                .collect(),
            with_requirements: with_requirements
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            constraints: constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            overrides: overrides
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            build_constraints: build_constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            isolated,
            show_resolution,
            lfs,
            python: python.and_then(Maybe::into_option),
            python_platform,
            refresh: Refresh::from(refresh),
            settings,
            options,
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
            env_file,
            no_env_file,
        }
    }
}

/// The resolved settings to use for a `tool install` invocation.
#[derive(Debug, Clone)]
pub(crate) struct ToolInstallSettings {
    pub(crate) package: String,
    pub(crate) from: Option<String>,
    pub(crate) with: Vec<String>,
    pub(crate) with_requirements: Vec<PathBuf>,
    pub(crate) with_executables_from: Vec<String>,
    pub(crate) with_editable: Vec<String>,
    pub(crate) constraints: Vec<PathBuf>,
    pub(crate) overrides: Vec<PathBuf>,
    pub(crate) excludes: Vec<PathBuf>,
    pub(crate) build_constraints: Vec<PathBuf>,
    pub(crate) lfs: GitLfsSetting,
    pub(crate) python: Option<String>,
    pub(crate) python_platform: Option<TargetTriple>,
    pub(crate) refresh: Refresh,
    pub(crate) options: ResolverInstallerOptions,
    pub(crate) settings: ResolverInstallerSettings,
    pub(crate) force: bool,
    pub(crate) editable: bool,
    pub(crate) install_mirrors: PythonInstallMirrors,
}

impl ToolInstallSettings {
    /// Resolve the [`ToolInstallSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: ToolInstallArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let ToolInstallArgs {
            package,
            editable,
            from,
            with,
            with_editable,
            with_requirements,
            with_executables_from,
            constraints,
            overrides,
            excludes,
            build_constraints,
            lfs,
            installer,
            force,
            build,
            refresh,
            python,
            python_platform,
            torch_backend,
        } = args;

        let filesystem_options = filesystem.map(FilesystemOptions::into_options);

        let options = resolver_installer_options_with_environment(
            resolver_installer_options(installer, build),
            &environment,
        )
        .combine(ResolverInstallerOptions::from(
            filesystem_options
                .as_ref()
                .map(|options| options.top_level.clone())
                .unwrap_or_default(),
        ));

        let filesystem_install_mirrors = filesystem_options
            .map(|options| options.install_mirrors.clone())
            .unwrap_or_default();

        let mut settings = ResolverInstallerSettings::from(options.clone());
        if torch_backend.is_some() {
            settings.resolver.torch_backend = torch_backend;
        }
        let lfs = GitLfsSetting::new(lfs.then_some(true), environment.lfs);

        Self {
            package,
            from,
            with: with
                .into_iter()
                .flat_map(CommaSeparatedRequirements::into_iter)
                .collect(),
            with_editable: with_editable
                .into_iter()
                .flat_map(CommaSeparatedRequirements::into_iter)
                .collect(),
            with_requirements: with_requirements
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            with_executables_from: with_executables_from
                .into_iter()
                .flat_map(CommaSeparatedRequirements::into_iter)
                .collect(),
            constraints: constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            overrides: overrides
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            excludes: excludes
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            build_constraints: build_constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            lfs,
            python: python.and_then(Maybe::into_option),
            python_platform,
            force,
            editable,
            refresh: Refresh::from(refresh),
            options,
            settings,
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
        }
    }
}

/// The resolved settings to use for a `tool upgrade` invocation.
#[derive(Debug, Clone)]
pub(crate) struct ToolUpgradeSettings {
    pub(crate) names: Vec<String>,
    pub(crate) python: Option<String>,
    pub(crate) python_platform: Option<TargetTriple>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) args: ResolverInstallerOptions,
    pub(crate) filesystem: ResolverInstallerOptions,
}
impl ToolUpgradeSettings {
    /// Resolve the [`ToolUpgradeSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: ToolUpgradeArgs,
        filesystem: Option<FilesystemOptions>,
        environment: &EnvironmentOptions,
    ) -> Self {
        let ToolUpgradeArgs {
            name,
            python,
            python_platform,
            upgrade,
            upgrade_package,
            upgrade_group,
            index_args,
            all,
            reinstall,
            no_reinstall,
            reinstall_package,
            index_strategy,
            keyring_provider,
            resolution,
            prerelease,
            pre,
            fork_strategy,
            config_setting,
            config_setting_package: config_settings_package,
            no_build_isolation,
            no_build_isolation_package,
            build_isolation,
            exclude_newer,
            link_mode,
            compile_bytecode,
            no_compile_bytecode,
            no_sources,
            no_sources_package,
            exclude_newer_package,
            build,
        } = args;

        if upgrade {
            warn_user_once!("`--upgrade` is enabled by default on `uv tool upgrade`");
        }
        if !upgrade_package.is_empty() {
            warn_user_once!("`--upgrade-package` is enabled by default on `uv tool upgrade`");
        }

        // Enable `--upgrade` by default.
        let installer = ResolverInstallerArgs {
            index_args,
            upgrade: upgrade_package.is_empty(),
            no_upgrade: false,
            upgrade_package,
            upgrade_group,
            reinstall,
            no_reinstall,
            reinstall_package,
            index_strategy,
            keyring_provider,
            resolution,
            prerelease,
            pre,
            fork_strategy,
            config_setting,
            config_settings_package,
            no_build_isolation,
            no_build_isolation_package,
            build_isolation,
            exclude_newer,
            exclude_newer_package,
            link_mode,
            compile_bytecode,
            no_compile_bytecode,
            no_sources,
            no_sources_package,
        };

        let args = resolver_installer_options_with_environment(
            resolver_installer_options(installer, build),
            environment,
        );
        let filesystem = filesystem.map(FilesystemOptions::into_options);
        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|options| options.install_mirrors)
            .unwrap_or_default();
        let top_level = ResolverInstallerOptions::from(
            filesystem
                .map(|options| options.top_level)
                .unwrap_or_default(),
        );

        Self {
            names: if all { vec![] } else { name },
            python: python.and_then(Maybe::into_option),
            python_platform,
            args,
            filesystem: top_level,
            install_mirrors: environment
                .install_mirrors
                .clone()
                .combine(filesystem_install_mirrors),
        }
    }
}

/// The resolved settings to use for a `tool list` invocation.
#[derive(Debug, Clone)]
pub(crate) struct ToolListSettings {
    pub(crate) show_paths: bool,
    pub(crate) show_version_specifiers: bool,
    pub(crate) show_with: bool,
    pub(crate) show_extras: bool,
    pub(crate) show_python: bool,
    pub(crate) outdated: bool,
    pub(crate) args: ResolverInstallerOptions,
    pub(crate) filesystem: ResolverInstallerOptions,
}

impl ToolListSettings {
    /// Resolve the [`ToolListSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(args: ToolListArgs, filesystem: Option<FilesystemOptions>) -> Self {
        let ToolListArgs {
            show_paths,
            show_version_specifiers,
            show_with,
            show_extras,
            show_python,
            outdated,
            no_outdated,
            exclude_newer,
            python_preference: _,
            no_python_downloads: _,
        } = args;

        let filesystem = filesystem.map(FilesystemOptions::into_options);
        let filesystem = ResolverInstallerOptions {
            exclude_newer: filesystem.and_then(|options| options.top_level.exclude_newer),
            ..ResolverInstallerOptions::default()
        };

        Self {
            show_paths,
            show_version_specifiers,
            show_with,
            show_extras,
            show_python,
            outdated: flag(outdated, no_outdated, "outdated").unwrap_or(false),
            args: ResolverInstallerOptions {
                exclude_newer,
                ..ResolverInstallerOptions::default()
            },
            filesystem,
        }
    }
}

/// The resolved settings to use for a `tool uninstall` invocation.
#[derive(Debug, Clone)]
pub(crate) struct ToolUninstallSettings {
    pub(crate) name: Vec<PackageName>,
}

impl ToolUninstallSettings {
    /// Resolve the [`ToolUninstallSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(args: ToolUninstallArgs, _filesystem: Option<FilesystemOptions>) -> Self {
        let ToolUninstallArgs { name, all } = args;

        Self {
            name: if all { vec![] } else { name },
        }
    }
}

/// The resolved settings to use for a `tool dir` invocation.
#[derive(Debug, Clone)]
pub(crate) struct ToolDirSettings {
    pub(crate) bin: bool,
}

impl ToolDirSettings {
    /// Resolve the [`ToolDirSettings`] from the CLI and filesystem configuration.
    #[expect(clippy::needless_pass_by_value)]
    pub(crate) fn resolve(args: ToolDirArgs, _filesystem: Option<FilesystemOptions>) -> Self {
        let ToolDirArgs { bin } = args;

        Self { bin }
    }
}

#[derive(Debug, Clone, Default)]
pub(crate) enum PythonListKinds {
    #[default]
    Default,
    /// Only list version downloads.
    Downloads,
    /// Only list installed versions.
    Installed,
}

/// The resolved settings to use for a `tool run` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PythonListSettings {
    pub(crate) request: Option<String>,
    pub(crate) kinds: PythonListKinds,
    pub(crate) all_platforms: bool,
    pub(crate) all_arches: bool,
    pub(crate) all_versions: bool,
    pub(crate) show_urls: bool,
    pub(crate) output_format: PythonListFormat,
    pub(crate) python_downloads_json_url: Option<String>,
    pub(crate) python_install_mirror: Option<String>,
    pub(crate) pypy_install_mirror: Option<String>,
}

impl PythonListSettings {
    /// Resolve the [`PythonListSettings`] from the CLI and filesystem configuration.
    #[expect(clippy::needless_pass_by_value)]
    pub(crate) fn resolve(
        args: PythonListArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PythonListArgs {
            request,
            all_versions,
            all_platforms,
            all_arches,
            only_installed,
            only_downloads,
            show_urls,
            output_format,
            python_downloads_json_url: python_downloads_json_url_arg,
        } = args;

        let options = filesystem.map(FilesystemOptions::into_options);
        let (
            python_downloads_json_url_option,
            python_install_mirror_option,
            pypy_install_mirror_option,
        ) = match &options {
            Some(options) => (
                options.install_mirrors.python_downloads_json_url.clone(),
                options.install_mirrors.python_install_mirror.clone(),
                options.install_mirrors.pypy_install_mirror.clone(),
            ),
            None => (None, None, None),
        };

        let python_downloads_json_url = python_downloads_json_url_arg
            .or(environment
                .install_mirrors
                .python_downloads_json_url
                .clone())
            .or(python_downloads_json_url_option);

        let python_install_mirror = environment
            .install_mirrors
            .python_install_mirror
            .clone()
            .or(python_install_mirror_option);

        let pypy_install_mirror = environment
            .install_mirrors
            .pypy_install_mirror
            .clone()
            .or(pypy_install_mirror_option);

        let kinds = if only_installed {
            PythonListKinds::Installed
        } else if only_downloads {
            PythonListKinds::Downloads
        } else {
            PythonListKinds::default()
        };

        Self {
            request,
            kinds,
            all_platforms,
            all_arches,
            all_versions,
            show_urls,
            output_format,
            python_downloads_json_url,
            python_install_mirror,
            pypy_install_mirror,
        }
    }
}

/// The resolved settings to use for a `python dir` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PythonDirSettings {
    pub(crate) bin: bool,
}

impl PythonDirSettings {
    /// Resolve the [`PythonDirSettings`] from the CLI and filesystem configuration.
    #[expect(clippy::needless_pass_by_value)]
    pub(crate) fn resolve(args: PythonDirArgs, _filesystem: Option<FilesystemOptions>) -> Self {
        let PythonDirArgs { bin } = args;

        Self { bin }
    }
}

/// The resolved settings to use for a `python install` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PythonInstallSettings {
    pub(crate) install_dir: Option<PathBuf>,
    pub(crate) targets: Vec<String>,
    pub(crate) reinstall: bool,
    pub(crate) force: bool,
    pub(crate) upgrade: PythonUpgrade,
    pub(crate) bin: Option<bool>,
    pub(crate) registry: Option<bool>,
    pub(crate) python_install_mirror: Option<String>,
    pub(crate) pypy_install_mirror: Option<String>,
    pub(crate) python_downloads_json_url: Option<String>,
    pub(crate) default: bool,
    pub(crate) compile_bytecode: bool,
}

impl PythonInstallSettings {
    /// Resolve the [`PythonInstallSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PythonInstallArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let filesystem_install_mirrors = filesystem
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let install_mirrors = args
            .install_mirrors()
            .combine(environment.install_mirrors)
            .combine(filesystem_install_mirrors);

        let PythonInstallMirrors {
            python_install_mirror,
            pypy_install_mirror,
            python_downloads_json_url,
        } = install_mirrors;

        let PythonInstallArgs {
            install_dir,
            targets,
            reinstall,
            bin,
            no_bin,
            registry,
            no_registry,
            force,
            upgrade,
            mirror: _,
            pypy_mirror: _,
            python_downloads_json_url: _,
            default,
            compile_bytecode,
        } = args;

        Self {
            install_dir,
            targets,
            reinstall,
            force,
            upgrade: if upgrade {
                PythonUpgrade::Enabled(PythonUpgradeSource::Install)
            } else {
                PythonUpgrade::Disabled
            },
            bin: flag(bin, no_bin, "bin").or(environment.python_install_bin),
            registry: match flag(registry, no_registry, "registry") {
                Some(registry) => Some(registry),
                None => environment.python_install_registry.or(
                    if environment.python_no_registry.value == Some(true) {
                        Some(false)
                    } else {
                        None
                    },
                ),
            },
            python_install_mirror,
            pypy_install_mirror,
            python_downloads_json_url,
            default,
            compile_bytecode: flag(
                compile_bytecode.compile_bytecode,
                compile_bytecode.no_compile_bytecode,
                "compile-bytecode",
            )
            .unwrap_or_default(),
        }
    }
}

/// The resolved settings to use for a `python upgrade` invocation.
#[expect(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)]
pub(crate) struct PythonUpgradeSettings {
    pub(crate) install_dir: Option<PathBuf>,
    pub(crate) targets: Vec<String>,
    pub(crate) force: bool,
    pub(crate) registry: Option<bool>,
    pub(crate) python_install_mirror: Option<String>,
    pub(crate) pypy_install_mirror: Option<String>,
    pub(crate) reinstall: bool,
    pub(crate) python_downloads_json_url: Option<String>,
    pub(crate) default: bool,
    pub(crate) bin: Option<bool>,
    pub(crate) compile_bytecode: bool,
}

impl PythonUpgradeSettings {
    /// Resolve the [`PythonUpgradeSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PythonUpgradeArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let filesystem_install_mirrors = filesystem
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let install_mirrors = args
            .install_mirrors()
            .combine(environment.install_mirrors)
            .combine(filesystem_install_mirrors);

        let PythonInstallMirrors {
            python_install_mirror,
            pypy_install_mirror,
            python_downloads_json_url,
        } = install_mirrors;

        let force = false;
        let default = false;
        let bin = None;
        let registry = environment.python_install_registry.or(
            if environment.python_no_registry.value == Some(true) {
                Some(false)
            } else {
                None
            },
        );

        let PythonUpgradeArgs {
            install_dir,
            targets,
            mirror: _,
            pypy_mirror: _,
            reinstall,
            python_downloads_json_url: _,
            compile_bytecode,
        } = args;

        Self {
            install_dir,
            targets,
            force,
            registry,
            python_install_mirror,
            pypy_install_mirror,
            reinstall,
            python_downloads_json_url,
            default,
            bin,
            compile_bytecode: flag(
                compile_bytecode.compile_bytecode,
                compile_bytecode.no_compile_bytecode,
                "compile-bytecode",
            )
            .unwrap_or_default(),
        }
    }
}

/// The resolved settings to use for a `python uninstall` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PythonUninstallSettings {
    pub(crate) install_dir: Option<PathBuf>,
    pub(crate) targets: Vec<String>,
    pub(crate) all: bool,
}

impl PythonUninstallSettings {
    /// Resolve the [`PythonUninstallSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PythonUninstallArgs,
        _filesystem: Option<FilesystemOptions>,
    ) -> Self {
        let PythonUninstallArgs {
            install_dir,
            targets,
            all,
        } = args;

        Self {
            install_dir,
            targets,
            all,
        }
    }
}

/// The resolved settings to use for a `python find` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PythonFindSettings {
    pub(crate) request: Option<String>,
    pub(crate) show_version: bool,
    pub(crate) resolve_links: bool,
    pub(crate) no_project: bool,
    pub(crate) system: bool,
    pub(crate) python_downloads_json_url: Option<String>,
}

impl PythonFindSettings {
    /// Resolve the [`PythonFindSettings`] from the CLI and workspace configuration.
    pub(crate) fn resolve(
        args: PythonFindArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PythonFindArgs {
            request,
            show_version,
            resolve_links,
            no_project,
            system,
            no_system,
            script: _,
            python_downloads_json_url,
        } = args;

        let filesystem_install_mirrors = filesystem
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let install_mirrors = PythonInstallMirrors {
            python_downloads_json_url,
            ..Default::default()
        }
        .combine(environment.install_mirrors)
        .combine(filesystem_install_mirrors);

        let PythonInstallMirrors {
            python_install_mirror: _,
            pypy_install_mirror: _,
            python_downloads_json_url,
        } = install_mirrors;

        Self {
            request,
            show_version,
            resolve_links,
            no_project,
            system: flag(system, no_system, "system").unwrap_or_default(),
            python_downloads_json_url,
        }
    }
}

/// The resolved settings to use for a `python pin` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PythonPinSettings {
    pub(crate) request: Option<String>,
    pub(crate) resolved: bool,
    pub(crate) no_project: bool,
    pub(crate) global: bool,
    pub(crate) rm: bool,
    pub(crate) install_mirrors: PythonInstallMirrors,
}

impl PythonPinSettings {
    /// Resolve the [`PythonPinSettings`] from the CLI and workspace configuration.
    pub(crate) fn resolve(
        args: PythonPinArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PythonPinArgs {
            request,
            no_resolved,
            resolved,
            no_project,
            global,
            rm,
            python_downloads_json_url,
        } = args;

        let filesystem_install_mirrors = filesystem
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let install_mirrors = PythonInstallMirrors {
            python_downloads_json_url,
            ..Default::default()
        }
        .combine(environment.install_mirrors)
        .combine(filesystem_install_mirrors);

        Self {
            request,
            resolved: flag(resolved, no_resolved, "resolved").unwrap_or(false),
            no_project,
            global,
            rm,
            install_mirrors,
        }
    }
}

/// The resolved settings to use for a `sync` invocation.
#[expect(dead_code)]
#[derive(Debug, Clone)]
pub(crate) struct SyncSettings {
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) dry_run: DryRun,
    pub(crate) script: Option<PathBuf>,
    pub(crate) active: Option<bool>,
    pub(crate) extras: ExtrasSpecification,
    pub(crate) groups: DependencyGroups,
    pub(crate) editable: Option<EditableMode>,
    pub(crate) install_options: InstallOptions,
    pub(crate) modifications: Modifications,
    pub(crate) all_packages: bool,
    pub(crate) package: Vec<PackageName>,
    pub(crate) python: Option<String>,
    pub(crate) python_platform: Option<TargetTriple>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) settings: ResolverInstallerSettings,
    pub(crate) output_format: SyncFormat,
    pub(crate) malware_settings: MalwareCheckSettings,
}

impl SyncSettings {
    /// Resolve the [`SyncSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: SyncArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let SyncArgs {
            extra,
            all_extras,
            no_extra,
            no_all_extras,
            dev,
            no_dev,
            only_dev,
            group,
            no_group,
            no_default_groups,
            only_group,
            all_groups,
            editable,
            no_editable,
            no_editable_package,
            inexact,
            exact,
            no_install_project,
            only_install_project,
            no_install_workspace,
            only_install_workspace,
            no_install_local,
            only_install_local,
            no_install_package,
            only_install_package,
            locked,
            frozen,
            active,
            no_active,
            dry_run,
            installer,
            build,
            refresh,
            all_packages,
            package,
            script,
            python,
            python_platform,
            check,
            no_check,
            output_format,
        } = args;
        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let settings = ResolverInstallerSettings::combine(
            resolver_installer_options(installer, build),
            filesystem,
            &environment,
        );

        let check = flag(check, no_check, "check").unwrap_or_default();
        let dry_run = if check {
            DryRun::Check
        } else {
            DryRun::from_args(dry_run)
        };

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen, "frozen", environment.frozen);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        let (dev, no_dev) = resolve_flag_pair(
            dev,
            no_dev,
            "dev",
            "no-dev",
            Some(environment.dev),
            Some(environment.no_dev),
        );
        let (editable, no_editable) = resolve_flag_pair(
            editable,
            no_editable,
            "editable",
            "no-editable",
            None,
            Some(environment.no_editable),
        );

        let malware_settings = MalwareCheckSettings::from(&environment);

        Self {
            output_format,
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            dry_run,
            script,
            active: flag(active, no_active, "active"),
            extras: ExtrasSpecification::from_args(
                extra.unwrap_or_default(),
                no_extra,
                // TODO(blueraft): support no_default_extras
                false,
                // TODO(blueraft): support only_extra
                vec![],
                flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
            ),
            groups: DependencyGroups::from_args(
                dev.into(),
                no_dev.into(),
                only_dev,
                group,
                if no_group.is_empty() {
                    environment.no_group.clone().unwrap_or_default()
                } else {
                    no_group
                },
                no_default_groups,
                only_group,
                all_groups,
            ),
            editable: EditableMode::from_args(
                flag(editable.into(), no_editable.into(), "editable"),
                no_editable_package,
            ),
            install_options: InstallOptions::new(
                no_install_project,
                only_install_project,
                no_install_workspace,
                only_install_workspace,
                no_install_local,
                only_install_local,
                no_install_package,
                only_install_package,
            ),
            modifications: if flag(exact, inexact, "inexact").unwrap_or(true) {
                Modifications::Exact
            } else {
                Modifications::Sufficient
            },
            all_packages,
            package,
            python: python.and_then(Maybe::into_option),
            python_platform,
            refresh: Refresh::from(refresh),
            settings,
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
            malware_settings,
        }
    }
}

/// The resolved settings to use for a `lock` invocation.
#[derive(Debug, Clone)]
pub(crate) struct LockSettings {
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) dry_run: DryRun,
    pub(crate) script: Option<PathBuf>,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) settings: ResolverSettings,
}

impl LockSettings {
    /// Resolve the [`LockSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: LockArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let LockArgs {
            check,
            locked,
            check_exists,
            dry_run,
            script,
            resolver,
            build,
            refresh,
            python,
        } = args;

        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(check_exists, "frozen", environment.frozen);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        let lock_check = if check {
            LockCheck::Enabled(LockCheckSource::Check)
        } else {
            resolve_lock_check(locked)
        };

        Self {
            lock_check,
            frozen: resolve_frozen(frozen),
            dry_run: DryRun::from_args(dry_run),
            script,
            python: python.and_then(Maybe::into_option),
            refresh: Refresh::from(refresh),
            settings: ResolverSettings::combine(
                resolver_options(resolver, build),
                filesystem,
                &environment,
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
        }
    }
}
/// The resolved settings to use for a `lock` invocation.
#[derive(Debug, Clone)]
pub(crate) struct MetadataSettings {
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) dry_run: DryRun,
    pub(crate) sync: bool,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) settings: ResolverSettings,
    pub(crate) malware_settings: MalwareCheckSettings,
}

impl MetadataSettings {
    /// Resolve the [`LockSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: Box<MetadataArgs>,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let MetadataArgs {
            locked,
            frozen,
            dry_run,
            resolver,
            build,
            refresh,
            sync,
            python,
        } = *args;

        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen, "frozen", environment.frozen);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        let malware_settings = MalwareCheckSettings::from(&environment);

        Self {
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            dry_run: DryRun::from_args(dry_run),
            sync,
            python: python.and_then(Maybe::into_option),
            refresh: Refresh::from(refresh),
            settings: ResolverSettings::combine(
                resolver_options(resolver, build),
                filesystem,
                &environment,
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
            malware_settings,
        }
    }
}

/// The resolved settings to use for a `add` invocation.
#[expect(clippy::struct_excessive_bools)]
#[derive(Debug, Clone)]
pub(crate) struct AddSettings {
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) active: Option<bool>,
    pub(crate) no_sync: bool,
    pub(crate) packages: Vec<String>,
    pub(crate) requirements: Vec<PathBuf>,
    pub(crate) constraints: Vec<PathBuf>,
    pub(crate) marker: Option<MarkerTree>,
    pub(crate) dependency_type: DependencyType,
    pub(crate) editable: Option<EditableMode>,
    pub(crate) extras: Vec<ExtraName>,
    pub(crate) raw: bool,
    pub(crate) bounds: Option<AddBoundsKind>,
    pub(crate) rev: Option<String>,
    pub(crate) tag: Option<String>,
    pub(crate) branch: Option<String>,
    pub(crate) lfs: GitLfsSetting,
    pub(crate) package: Option<PackageName>,
    pub(crate) script: Option<PathBuf>,
    pub(crate) python: Option<String>,
    pub(crate) workspace: Option<bool>,
    pub(crate) no_install_project: bool,
    pub(crate) only_install_project: bool,
    pub(crate) no_install_workspace: bool,
    pub(crate) only_install_workspace: bool,
    pub(crate) no_install_local: bool,
    pub(crate) only_install_local: bool,
    pub(crate) no_install_package: Vec<PackageName>,
    pub(crate) only_install_package: Vec<PackageName>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) indexes: Vec<Index>,
    pub(crate) settings: ResolverInstallerSettings,
    pub(crate) malware_settings: MalwareCheckSettings,
}

impl AddSettings {
    /// Resolve the [`AddSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: AddArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let AddArgs {
            packages,
            requirements,
            constraints,
            marker,
            dev,
            optional,
            group,
            editable,
            no_editable,
            no_editable_package,
            extra,
            raw,
            bounds,
            rev,
            tag,
            branch,
            lfs,
            no_sync,
            locked,
            frozen,
            active,
            no_active,
            installer,
            build,
            refresh,
            package,
            script,
            python,
            workspace,
            no_workspace,
            no_install_project,
            only_install_project,
            no_install_workspace,
            only_install_workspace,
            no_install_local,
            only_install_local,
            no_install_package,
            only_install_package,
        } = args;

        // Resolve flags from CLI and environment variables.
        let dev = dev || environment.dev.value == Some(true);
        let (editable, no_editable) = resolve_flag_pair(
            editable,
            no_editable,
            "editable",
            "no-editable",
            None,
            Some(environment.no_editable),
        );

        let dependency_type = if let Some(extra) = optional {
            DependencyType::Optional(extra)
        } else if let Some(group) = group {
            DependencyType::Group(group)
        } else if dev {
            DependencyType::Dev
        } else {
            DependencyType::Production
        };

        // Track the `--index` and `--default-index` arguments from the command-line.
        let indexes = installer
            .index_args
            .default_index
            .clone()
            .and_then(Maybe::into_option)
            .into_iter()
            .chain(
                installer
                    .index_args
                    .index
                    .clone()
                    .into_iter()
                    .flat_map(|v| v.clone())
                    .flatten()
                    .filter_map(Maybe::into_option),
            )
            .collect::<Vec<_>>();

        // Warn user if an ambiguous relative path was passed as a value for
        // `--index` or `--default-index`.
        for index in &indexes {
            index.url().warn_on_disambiguated_relative_path();
        }

        // If the user passed an `--index-url` or `--extra-index-url`, warn.
        if installer
            .index_args
            .index_url
            .as_ref()
            .is_some_and(Maybe::is_some)
        {
            if script.is_some() {
                warn_user_once!(
                    "Indexes specified via `--index-url` will not be persisted to the script; use `--default-index` instead."
                );
            } else {
                warn_user_once!(
                    "Indexes specified via `--index-url` will not be persisted to the `pyproject.toml` file; use `--default-index` instead."
                );
            }
        }

        if installer
            .index_args
            .extra_index_url
            .as_ref()
            .is_some_and(|extra_index_url| extra_index_url.iter().any(Maybe::is_some))
        {
            if script.is_some() {
                warn_user_once!(
                    "Indexes specified via `--extra-index-url` will not be persisted to the script; use `--index` instead."
                );
            } else {
                warn_user_once!(
                    "Indexes specified via `--extra-index-url` will not be persisted to the `pyproject.toml` file; use `--index` instead."
                );
            }
        }

        let filesystem_install_mirrors = filesystem
            .as_ref()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let bounds = bounds.or(filesystem.as_ref().and_then(|fs| fs.add.add_bounds));
        let lfs = GitLfsSetting::new(lfs.then_some(true), environment.lfs);

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen, "frozen", environment.frozen);
        let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        // Check for conflicts between no_sync and frozen.
        check_conflicts(no_sync, frozen);

        let malware_settings = MalwareCheckSettings::from(&environment);

        Self {
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            active: flag(active, no_active, "active"),
            no_sync: no_sync.is_enabled(),
            packages,
            requirements,
            constraints: constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            marker,
            dependency_type,
            raw,
            bounds,
            rev,
            tag,
            branch,
            lfs,
            package,
            script,
            python: python.and_then(Maybe::into_option),
            workspace: flag(workspace, no_workspace, "workspace"),
            no_install_project,
            only_install_project,
            no_install_workspace,
            only_install_workspace,
            no_install_local,
            only_install_local,
            no_install_package,
            only_install_package,
            editable: EditableMode::from_args(
                flag(editable.into(), no_editable.into(), "editable"),
                no_editable_package,
            ),
            extras: extra.unwrap_or_default(),
            refresh: Refresh::from(refresh),
            indexes,
            settings: ResolverInstallerSettings::combine(
                resolver_installer_options(installer, build),
                filesystem,
                &environment,
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
            malware_settings,
        }
    }
}

/// The resolved settings to use for a `remove` invocation.
#[expect(dead_code)]
#[derive(Debug, Clone)]
pub(crate) struct RemoveSettings {
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) active: Option<bool>,
    pub(crate) no_sync: bool,
    pub(crate) packages: Vec<PackageName>,
    pub(crate) dependency_type: DependencyType,
    pub(crate) package: Option<PackageName>,
    pub(crate) script: Option<PathBuf>,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) settings: ResolverInstallerSettings,
    pub(crate) malware_settings: MalwareCheckSettings,
}

impl RemoveSettings {
    /// Resolve the [`RemoveSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: RemoveArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let RemoveArgs {
            dev,
            optional,
            packages,
            group,
            no_sync,
            locked,
            frozen,
            active,
            no_active,
            installer,
            build,
            refresh,
            package,
            script,
            python,
        } = args;

        // Resolve flags from CLI and environment variables.
        let dev = dev || environment.dev.value == Some(true);

        let dependency_type = if let Some(extra) = optional {
            DependencyType::Optional(extra)
        } else if let Some(group) = group {
            DependencyType::Group(group)
        } else if dev {
            DependencyType::Dev
        } else {
            DependencyType::Production
        };

        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let packages = packages
            .into_iter()
            .map(|requirement| requirement.name)
            .collect();

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen, "frozen", environment.frozen);
        let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        // Check for conflicts between no_sync and frozen.
        check_conflicts(no_sync, frozen);

        let malware_settings = MalwareCheckSettings::from(&environment);

        Self {
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            active: flag(active, no_active, "active"),
            no_sync: no_sync.is_enabled(),
            packages,
            dependency_type,
            package,
            script,
            python: python.and_then(Maybe::into_option),
            refresh: Refresh::from(refresh),
            settings: ResolverInstallerSettings::combine(
                resolver_installer_options(installer, build),
                filesystem,
                &environment,
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
            malware_settings,
        }
    }
}

/// The resolved settings to use for a `version` invocation.
#[derive(Debug, Clone)]
pub(crate) struct VersionSettings {
    pub(crate) value: Option<String>,
    pub(crate) bump: Vec<VersionBumpSpec>,
    pub(crate) short: bool,
    pub(crate) output_format: VersionFormat,
    pub(crate) dry_run: bool,
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) active: Option<bool>,
    pub(crate) no_sync: bool,
    pub(crate) package: Option<PackageName>,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) settings: ResolverInstallerSettings,
    pub(crate) malware_settings: MalwareCheckSettings,
}

impl VersionSettings {
    /// Resolve the [`RemoveSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: VersionArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let VersionArgs {
            value,
            bump,
            short,
            output_format,
            dry_run,
            no_sync,
            locked,
            frozen,
            active,
            no_active,
            installer,
            build,
            refresh,
            package,
            python,
        } = args;

        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen, "frozen", environment.frozen);
        let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        // Check for conflicts between no_sync and frozen.
        check_conflicts(no_sync, frozen);

        let malware_settings = MalwareCheckSettings::from(&environment);

        Self {
            value,
            bump,
            short,
            output_format,
            dry_run,
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            active: flag(active, no_active, "active"),
            no_sync: no_sync.is_enabled(),
            package,
            python: python.and_then(Maybe::into_option),
            refresh: Refresh::from(refresh),
            settings: ResolverInstallerSettings::combine(
                resolver_installer_options(installer, build),
                filesystem,
                &environment,
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
            malware_settings,
        }
    }
}

/// The resolved settings to use for a `tree` invocation.
#[derive(Debug, Clone)]
pub(crate) struct TreeSettings {
    pub(crate) groups: DependencyGroups,
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) universal: bool,
    pub(crate) depth: u8,
    pub(crate) prune: Vec<PackageName>,
    pub(crate) package: Vec<PackageName>,
    pub(crate) no_dedupe: bool,
    pub(crate) invert: bool,
    pub(crate) outdated: bool,
    pub(crate) show_sizes: bool,
    #[allow(dead_code)]
    pub(crate) script: Option<PathBuf>,
    pub(crate) python_version: Option<PythonVersion>,
    pub(crate) python_platform: Option<TargetTriple>,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) resolver: ResolverSettings,
}

impl TreeSettings {
    /// Resolve the [`TreeSettings`] from the CLI and workspace configuration.
    pub(crate) fn resolve(
        args: TreeArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let TreeArgs {
            tree,
            universal,
            dev,
            only_dev,
            no_dev,
            group,
            no_group,
            no_default_groups,
            only_group,
            all_groups,
            locked,
            frozen,
            build,
            resolver,
            script,
            python_version,
            python_platform,
            python,
        } = args;

        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen, "frozen", environment.frozen);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        let (dev, no_dev) = resolve_flag_pair(
            dev,
            no_dev,
            "dev",
            "no-dev",
            Some(environment.dev),
            Some(environment.no_dev),
        );

        Self {
            groups: DependencyGroups::from_args(
                dev.into(),
                no_dev.into(),
                only_dev,
                group,
                if no_group.is_empty() {
                    environment.no_group.clone().unwrap_or_default()
                } else {
                    no_group
                },
                no_default_groups,
                only_group,
                all_groups,
            ),
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            universal,
            depth: tree.depth,
            prune: tree.prune,
            package: tree.package,
            no_dedupe: tree.no_dedupe,
            invert: tree.invert,
            outdated: tree.outdated,
            show_sizes: tree.show_sizes,
            script,
            python_version,
            python_platform,
            python: python.and_then(Maybe::into_option),
            resolver: ResolverSettings::combine(
                resolver_options(resolver, build),
                filesystem,
                &environment,
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
        }
    }
}

/// The resolved settings to use for an `export` invocation.
#[expect(clippy::struct_excessive_bools, dead_code)]
#[derive(Debug, Clone)]
pub(crate) struct ExportSettings {
    pub(crate) format: Option<ExportFormat>,
    pub(crate) all_packages: bool,
    pub(crate) package: Vec<PackageName>,
    pub(crate) prune: Vec<PackageName>,
    pub(crate) extras: ExtrasSpecification,
    pub(crate) groups: DependencyGroups,
    pub(crate) editable: Option<EditableMode>,
    pub(crate) hashes: bool,
    pub(crate) install_options: InstallOptions,
    pub(crate) output_file: Option<PathBuf>,
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) include_annotations: bool,
    pub(crate) include_header: bool,
    pub(crate) script: Option<PathBuf>,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) settings: ResolverSettings,
}

impl ExportSettings {
    /// Resolve the [`ExportSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: ExportArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let ExportArgs {
            format,
            all_packages,
            package,
            prune,
            extra,
            all_extras,
            no_extra,
            no_all_extras,
            dev,
            no_dev,
            only_dev,
            group,
            no_group,
            no_default_groups,
            only_group,
            all_groups,
            annotate,
            no_annotate,
            header,
            no_header,
            editable,
            no_editable,
            no_editable_package,
            hashes,
            no_hashes,
            output_file,
            no_emit_project,
            only_emit_project,
            no_emit_workspace,
            only_emit_workspace,
            no_emit_local,
            only_emit_local,
            no_emit_package,
            only_emit_package,
            locked,
            frozen: frozen_cli,
            resolver,
            build,
            refresh,
            script,
            python,
        } = args;
        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen_cli, "frozen", environment.frozen);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        let (dev, no_dev) = resolve_flag_pair(
            dev,
            no_dev,
            "dev",
            "no-dev",
            Some(environment.dev),
            Some(environment.no_dev),
        );
        let (editable, no_editable) = resolve_flag_pair(
            editable,
            no_editable,
            "editable",
            "no-editable",
            None,
            Some(environment.no_editable),
        );

        Self {
            format,
            all_packages,
            package,
            prune,
            extras: ExtrasSpecification::from_args(
                extra.unwrap_or_default(),
                no_extra,
                // TODO(blueraft): support no_default_extras
                false,
                // TODO(blueraft): support only_extra
                vec![],
                flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
            ),
            groups: DependencyGroups::from_args(
                dev.into(),
                no_dev.into(),
                only_dev,
                group,
                if no_group.is_empty() {
                    environment.no_group.clone().unwrap_or_default()
                } else {
                    no_group
                },
                no_default_groups,
                only_group,
                all_groups,
            ),
            editable: EditableMode::from_args(
                flag(editable.into(), no_editable.into(), "editable"),
                no_editable_package,
            ),
            hashes: flag(hashes, no_hashes, "hashes").unwrap_or(true),
            install_options: InstallOptions::new(
                no_emit_project,
                only_emit_project,
                no_emit_workspace,
                only_emit_workspace,
                no_emit_local,
                only_emit_local,
                no_emit_package,
                only_emit_package,
            ),
            output_file,
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            include_annotations: flag(annotate, no_annotate, "annotate").unwrap_or(true),
            include_header: flag(header, no_header, "header").unwrap_or(true),
            script,
            python: python.and_then(Maybe::into_option),
            refresh: Refresh::from(refresh),
            settings: ResolverSettings::combine(
                resolver_options(resolver, build),
                filesystem,
                &environment,
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
        }
    }
}

/// The resolved settings to use for a `format` invocation.
#[derive(Debug, Clone)]
pub(crate) struct FormatSettings {
    pub(crate) check: bool,
    pub(crate) diff: bool,
    pub(crate) extra_args: Vec<String>,
    pub(crate) version: Option<String>,
    pub(crate) exclude_newer: Option<jiff::Timestamp>,
    pub(crate) no_project: bool,
    pub(crate) show_version: bool,
}

impl FormatSettings {
    /// Resolve the [`FormatSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(args: FormatArgs, _filesystem: Option<FilesystemOptions>) -> Self {
        let FormatArgs {
            check,
            diff,
            extra_args,
            version,
            exclude_newer,
            no_project,
            show_version,
        } = args;

        Self {
            check,
            diff,
            extra_args,
            version,
            exclude_newer: exclude_newer.map(|v| v.timestamp()),
            no_project,
            show_version,
        }
    }
}

/// The resolved settings to use for a `check` invocation.
#[derive(Debug, Clone)]
pub(crate) struct CheckSettings {
    pub(crate) extras: ExtrasSpecification,
    pub(crate) groups: DependencyGroups,
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) no_sync: bool,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) settings: ResolverInstallerSettings,
    pub(crate) ty_version: Option<String>,
    pub(crate) no_project: bool,
    pub(crate) malware_settings: MalwareCheckSettings,
}

impl CheckSettings {
    /// Resolve the [`CheckSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: CheckArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let CheckArgs {
            extra,
            all_extras,
            no_extra,
            no_all_extras,
            dev,
            no_dev,
            only_dev,
            group,
            no_group,
            no_default_groups,
            only_group,
            all_groups,
            locked,
            frozen,
            no_sync,
            python,
            ty_version,
            no_project,
            installer,
            build,
            refresh,
        } = args;

        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen, "frozen", environment.frozen);
        let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);
        check_conflicts(locked, frozen);

        let (dev, no_dev) = resolve_flag_pair(
            dev,
            no_dev,
            "dev",
            "no-dev",
            Some(environment.dev),
            Some(environment.no_dev),
        );
        let settings = ResolverInstallerSettings::combine(
            resolver_installer_options(installer, build),
            filesystem,
            &environment,
        );
        let malware_settings = MalwareCheckSettings::from(&environment);

        Self {
            extras: ExtrasSpecification::from_args(
                extra.unwrap_or_default(),
                no_extra,
                false,
                vec![],
                flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
            ),
            groups: DependencyGroups::from_args(
                dev.into(),
                no_dev.into(),
                only_dev,
                group,
                if no_group.is_empty() {
                    environment.no_group.clone().unwrap_or_default()
                } else {
                    no_group
                },
                no_default_groups,
                only_group,
                all_groups,
            ),
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            no_sync: no_sync.is_enabled(),
            python: python.and_then(Maybe::into_option),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
            refresh: Refresh::from(refresh),
            settings,
            ty_version,
            no_project,
            malware_settings,
        }
    }
}

/// The resolved settings to use for an `audit` invocation.
#[derive(Debug, Clone)]
pub(crate) struct AuditSettings {
    pub(crate) extras: ExtrasSpecification,
    pub(crate) groups: DependencyGroups,
    pub(crate) lock_check: LockCheck,
    pub(crate) frozen: Option<FrozenSource>,
    pub(crate) python_version: Option<PythonVersion>,
    pub(crate) python_platform: Option<TargetTriple>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) settings: ResolverSettings,
    pub(crate) output_format: AuditOutputFormat,
    pub(crate) service_format: VulnerabilityServiceFormat,
    pub(crate) service_url: Option<String>,
    pub(crate) ignore: Vec<VulnerabilityID>,
    pub(crate) ignore_until_fixed: Vec<VulnerabilityID>,
}

impl AuditSettings {
    /// Resolve the [`AuditSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: AuditArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let AuditArgs {
            no_extra,
            no_dev,
            no_group,
            no_default_groups,
            only_group,
            only_dev,
            script: _,
            python_version,
            python_platform,
            locked,
            frozen,
            output_format,
            build,
            resolver,
            ignore,
            ignore_until_fixed,
            service_format,
            service_url,
        } = args;

        let filesystem_install_mirrors = filesystem
            .clone()
            .map(|fs| fs.install_mirrors.clone())
            .unwrap_or_default();

        let filesystem_audit = filesystem
            .as_ref()
            .and_then(|fs| fs.audit.clone())
            .unwrap_or_default();

        let no_dev = no_dev || environment.no_dev.value == Some(true);

        // Resolve flags from CLI and environment variables.
        let locked = resolve_flag(locked, "locked", environment.locked);
        let frozen = resolve_flag(frozen, "frozen", environment.frozen);

        // Check for conflicts between locked and frozen.
        check_conflicts(locked, frozen);

        Self {
            extras: ExtrasSpecification::from_args(
                vec![],
                no_extra,
                // TODO(ww): support no_default_extras?
                false,
                // TODO(ww): support only_extra?
                vec![],
                true,
            ),
            groups: DependencyGroups::from_args(
                only_group.is_empty() && !only_dev,
                no_dev,
                only_dev,
                vec![],
                if no_group.is_empty() {
                    environment.no_group.clone().unwrap_or_default()
                } else {
                    no_group
                },
                no_default_groups,
                only_group.clone(),
                only_group.is_empty() && !only_dev,
            ),
            lock_check: resolve_lock_check(locked),
            frozen: resolve_frozen(frozen),
            python_version,
            python_platform,
            settings: ResolverSettings::combine(
                resolver_options(resolver, build),
                filesystem,
                &environment,
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
            output_format,
            service_format,
            service_url,
            ignore: {
                let config_ignore = filesystem_audit.ignore.unwrap_or_default();
                let mut merged = ignore;
                merged.extend(config_ignore);
                merged.into_iter().map(VulnerabilityID::new).collect()
            },
            ignore_until_fixed: {
                let config_ignore_until_fixed =
                    filesystem_audit.ignore_until_fixed.unwrap_or_default();
                let mut merged = ignore_until_fixed;
                merged.extend(config_ignore_until_fixed);
                merged.into_iter().map(VulnerabilityID::new).collect()
            },
        }
    }
}

/// The resolved settings to use for a `pip compile` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PipCompileSettings {
    pub(crate) format: Option<PipCompileFormat>,
    pub(crate) src_file: Vec<PathBuf>,
    pub(crate) constraints: Vec<PathBuf>,
    pub(crate) overrides: Vec<PathBuf>,
    pub(crate) excludes: Vec<PathBuf>,
    pub(crate) build_constraints: Vec<PathBuf>,
    pub(crate) constraints_from_workspace: Vec<Requirement>,
    pub(crate) overrides_from_workspace: Vec<Requirement>,
    pub(crate) excludes_from_workspace: Vec<PackageName>,
    pub(crate) build_constraints_from_workspace: Vec<Requirement>,
    pub(crate) environments: SupportedEnvironments,
    pub(crate) required_environments: SupportedEnvironments,
    pub(crate) refresh: Refresh,
    pub(crate) settings: PipSettings,
}

impl PipCompileSettings {
    /// Resolve the [`PipCompileSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PipCompileArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PipCompileArgs {
            src_file,
            constraints,
            overrides,
            excludes,
            extra,
            all_extras,
            no_all_extras,
            build_constraints,
            refresh,
            no_deps,
            deps,
            group,
            output_file,
            format,
            no_strip_extras,
            strip_extras,
            no_strip_markers,
            strip_markers,
            no_annotate,
            annotate,
            no_header,
            header,
            annotation_style,
            custom_compile_command,
            resolver,
            python,
            system,
            no_system,
            generate_hashes,
            no_generate_hashes,
            no_build,
            build,
            no_binary,
            only_binary,
            python_version,
            python_platform,
            universal,
            no_universal,
            no_emit_package,
            emit_index_url,
            no_emit_index_url,
            emit_find_links,
            no_emit_find_links,
            emit_build_options,
            no_emit_build_options,
            emit_marker_expression,
            no_emit_marker_expression,
            emit_index_annotation,
            no_emit_index_annotation,
            torch_backend,
            compat_args: _,
        } = args;

        let constraints_from_workspace = if let Some(configuration) = &filesystem {
            configuration
                .constraint_dependencies
                .clone()
                .unwrap_or_default()
                .into_iter()
                .map(|requirement| {
                    Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
                })
                .collect()
        } else {
            Vec::new()
        };

        let overrides_from_workspace = if let Some(configuration) = &filesystem {
            configuration
                .override_dependencies
                .clone()
                .unwrap_or_default()
                .into_iter()
                .map(|requirement| {
                    Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
                })
                .collect()
        } else {
            Vec::new()
        };

        let excludes_from_workspace = if let Some(configuration) = &filesystem {
            configuration
                .exclude_dependencies
                .clone()
                .unwrap_or_default()
        } else {
            Vec::new()
        };

        let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
            configuration
                .build_constraint_dependencies
                .clone()
                .unwrap_or_default()
                .into_iter()
                .map(|requirement| {
                    Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
                })
                .collect()
        } else {
            Vec::new()
        };

        let environments = if let Some(configuration) = &filesystem {
            configuration.environments.clone().unwrap_or_default()
        } else {
            SupportedEnvironments::default()
        };

        let required_environments = if let Some(configuration) = &filesystem {
            configuration
                .required_environments
                .clone()
                .unwrap_or_default()
        } else {
            SupportedEnvironments::default()
        };

        Self {
            format,
            src_file,
            constraints: constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            build_constraints: build_constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            overrides: overrides
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            excludes: excludes
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            constraints_from_workspace,
            overrides_from_workspace,
            excludes_from_workspace,
            build_constraints_from_workspace,
            environments,
            required_environments,
            refresh: Refresh::from(refresh),
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    no_build: flag(no_build, build, "build"),
                    no_binary,
                    only_binary,
                    extra,
                    all_extras: flag(all_extras, no_all_extras, "all-extras"),
                    no_deps: flag(no_deps, deps, "deps"),
                    group: Some(group),
                    output_file,
                    no_strip_extras: flag(no_strip_extras, strip_extras, "strip-extras"),
                    no_strip_markers: flag(no_strip_markers, strip_markers, "strip-markers"),
                    no_annotate: flag(no_annotate, annotate, "annotate"),
                    no_header: flag(no_header, header, "header"),
                    custom_compile_command,
                    generate_hashes: flag(generate_hashes, no_generate_hashes, "generate-hashes"),
                    python_version,
                    python_platform,
                    universal: flag(universal, no_universal, "universal"),
                    no_emit_package,
                    emit_index_url: flag(emit_index_url, no_emit_index_url, "emit-index-url"),
                    emit_find_links: flag(emit_find_links, no_emit_find_links, "emit-find-links"),
                    emit_build_options: flag(
                        emit_build_options,
                        no_emit_build_options,
                        "emit-build-options",
                    ),
                    emit_marker_expression: flag(
                        emit_marker_expression,
                        no_emit_marker_expression,
                        "emit-marker-expression",
                    ),
                    emit_index_annotation: flag(
                        emit_index_annotation,
                        no_emit_index_annotation,
                        "emit-index-annotation",
                    ),
                    annotation_style,
                    torch_backend,
                    ..PipOptions::from(resolver)
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for a `pip sync` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PipSyncSettings {
    pub(crate) src_file: Vec<PathBuf>,
    pub(crate) constraints: Vec<PathBuf>,
    pub(crate) build_constraints: Vec<PathBuf>,
    pub(crate) dry_run: DryRun,
    pub(crate) refresh: Refresh,
    pub(crate) settings: PipSettings,
}

impl PipSyncSettings {
    /// Resolve the [`PipSyncSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: Box<PipSyncArgs>,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PipSyncArgs {
            src_file,
            constraints,
            build_constraints,
            extra,
            all_extras,
            no_all_extras,
            group,
            installer,
            refresh,
            require_hashes,
            no_require_hashes,
            verify_hashes,
            no_verify_hashes,
            python,
            system,
            no_system,
            break_system_packages,
            no_break_system_packages,
            target,
            prefix,
            allow_empty_requirements,
            no_allow_empty_requirements,
            no_build,
            build,
            no_binary,
            only_binary,
            python_version,
            python_platform,
            strict,
            no_strict,
            dry_run,
            torch_backend,
            compat_args: _,
        } = *args;

        Self {
            src_file,
            constraints: constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            build_constraints: build_constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            dry_run: DryRun::from_args(dry_run),
            refresh: Refresh::from(refresh),
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    break_system_packages: flag(
                        break_system_packages,
                        no_break_system_packages,
                        "break-system-packages",
                    ),
                    target,
                    prefix,
                    require_hashes: flag(require_hashes, no_require_hashes, "require-hashes"),
                    verify_hashes: flag(verify_hashes, no_verify_hashes, "verify-hashes"),
                    no_build: flag(no_build, build, "build"),
                    no_binary,
                    only_binary,
                    allow_empty_requirements: flag(
                        allow_empty_requirements,
                        no_allow_empty_requirements,
                        "allow-empty-requirements",
                    ),
                    python_version,
                    python_platform,
                    strict: flag(strict, no_strict, "strict"),
                    extra,
                    all_extras: flag(all_extras, no_all_extras, "all-extras"),
                    group: Some(group),
                    torch_backend,
                    ..PipOptions::from(installer)
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for a `pip install` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PipInstallSettings {
    pub(crate) package: Vec<String>,
    pub(crate) requirements: Vec<PathBuf>,
    pub(crate) editables: Vec<String>,
    pub(crate) editable: Option<EditableMode>,
    pub(crate) constraints: Vec<PathBuf>,
    pub(crate) overrides: Vec<PathBuf>,
    pub(crate) excludes: Vec<PathBuf>,
    pub(crate) build_constraints: Vec<PathBuf>,
    pub(crate) dry_run: DryRun,
    pub(crate) constraints_from_workspace: Vec<Requirement>,
    pub(crate) overrides_from_workspace: Vec<Requirement>,
    pub(crate) excludes_from_workspace: Vec<PackageName>,
    pub(crate) build_constraints_from_workspace: Vec<Requirement>,
    pub(crate) modifications: Modifications,
    pub(crate) refresh: Refresh,
    pub(crate) settings: PipSettings,
}

impl PipInstallSettings {
    /// Resolve the [`PipInstallSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PipInstallArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PipInstallArgs {
            package,
            requirements,
            editable,
            no_editable,
            no_editable_package,
            constraints,
            overrides,
            excludes,
            build_constraints,
            extra,
            all_extras,
            no_all_extras,
            installer,
            refresh,
            no_deps,
            deps,
            group,
            require_hashes,
            no_require_hashes,
            verify_hashes,
            no_verify_hashes,
            python,
            system,
            no_system,
            break_system_packages,
            no_break_system_packages,
            target,
            prefix,
            no_build,
            build,
            no_binary,
            only_binary,
            python_version,
            python_platform,
            inexact,
            exact,
            strict,
            no_strict,
            dry_run,
            torch_backend,
            compat_args: _,
        } = args;

        let constraints_from_workspace = if let Some(configuration) = &filesystem {
            configuration
                .constraint_dependencies
                .clone()
                .unwrap_or_default()
                .into_iter()
                .map(|requirement| {
                    Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
                })
                .collect()
        } else {
            Vec::new()
        };

        let overrides_from_workspace = if let Some(configuration) = &filesystem {
            configuration
                .override_dependencies
                .clone()
                .unwrap_or_default()
                .into_iter()
                .map(|requirement| {
                    Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
                })
                .collect()
        } else {
            Vec::new()
        };

        let excludes_from_workspace = if let Some(configuration) = &filesystem {
            configuration
                .exclude_dependencies
                .clone()
                .unwrap_or_default()
        } else {
            Vec::new()
        };

        let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
            configuration
                .build_constraint_dependencies
                .clone()
                .unwrap_or_default()
                .into_iter()
                .map(|requirement| {
                    Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
                })
                .collect()
        } else {
            Vec::new()
        };

        Self {
            package,
            requirements,
            editables: editable,
            constraints: constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            overrides: overrides
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            excludes: excludes
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            build_constraints: build_constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            dry_run: DryRun::from_args(dry_run),
            constraints_from_workspace,
            overrides_from_workspace,
            excludes_from_workspace,
            build_constraints_from_workspace,
            modifications: if flag(exact, inexact, "inexact").unwrap_or(false) {
                Modifications::Exact
            } else {
                Modifications::Sufficient
            },
            editable: EditableMode::from_args(
                if no_editable || environment.no_editable.value == Some(true) {
                    Some(false)
                } else {
                    None
                },
                no_editable_package,
            ),
            refresh: Refresh::from(refresh),
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    break_system_packages: flag(
                        break_system_packages,
                        no_break_system_packages,
                        "break-system-packages",
                    ),
                    target,
                    prefix,
                    no_build: flag(no_build, build, "build"),
                    no_binary,
                    only_binary,
                    strict: flag(strict, no_strict, "strict"),
                    extra,
                    all_extras: flag(all_extras, no_all_extras, "all-extras"),
                    group: Some(group),
                    no_deps: flag(no_deps, deps, "deps"),
                    python_version,
                    python_platform,
                    require_hashes: flag(require_hashes, no_require_hashes, "require-hashes"),
                    verify_hashes: flag(verify_hashes, no_verify_hashes, "verify-hashes"),
                    torch_backend,
                    ..PipOptions::from(installer)
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for a `pip uninstall` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PipUninstallSettings {
    pub(crate) package: Vec<String>,
    pub(crate) requirements: Vec<PathBuf>,
    pub(crate) dry_run: DryRun,
    pub(crate) settings: PipSettings,
}

impl PipUninstallSettings {
    /// Resolve the [`PipUninstallSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PipUninstallArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PipUninstallArgs {
            package,
            requirements,
            python,
            keyring_provider,
            system,
            no_system,
            break_system_packages,
            no_break_system_packages,
            target,
            prefix,
            dry_run,
            compat_args: _,
        } = args;

        Self {
            package,
            requirements,
            dry_run: DryRun::from_args(dry_run),
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    break_system_packages: flag(
                        break_system_packages,
                        no_break_system_packages,
                        "break-system-packages",
                    ),
                    target,
                    prefix,
                    keyring_provider,
                    ..PipOptions::default()
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for a `pip freeze` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PipFreezeSettings {
    pub(crate) exclude_editable: bool,
    pub(crate) exclude: FxHashSet<PackageName>,
    pub(crate) paths: Option<Vec<PathBuf>>,
    pub(crate) settings: PipSettings,
}

impl PipFreezeSettings {
    /// Resolve the [`PipFreezeSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PipFreezeArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PipFreezeArgs {
            exclude_editable,
            exclude,
            strict,
            no_strict,
            python,
            paths,
            system,
            no_system,
            target,
            prefix,
            compat_args: _,
        } = args;

        Self {
            exclude_editable,
            exclude: exclude.into_iter().collect(),
            paths,
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    strict: flag(strict, no_strict, "strict"),
                    target,
                    prefix,
                    ..PipOptions::default()
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for a `pip list` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PipListSettings {
    pub(crate) editable: Option<bool>,
    pub(crate) exclude: FxHashSet<PackageName>,
    pub(crate) format: ListFormat,
    pub(crate) outdated: bool,
    pub(crate) settings: PipSettings,
}

impl PipListSettings {
    /// Resolve the [`PipListSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PipListArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PipListArgs {
            editable,
            exclude_editable,
            exclude,
            format,
            outdated,
            no_outdated,
            strict,
            no_strict,
            fetch,
            python,
            system,
            no_system,
            target,
            prefix,
            compat_args: _,
        } = args;

        Self {
            editable: flag(editable, exclude_editable, "exclude-editable"),
            exclude: exclude.into_iter().collect(),
            format,
            outdated: flag(outdated, no_outdated, "outdated").unwrap_or(false),
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    strict: flag(strict, no_strict, "strict"),
                    target,
                    prefix,
                    ..PipOptions::from(fetch)
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for a `pip show` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PipShowSettings {
    pub(crate) package: Vec<PackageName>,
    pub(crate) files: bool,
    pub(crate) settings: PipSettings,
}

impl PipShowSettings {
    /// Resolve the [`PipShowSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PipShowArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PipShowArgs {
            package,
            strict,
            no_strict,
            files,
            python,
            system,
            no_system,
            target,
            prefix,
            compat_args: _,
        } = args;

        Self {
            package,
            files,
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    strict: flag(strict, no_strict, "strict"),
                    target,
                    prefix,
                    ..PipOptions::default()
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for a `pip tree` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PipTreeSettings {
    pub(crate) show_version_specifiers: bool,
    pub(crate) depth: u8,
    pub(crate) prune: Vec<PackageName>,
    pub(crate) package: Vec<PackageName>,
    pub(crate) no_dedupe: bool,
    pub(crate) invert: bool,
    pub(crate) outdated: bool,
    pub(crate) settings: PipSettings,
}

impl PipTreeSettings {
    /// Resolve the [`PipTreeSettings`] from the CLI and workspace configuration.
    pub(crate) fn resolve(
        args: PipTreeArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PipTreeArgs {
            show_version_specifiers,
            tree,
            strict,
            no_strict,
            fetch,
            python,
            system,
            no_system,
            compat_args: _,
        } = args;

        Self {
            show_version_specifiers,
            depth: tree.depth,
            prune: tree.prune,
            no_dedupe: tree.no_dedupe,
            invert: tree.invert,
            package: tree.package,
            outdated: tree.outdated,
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    strict: flag(strict, no_strict, "strict"),
                    ..PipOptions::from(fetch)
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for a `pip check` invocation.
#[derive(Debug, Clone)]
pub(crate) struct PipCheckSettings {
    pub(crate) settings: PipSettings,
}

impl PipCheckSettings {
    /// Resolve the [`PipCheckSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: PipCheckArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let PipCheckArgs {
            python,
            system,
            no_system,
            python_version,
            python_platform,
        } = args;

        Self {
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    python_version,
                    python_platform,
                    ..PipOptions::default()
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for a `build` invocation.
#[derive(Debug, Clone)]
pub(crate) struct BuildSettings {
    pub(crate) src: Option<PathBuf>,
    pub(crate) package: Option<PackageName>,
    pub(crate) all_packages: bool,
    pub(crate) out_dir: Option<PathBuf>,
    pub(crate) sdist: bool,
    pub(crate) wheel: bool,
    pub(crate) list: bool,
    pub(crate) build_logs: bool,
    pub(crate) gitignore: bool,
    pub(crate) force_pep517: bool,
    pub(crate) clear: bool,
    pub(crate) build_constraints: Vec<PathBuf>,
    pub(crate) build_constraints_from_workspace: Vec<Requirement>,
    pub(crate) hash_checking: Option<HashCheckingMode>,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) refresh: Refresh,
    pub(crate) settings: ResolverSettings,
}

impl BuildSettings {
    /// Resolve the [`BuildSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: BuildArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let BuildArgs {
            src,
            out_dir,
            package,
            all_packages,
            sdist,
            wheel,
            list,
            force_pep517,
            clear,
            build_constraints,
            require_hashes,
            no_require_hashes,
            verify_hashes,
            no_verify_hashes,
            build_logs,
            no_build_logs,
            create_gitignore,
            no_create_gitignore,
            python,
            build,
            refresh,
            resolver,
        } = args;
        let filesystem_install_mirrors = match &filesystem {
            Some(fs) => fs.install_mirrors.clone(),
            None => PythonInstallMirrors::default(),
        };
        let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
            configuration
                .build_constraint_dependencies
                .clone()
                .unwrap_or_default()
                .into_iter()
                .map(|requirement| {
                    Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
                })
                .collect()
        } else {
            Vec::new()
        };

        Self {
            src,
            package,
            all_packages,
            out_dir,
            sdist,
            wheel,
            list,
            build_logs: flag(build_logs, no_build_logs, "build-logs").unwrap_or(true),
            force_pep517,
            clear,
            gitignore: flag(create_gitignore, no_create_gitignore, "create-gitignore")
                .unwrap_or(true),
            build_constraints: build_constraints
                .into_iter()
                .filter_map(Maybe::into_option)
                .collect(),
            build_constraints_from_workspace,
            hash_checking: HashCheckingMode::from_args(
                flag(require_hashes, no_require_hashes, "require-hashes"),
                flag(verify_hashes, no_verify_hashes, "verify-hashes"),
            ),
            python: python.and_then(Maybe::into_option),
            refresh: Refresh::from(refresh),
            settings: ResolverSettings::combine(
                resolver_options(resolver, build),
                filesystem,
                &environment,
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
        }
    }
}

/// The resolved settings to use for a `venv` invocation.
#[derive(Debug, Clone)]
pub(crate) struct VenvSettings {
    pub(crate) seed: bool,
    pub(crate) allow_existing: bool,
    pub(crate) clear: bool,
    pub(crate) force: bool,
    pub(crate) no_clear: bool,
    pub(crate) path: Option<PathBuf>,
    pub(crate) prompt: Option<String>,
    pub(crate) system_site_packages: bool,
    pub(crate) relocatable: bool,
    pub(crate) no_relocatable: bool,
    pub(crate) no_project: bool,
    pub(crate) refresh: Refresh,
    pub(crate) settings: PipSettings,
}

impl VenvSettings {
    /// Resolve the [`VenvSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(
        args: VenvArgs,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let VenvArgs {
            python,
            system,
            no_system,
            seed,
            allow_existing,
            clear,
            force,
            no_clear,
            path,
            prompt,
            system_site_packages,
            relocatable,
            no_relocatable,
            index_args,
            index_strategy,
            keyring_provider,
            exclude_newer,
            no_project,
            link_mode,
            refresh,
            compat_args: _,
            exclude_newer_package,
        } = args;

        // Resolve flags from CLI and environment variables.
        let seed = seed || environment.venv_seed.value == Some(true);
        let (clear, no_clear) = resolve_flag_pair(
            clear,
            no_clear,
            "clear",
            "no-clear",
            Some(environment.venv_clear),
            None,
        );
        let (relocatable, no_relocatable) = resolve_flag_pair(
            relocatable,
            no_relocatable,
            "relocatable",
            "no-relocatable",
            Some(environment.venv_relocatable),
            None,
        );

        Self {
            seed,
            allow_existing,
            clear: clear.into(),
            force,
            no_clear: no_clear.into(),
            path,
            prompt,
            system_site_packages,
            no_project,
            relocatable: relocatable.into(),
            no_relocatable: no_relocatable.into(),
            refresh: Refresh::from(refresh),
            settings: PipSettings::combine(
                PipOptions {
                    python: python.and_then(Maybe::into_option),
                    system: flag(system, no_system, "system"),
                    index_strategy,
                    keyring_provider,
                    exclude_newer,
                    exclude_newer_package: exclude_newer_package
                        .map(ExcludeNewerPackage::from_iter),
                    link_mode,
                    ..PipOptions::from(index_args)
                },
                filesystem,
                environment,
            ),
        }
    }
}

/// The resolved settings to use for an invocation of the uv CLI when installing dependencies.
///
/// Combines the `[tool.uv]` persistent configuration with the command-line arguments
/// ([`InstallerArgs`], represented as [`InstallerOptions`]).
#[derive(Debug, Clone)]
pub(crate) struct InstallerSettingsRef<'a> {
    pub(crate) index_locations: &'a IndexLocations,
    pub(crate) index_strategy: IndexStrategy,
    pub(crate) keyring_provider: KeyringProviderType,
    pub(crate) dependency_metadata: &'a DependencyMetadata,
    pub(crate) config_setting: &'a ConfigSettings,
    pub(crate) config_settings_package: &'a PackageConfigSettings,
    pub(crate) build_isolation: &'a BuildIsolation,
    pub(crate) extra_build_dependencies: &'a ExtraBuildDependencies,
    pub(crate) extra_build_variables: &'a ExtraBuildVariables,
    pub(crate) exclude_newer: &'a ExcludeNewer,
    pub(crate) link_mode: LinkMode,
    pub(crate) compile_bytecode: bool,
    pub(crate) reinstall: &'a Reinstall,
    pub(crate) build_options: &'a BuildOptions,
    pub(crate) sources: NoSources,
}

/// The resolved settings to use for an invocation of the uv CLI when resolving dependencies.
///
/// Combines the `[tool.uv]` persistent configuration with the command-line arguments
/// ([`ResolverArgs`], represented as [`ResolverOptions`]).
#[derive(Debug, Clone, Default)]
pub(crate) struct ResolverSettings {
    pub(crate) build_options: BuildOptions,
    pub(crate) config_setting: ConfigSettings,
    pub(crate) config_settings_package: PackageConfigSettings,
    pub(crate) dependency_metadata: DependencyMetadata,
    pub(crate) exclude_newer: ExcludeNewer,
    pub(crate) fork_strategy: ForkStrategy,
    pub(crate) index_locations: IndexLocations,
    pub(crate) index_strategy: IndexStrategy,
    pub(crate) keyring_provider: KeyringProviderType,
    pub(crate) link_mode: LinkMode,
    pub(crate) build_isolation: BuildIsolation,
    pub(crate) extra_build_dependencies: ExtraBuildDependencies,
    pub(crate) extra_build_variables: ExtraBuildVariables,
    pub(crate) prerelease: PrereleaseMode,
    pub(crate) resolution: ResolutionMode,
    pub(crate) sources: NoSources,
    pub(crate) torch_backend: Option<TorchMode>,
    pub(crate) upgrade: Upgrade,
}

impl ResolverSettings {
    /// Resolve the [`ResolverSettings`] from the CLI and filesystem configuration.
    pub(crate) fn combine(
        mut args: ResolverOptions,
        filesystem: Option<FilesystemOptions>,
        environment: &EnvironmentOptions,
    ) -> Self {
        args.no_binary_package = args
            .no_binary_package
            .or(environment.no_binary_package.clone());
        args.no_build_package = args
            .no_build_package
            .or(environment.no_build_package.clone());
        args.no_sources_package = args
            .no_sources_package
            .or(environment.no_sources_package.clone());

        // The problem is that for `upgrade`... we want to combine the two `Upgrade` structs,
        // not the individual fields.
        let options = args.combine(ResolverOptions::from(
            filesystem
                .map(FilesystemOptions::into_options)
                .map(|options| options.top_level)
                .unwrap_or_default(),
        ));

        Self::from(options)
    }
}

impl From<ResolverOptions> for ResolverSettings {
    fn from(value: ResolverOptions) -> Self {
        let index_locations = IndexLocations::new(
            value
                .index
                .into_iter()
                .flatten()
                .chain(value.extra_index_url.into_iter().flatten().map(Index::from))
                .chain(value.index_url.into_iter().map(Index::from))
                .collect(),
            value
                .find_links
                .into_iter()
                .flatten()
                .map(Index::from)
                .collect(),
            value.no_index.unwrap_or_default(),
        );
        Self {
            index_locations,
            resolution: value.resolution.unwrap_or_default(),
            prerelease: value.prerelease.unwrap_or_default(),
            fork_strategy: value.fork_strategy.unwrap_or_default(),
            dependency_metadata: DependencyMetadata::from_entries(
                value.dependency_metadata.into_iter().flatten(),
            ),
            index_strategy: value.index_strategy.unwrap_or_default(),
            keyring_provider: value.keyring_provider.unwrap_or_default(),
            config_setting: value.config_settings.unwrap_or_default(),
            config_settings_package: value.config_settings_package.unwrap_or_default(),
            build_isolation: value.build_isolation.unwrap_or_default(),
            extra_build_dependencies: value.extra_build_dependencies.unwrap_or_default(),
            extra_build_variables: value.extra_build_variables.unwrap_or_default(),
            exclude_newer: value.exclude_newer,
            link_mode: value.link_mode.unwrap_or_default(),
            torch_backend: value.torch_backend,
            sources: NoSources::from_args(
                value.no_sources,
                value.no_sources_package.unwrap_or_default(),
            ),
            upgrade: value.upgrade.unwrap_or_default(),
            build_options: BuildOptions::new(
                NoBinary::from_args(value.no_binary, value.no_binary_package.unwrap_or_default()),
                NoBuild::from_args(value.no_build, value.no_build_package.unwrap_or_default()),
            ),
        }
    }
}

/// The resolved settings to use for an invocation of the uv CLI with both resolver and installer
/// capabilities.
///
/// Represents the shared settings that are used across all uv commands outside the `pip` API.
/// Analogous to the settings contained in the `[tool.uv]` table, combined with [`ResolverInstallerArgs`].
#[derive(Debug, Clone, Default)]
pub(crate) struct ResolverInstallerSettings {
    pub(crate) resolver: ResolverSettings,
    pub(crate) compile_bytecode: bool,
    pub(crate) reinstall: Reinstall,
}

impl ResolverInstallerSettings {
    /// Reconcile the [`ResolverInstallerSettings`] from the CLI and filesystem configuration.
    pub(crate) fn combine(
        args: ResolverInstallerOptions,
        filesystem: Option<FilesystemOptions>,
        environment: &EnvironmentOptions,
    ) -> Self {
        let options = resolver_installer_options_with_environment(args, environment).combine(
            ResolverInstallerOptions::from(
                filesystem
                    .map(FilesystemOptions::into_options)
                    .map(|options| options.top_level)
                    .unwrap_or_default(),
            ),
        );

        Self::from(options)
    }
}

fn resolver_installer_options_with_environment(
    mut options: ResolverInstallerOptions,
    environment: &EnvironmentOptions,
) -> ResolverInstallerOptions {
    options.no_binary_package = options
        .no_binary_package
        .or(environment.no_binary_package.clone());
    options.no_build_package = options
        .no_build_package
        .or(environment.no_build_package.clone());
    options.no_sources_package = options
        .no_sources_package
        .or(environment.no_sources_package.clone());
    options
}

impl From<ResolverInstallerOptions> for ResolverInstallerSettings {
    fn from(value: ResolverInstallerOptions) -> Self {
        let index_locations = IndexLocations::new(
            value
                .index
                .into_iter()
                .flatten()
                .chain(value.extra_index_url.into_iter().flatten().map(Index::from))
                .chain(value.index_url.into_iter().map(Index::from))
                .collect(),
            value
                .find_links
                .into_iter()
                .flatten()
                .map(Index::from)
                .collect(),
            value.no_index.unwrap_or_default(),
        );
        Self {
            resolver: ResolverSettings {
                build_options: BuildOptions::new(
                    NoBinary::from_args(
                        value.no_binary,
                        value.no_binary_package.unwrap_or_default(),
                    ),
                    NoBuild::from_args(value.no_build, value.no_build_package.unwrap_or_default()),
                ),
                config_setting: value.config_settings.unwrap_or_default(),
                config_settings_package: value.config_settings_package.unwrap_or_default(),
                dependency_metadata: DependencyMetadata::from_entries(
                    value.dependency_metadata.into_iter().flatten(),
                ),
                exclude_newer: ExcludeNewer::from_args(
                    value.exclude_newer,
                    value
                        .exclude_newer_package
                        .unwrap_or_default()
                        .into_iter()
                        .map(Into::into)
                        .collect(),
                ),
                fork_strategy: value.fork_strategy.unwrap_or_default(),
                index_locations,
                index_strategy: value.index_strategy.unwrap_or_default(),
                keyring_provider: value.keyring_provider.unwrap_or_default(),
                link_mode: value.link_mode.unwrap_or_default(),
                build_isolation: value.build_isolation.unwrap_or_default(),
                extra_build_dependencies: value.extra_build_dependencies.unwrap_or_default(),
                extra_build_variables: value.extra_build_variables.unwrap_or_default(),
                prerelease: value.prerelease.unwrap_or_default(),
                resolution: value.resolution.unwrap_or_default(),
                sources: NoSources::from_args(
                    value.no_sources,
                    value.no_sources_package.unwrap_or_default(),
                ),
                torch_backend: value.torch_backend,
                upgrade: value.upgrade.unwrap_or_default(),
            },
            compile_bytecode: value.compile_bytecode.unwrap_or_default(),
            reinstall: value.reinstall.unwrap_or_default(),
        }
    }
}

/// The resolved settings to use for an invocation of the `pip` CLI.
///
/// Represents the shared settings that are used across all `pip` commands. Analogous to the
/// settings contained in the `[tool.uv.pip]` table.
#[derive(Debug, Clone)]
pub(crate) struct PipSettings {
    pub(crate) index_locations: IndexLocations,
    pub(crate) python: Option<String>,
    pub(crate) install_mirrors: PythonInstallMirrors,
    pub(crate) system: bool,
    pub(crate) extras: ExtrasSpecification,
    pub(crate) groups: Vec<PipGroupName>,
    pub(crate) break_system_packages: bool,
    pub(crate) target: Option<Target>,
    pub(crate) prefix: Option<Prefix>,
    pub(crate) index_strategy: IndexStrategy,
    pub(crate) keyring_provider: KeyringProviderType,
    pub(crate) torch_backend: Option<TorchMode>,
    pub(crate) build_isolation: BuildIsolation,
    pub(crate) extra_build_dependencies: ExtraBuildDependencies,
    pub(crate) extra_build_variables: ExtraBuildVariables,
    pub(crate) build_options: BuildOptions,
    pub(crate) allow_empty_requirements: bool,
    pub(crate) strict: bool,
    pub(crate) dependency_mode: DependencyMode,
    pub(crate) resolution: ResolutionMode,
    pub(crate) prerelease: PrereleaseMode,
    pub(crate) fork_strategy: ForkStrategy,
    pub(crate) dependency_metadata: DependencyMetadata,
    pub(crate) output_file: Option<PathBuf>,
    pub(crate) no_strip_extras: bool,
    pub(crate) no_strip_markers: bool,
    pub(crate) no_annotate: bool,
    pub(crate) no_header: bool,
    pub(crate) custom_compile_command: Option<String>,
    pub(crate) generate_hashes: bool,
    pub(crate) config_setting: ConfigSettings,
    pub(crate) config_settings_package: PackageConfigSettings,
    pub(crate) python_version: Option<PythonVersion>,
    pub(crate) python_platform: Option<TargetTriple>,
    pub(crate) universal: bool,
    pub(crate) exclude_newer: ExcludeNewer,
    pub(crate) no_emit_package: Vec<PackageName>,
    pub(crate) emit_index_url: bool,
    pub(crate) emit_find_links: bool,
    pub(crate) emit_build_options: bool,
    pub(crate) emit_marker_expression: bool,
    pub(crate) emit_index_annotation: bool,
    pub(crate) annotation_style: AnnotationStyle,
    pub(crate) link_mode: LinkMode,
    pub(crate) compile_bytecode: bool,
    pub(crate) sources: NoSources,
    pub(crate) hash_checking: Option<HashCheckingMode>,
    pub(crate) upgrade: Upgrade,
    pub(crate) reinstall: Reinstall,
}

impl PipSettings {
    /// Resolve the [`PipSettings`] from the CLI and filesystem configuration.
    pub(crate) fn combine(
        args: PipOptions,
        filesystem: Option<FilesystemOptions>,
        environment: EnvironmentOptions,
    ) -> Self {
        let Options {
            top_level,
            pip,
            install_mirrors: filesystem_install_mirrors,
            ..
        } = filesystem
            .map(FilesystemOptions::into_options)
            .unwrap_or_default();

        let PipOptions {
            python,
            system,
            break_system_packages,
            target,
            prefix,
            index,
            index_url,
            extra_index_url,
            no_index,
            find_links,
            index_strategy,
            torch_backend,
            keyring_provider,
            no_build,
            no_binary,
            only_binary,
            no_build_isolation,
            no_build_isolation_package,
            extra_build_dependencies,
            extra_build_variables,
            strict,
            extra,
            all_extras,
            no_extra,
            group,
            no_deps,
            allow_empty_requirements,
            resolution,
            prerelease,
            fork_strategy,
            dependency_metadata,
            output_file,
            no_strip_extras,
            no_strip_markers,
            no_annotate,
            no_header,
            custom_compile_command,
            generate_hashes,
            config_settings,
            config_settings_package,
            python_version,
            python_platform,
            universal,
            exclude_newer,
            no_emit_package,
            emit_index_url,
            emit_find_links,
            emit_build_options,
            emit_marker_expression,
            emit_index_annotation,
            annotation_style,
            link_mode,
            compile_bytecode,
            require_hashes,
            verify_hashes,
            no_sources,
            no_sources_package,
            upgrade,
            upgrade_package,
            reinstall,
            reinstall_package,
            exclude_newer_package,
        } = pip.unwrap_or_default();

        let ResolverInstallerSchema {
            index: top_level_index,
            index_url: top_level_index_url,
            extra_index_url: top_level_extra_index_url,
            no_index: top_level_no_index,
            find_links: top_level_find_links,
            index_strategy: top_level_index_strategy,
            keyring_provider: top_level_keyring_provider,
            resolution: top_level_resolution,
            prerelease: top_level_prerelease,
            fork_strategy: top_level_fork_strategy,
            dependency_metadata: top_level_dependency_metadata,
            config_settings: top_level_config_settings,
            config_settings_package: top_level_config_settings_package,
            no_build_isolation: top_level_no_build_isolation,
            no_build_isolation_package: top_level_no_build_isolation_package,
            extra_build_dependencies: top_level_extra_build_dependencies,
            extra_build_variables: top_level_extra_build_variables,
            exclude_newer: top_level_exclude_newer,
            link_mode: top_level_link_mode,
            compile_bytecode: top_level_compile_bytecode,
            no_sources: top_level_no_sources,
            no_sources_package: top_level_no_sources_package,
            upgrade: top_level_upgrade,
            upgrade_package: top_level_upgrade_package,
            reinstall: top_level_reinstall,
            reinstall_package: top_level_reinstall_package,
            no_build: top_level_no_build,
            no_build_package: top_level_no_build_package,
            no_binary: top_level_no_binary,
            no_binary_package: top_level_no_binary_package,
            exclude_newer_package: top_level_exclude_newer_package,
            torch_backend: top_level_torch_backend,
        } = top_level;

        // Merge the top-level options (`tool.uv`) with the pip-specific options (`tool.uv.pip`),
        // preferring the latter.
        //
        // For example, prefer `tool.uv.pip.index-url` over `tool.uv.index-url`.
        let index = index.combine(top_level_index);
        let no_index = no_index.combine(top_level_no_index);
        let index_url = index_url.combine(top_level_index_url);
        let extra_index_url = extra_index_url.combine(top_level_extra_index_url);
        let find_links = find_links.combine(top_level_find_links);
        let index_strategy = index_strategy.combine(top_level_index_strategy);
        let keyring_provider = keyring_provider.combine(top_level_keyring_provider);
        let resolution = resolution.combine(top_level_resolution);
        let prerelease = prerelease.combine(top_level_prerelease);
        let fork_strategy = fork_strategy.combine(top_level_fork_strategy);
        let dependency_metadata = dependency_metadata.combine(top_level_dependency_metadata);
        let config_settings = config_settings.combine(top_level_config_settings);
        let config_settings_package =
            config_settings_package.combine(top_level_config_settings_package);
        let no_build_isolation = no_build_isolation.combine(top_level_no_build_isolation);
        let no_build_isolation_package =
            no_build_isolation_package.combine(top_level_no_build_isolation_package);
        let extra_build_dependencies =
            extra_build_dependencies.combine(top_level_extra_build_dependencies);
        let extra_build_variables = extra_build_variables.combine(top_level_extra_build_variables);
        let exclude_newer = args
            .exclude_newer
            .combine(exclude_newer)
            .combine(top_level_exclude_newer);
        let exclude_newer_package = args
            .exclude_newer_package
            .combine(exclude_newer_package)
            .combine(top_level_exclude_newer_package)
            .unwrap_or_default();
        let link_mode = link_mode.combine(top_level_link_mode);
        let compile_bytecode = compile_bytecode.combine(top_level_compile_bytecode);
        let no_sources = no_sources.combine(top_level_no_sources);
        let no_sources_package = no_sources_package.combine(top_level_no_sources_package);
        let upgrade = upgrade.combine(top_level_upgrade);
        let upgrade_package = upgrade_package.combine(top_level_upgrade_package);
        let reinstall = reinstall.combine(top_level_reinstall);
        let reinstall_package = reinstall_package.combine(top_level_reinstall_package);
        let torch_backend = torch_backend.combine(top_level_torch_backend);
        let args_no_sources_package = args
            .no_sources_package
            .or(environment.no_sources_package.clone());

        Self {
            index_locations: IndexLocations::new(
                args.index
                    .into_iter()
                    .flatten()
                    .chain(args.extra_index_url.into_iter().flatten().map(Index::from))
                    .chain(args.index_url.into_iter().map(Index::from))
                    .chain(index.into_iter().flatten())
                    .chain(extra_index_url.into_iter().flatten().map(Index::from))
                    .chain(index_url.into_iter().map(Index::from))
                    .collect(),
                args.find_links
                    .combine(find_links)
                    .into_iter()
                    .flatten()
                    .map(Index::from)
                    .collect(),
                args.no_index.combine(no_index).unwrap_or_default(),
            ),
            extras: ExtrasSpecification::from_args(
                args.extra.combine(extra).unwrap_or_default(),
                args.no_extra.combine(no_extra).unwrap_or_default(),
                // TODO(blueraft): support no_default_extras
                false,
                // TODO(blueraft): support only_extra
                vec![],
                args.all_extras.combine(all_extras).unwrap_or_default(),
            ),

            groups: args.group.combine(group).unwrap_or_default(),
            dependency_mode: if args.no_deps.combine(no_deps).unwrap_or_default() {
                DependencyMode::Direct
            } else {
                DependencyMode::Transitive
            },
            resolution: args.resolution.combine(resolution).unwrap_or_default(),
            prerelease: args.prerelease.combine(prerelease).unwrap_or_default(),
            fork_strategy: args
                .fork_strategy
                .combine(fork_strategy)
                .unwrap_or_default(),
            dependency_metadata: DependencyMetadata::from_entries(
                args.dependency_metadata
                    .combine(dependency_metadata)
                    .unwrap_or_default(),
            ),
            output_file: args.output_file.combine(output_file),
            no_strip_extras: args
                .no_strip_extras
                .combine(no_strip_extras)
                .unwrap_or_default(),
            no_strip_markers: args
                .no_strip_markers
                .combine(no_strip_markers)
                .unwrap_or_default(),
            no_annotate: args.no_annotate.combine(no_annotate).unwrap_or_default(),
            no_header: args.no_header.combine(no_header).unwrap_or_default(),
            custom_compile_command: args.custom_compile_command.combine(custom_compile_command),
            annotation_style: args
                .annotation_style
                .combine(annotation_style)
                .unwrap_or_default(),
            index_strategy: args
                .index_strategy
                .combine(index_strategy)
                .unwrap_or_default(),
            keyring_provider: args
                .keyring_provider
                .combine(keyring_provider)
                .unwrap_or_default(),
            generate_hashes: args
                .generate_hashes
                .combine(generate_hashes)
                .unwrap_or_default(),
            allow_empty_requirements: args
                .allow_empty_requirements
                .combine(allow_empty_requirements)
                .unwrap_or_default(),
            build_isolation: BuildIsolation::from_args(
                args.no_build_isolation,
                args.no_build_isolation_package.unwrap_or_default(),
            )
            .combine(BuildIsolation::from_args(
                no_build_isolation,
                no_build_isolation_package.unwrap_or_default(),
            ))
            .unwrap_or_default(),
            extra_build_dependencies: args
                .extra_build_dependencies
                .combine(extra_build_dependencies)
                .unwrap_or_default(),
            extra_build_variables: args
                .extra_build_variables
                .combine(extra_build_variables)
                .unwrap_or_default(),
            config_setting: args
                .config_settings
                .combine(config_settings)
                .unwrap_or_default(),
            config_settings_package: args
                .config_settings_package
                .combine(config_settings_package)
                .unwrap_or_default(),
            torch_backend: args.torch_backend.combine(torch_backend),
            python_version: args.python_version.combine(python_version),
            python_platform: args.python_platform.combine(python_platform),
            universal: args.universal.combine(universal).unwrap_or_default(),
            exclude_newer: ExcludeNewer::from_args(
                exclude_newer,
                exclude_newer_package.into_iter().map(Into::into).collect(),
            ),
            no_emit_package: args
                .no_emit_package
                .combine(no_emit_package)
                .unwrap_or_default(),
            emit_index_url: args
                .emit_index_url
                .combine(emit_index_url)
                .unwrap_or_default(),
            emit_find_links: args
                .emit_find_links
                .combine(emit_find_links)
                .unwrap_or_default(),
            emit_build_options: args
                .emit_build_options
                .combine(emit_build_options)
                .unwrap_or_default(),
            emit_marker_expression: args
                .emit_marker_expression
                .combine(emit_marker_expression)
                .unwrap_or_default(),
            emit_index_annotation: args
                .emit_index_annotation
                .combine(emit_index_annotation)
                .unwrap_or_default(),
            link_mode: args.link_mode.combine(link_mode).unwrap_or_default(),
            hash_checking: HashCheckingMode::from_args(
                args.require_hashes.combine(require_hashes),
                args.verify_hashes.combine(verify_hashes),
            ),
            python: args.python.combine(python),
            system: args.system.combine(system).unwrap_or_default(),
            break_system_packages: args
                .break_system_packages
                .combine(break_system_packages)
                .unwrap_or_default(),
            target: args.target.combine(target).map(Target::from),
            prefix: args.prefix.combine(prefix).map(Prefix::from),
            compile_bytecode: args
                .compile_bytecode
                .combine(compile_bytecode)
                .unwrap_or_default(),
            sources: NoSources::from_args(
                args.no_sources.combine(no_sources),
                args_no_sources_package
                    .combine(no_sources_package)
                    .unwrap_or_default(),
            ),
            strict: args.strict.combine(strict).unwrap_or_default(),
            upgrade: Upgrade::from_args(
                args.upgrade,
                args.upgrade_package
                    .into_iter()
                    .flatten()
                    .map(Requirement::from)
                    .collect(),
                Vec::new(),
            )
            .combine(Upgrade::from_args(
                upgrade,
                upgrade_package
                    .into_iter()
                    .flatten()
                    .map(Requirement::from)
                    .collect(),
                Vec::new(),
            ))
            .unwrap_or_default(),
            reinstall: Reinstall::from_args(
                args.reinstall,
                args.reinstall_package.unwrap_or_default(),
            )
            .combine(Reinstall::from_args(
                reinstall,
                reinstall_package.unwrap_or_default(),
            ))
            .unwrap_or_default(),
            build_options: BuildOptions::new(
                NoBinary::from_pip_args(args.no_binary.combine(no_binary).unwrap_or_default())
                    .combine(NoBinary::from_args(
                        top_level_no_binary,
                        top_level_no_binary_package.unwrap_or_default(),
                    )),
                NoBuild::from_pip_args(
                    args.only_binary.combine(only_binary).unwrap_or_default(),
                    args.no_build.combine(no_build).unwrap_or_default(),
                )
                .combine(NoBuild::from_args(
                    top_level_no_build,
                    top_level_no_build_package.unwrap_or_default(),
                )),
            ),
            install_mirrors: environment
                .install_mirrors
                .combine(filesystem_install_mirrors),
        }
    }
}

impl<'a> From<&'a ResolverInstallerSettings> for InstallerSettingsRef<'a> {
    fn from(settings: &'a ResolverInstallerSettings) -> Self {
        Self {
            index_locations: &settings.resolver.index_locations,
            index_strategy: settings.resolver.index_strategy,
            keyring_provider: settings.resolver.keyring_provider,
            dependency_metadata: &settings.resolver.dependency_metadata,
            config_setting: &settings.resolver.config_setting,
            config_settings_package: &settings.resolver.config_settings_package,
            build_isolation: &settings.resolver.build_isolation,
            extra_build_dependencies: &settings.resolver.extra_build_dependencies,
            extra_build_variables: &settings.resolver.extra_build_variables,
            exclude_newer: &settings.resolver.exclude_newer,
            link_mode: settings.resolver.link_mode,
            compile_bytecode: settings.compile_bytecode,
            reinstall: &settings.reinstall,
            build_options: &settings.resolver.build_options,
            sources: settings.resolver.sources.clone(),
        }
    }
}

/// The resolved settings to use for an invocation of the `uv publish` CLI.
#[derive(Debug, Clone)]
pub(crate) struct PublishSettings {
    // CLI only, see [`PublishArgs`] for docs.
    pub(crate) files: Vec<String>,
    pub(crate) username: Option<String>,
    pub(crate) password: Option<String>,
    pub(crate) index: Option<String>,
    pub(crate) dry_run: bool,
    pub(crate) no_attestations: bool,
    pub(crate) direct: bool,

    // Both CLI and configuration.
    pub(crate) publish_url: DisplaySafeUrl,
    pub(crate) trusted_publishing: TrustedPublishing,
    pub(crate) keyring_provider: KeyringProviderType,
    pub(crate) check_url: Option<IndexUrl>,

    // Configuration only
    pub(crate) index_locations: IndexLocations,
}

impl PublishSettings {
    /// Resolve the [`PublishSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(args: PublishArgs, filesystem: Option<FilesystemOptions>) -> Self {
        let Options {
            publish, top_level, ..
        } = filesystem
            .map(FilesystemOptions::into_options)
            .unwrap_or_default();

        let PublishOptions {
            publish_url,
            trusted_publishing,
            check_url,
        } = publish;
        let ResolverInstallerSchema {
            keyring_provider,
            index,
            extra_index_url,
            index_url,
            ..
        } = top_level;

        // Tokens are encoded in the same way as username/password
        let (username, password) = if let Some(token) = args.token {
            (Some("__token__".to_string()), Some(token))
        } else {
            (args.username, args.password)
        };

        Self {
            files: args.files,
            username,
            password,
            dry_run: args.dry_run,
            no_attestations: args.no_attestations,
            direct: args.direct,
            publish_url: args
                .publish_url
                .combine(publish_url)
                .unwrap_or_else(|| DisplaySafeUrl::parse(PYPI_PUBLISH_URL).unwrap()),
            trusted_publishing: trusted_publishing
                .combine(args.trusted_publishing)
                .unwrap_or_default(),
            keyring_provider: args
                .keyring_provider
                .combine(keyring_provider)
                .unwrap_or_default(),
            check_url: args.check_url.combine(check_url),
            index: args.index,
            index_locations: IndexLocations::new(
                index
                    .into_iter()
                    .flatten()
                    .chain(extra_index_url.into_iter().flatten().map(Index::from))
                    .chain(index_url.into_iter().map(Index::from))
                    .collect(),
                Vec::new(),
                false,
            ),
        }
    }
}

/// The resolved settings to use for an invocation of the `uv auth logout` CLI.
#[derive(Debug, Clone)]
pub(crate) struct AuthLogoutSettings {
    pub(crate) service: Service,
    pub(crate) username: Option<String>,
}

impl AuthLogoutSettings {
    /// Resolve the [`AuthLogoutSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(args: AuthLogoutArgs) -> Self {
        Self {
            service: args.service,
            username: args.username,
        }
    }
}

/// The resolved settings to use for an invocation of the `uv auth token` CLI.
#[derive(Debug, Clone)]
pub(crate) struct AuthTokenSettings {
    pub(crate) service: Service,
    pub(crate) username: Option<String>,
}

impl AuthTokenSettings {
    /// Resolve the [`AuthTokenSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(args: AuthTokenArgs) -> Self {
        Self {
            service: args.service,
            username: args.username,
        }
    }
}

/// The resolved settings to use for an invocation of the `uv auth set` CLI.
#[derive(Debug, Clone)]
pub(crate) struct AuthLoginSettings {
    pub(crate) service: Service,
    pub(crate) username: Option<String>,
    pub(crate) password: Option<String>,
    pub(crate) token: Option<String>,
}

impl AuthLoginSettings {
    /// Resolve the [`AuthLoginSettings`] from the CLI and filesystem configuration.
    pub(crate) fn resolve(args: AuthLoginArgs) -> Self {
        Self {
            service: args.service,
            username: args.username,
            password: args.password,
            token: args.token,
        }
    }
}

// Environment variables that are not exposed as CLI arguments.
mod env {
    use uv_static::EnvVars;
    pub(super) const UV_PYTHON_DOWNLOADS: (&str, &str) = (
        EnvVars::UV_PYTHON_DOWNLOADS,
        "one of 'auto', 'true', 'manual', 'never', or 'false'",
    );
}

/// Attempt to load and parse an environment variable with the given name.
///
/// Exits the program and prints an error message containing the expected type if
/// parsing values.
fn env<T>((name, expected): (&str, &str)) -> Option<T>
where
    T: FromStr,
{
    let val = match std::env::var(name) {
        Ok(val) => val,
        Err(VarError::NotPresent) => return None,
        Err(VarError::NotUnicode(_)) => parse_failure(name, expected),
    };
    Some(
        val.parse()
            .unwrap_or_else(|_| parse_failure(name, expected)),
    )
}

/// Prints a parse error and exits the process.
#[expect(clippy::exit, clippy::print_stderr)]
fn parse_failure(name: &str, expected: &str) -> ! {
    eprintln!("error: invalid value for {name}, expected {expected}");
    process::exit(1)
}
