use {
    clap::Parser,
    debrepo::{
        auth::AuthProvider, cli as deb_cli, content::HostCache, HostFileSystem, HttpTransport,
        Manifest, DEFAULT_ARCH,
    },
    std::{
        num::NonZero,
        path::{Path, PathBuf},
    },
};

impl deb_cli::Config for App {
    type FS = HostFileSystem;
    type Cache = HostCache;
    fn log_level(&self) -> i32 {
        if self.quiet {
            -1
        } else {
            self.debug as i32
        }
    }
    fn arch(&self) -> &str {
        &self.arch
    }
    fn manifest(&self) -> &Path {
        &self.manifest
    }
    fn concurrency(&self) -> NonZero<usize> {
        self.concurrency
    }
    fn fetcher(&self) -> std::io::Result<&HostCache> {
        static PROVIDER: once_cell::sync::OnceCell<HostCache> = once_cell::sync::OnceCell::new();
        PROVIDER.get_or_try_init(|| {
            let base = self
                .manifest
                .parent()
                .map(|s| {
                    if s.as_os_str().is_empty() {
                        Path::new(".")
                    } else {
                        s
                    }
                })
                .ok_or_else(|| {
                    std::io::Error::other(format!(
                        "invalid manifest file path: {}",
                        self.manifest.display()
                    ))
                })?;
            let base = std::fs::canonicalize(base).map_err(|err| {
                std::io::Error::other(format!(
                    "failed to find manifest file parent directory {}: {}",
                    self.manifest.parent().unwrap_or(Path::new(".")).display(),
                    err
                ))
            })?;
            if let Some(path) = self.cache_dir.as_deref() {
                std::fs::create_dir_all(path).map_err(|err| {
                    std::io::Error::other(format!(
                        "failed to create cache directory {}: {}",
                        path.display(),
                        err
                    ))
                })?;
            }
            let auth_file = base.join("auth.toml");
            let auth_file = std::fs::canonicalize(&auth_file)
                .ok()
                .and_then(|p| p.into_os_string().into_string().ok());
            Ok(HostCache::new(
                HttpTransport::new(
                    AuthProvider::new(self.auth.as_deref().or(auth_file.as_deref()))?,
                    self.insecure,
                    self.http11,
                    self.timeout.map(std::time::Duration::from_secs),
                ),
                self.cache_dir.as_deref(),
            ))
        })
    }
}

#[derive(Parser)]
#[command(
    version,
    next_line_help = false,
    about = "Manifest-driven Debian/Ubuntu bootstrapper",
    long_about = r#"rdebootstrap consumes a declarative manifest (default: Manifest.toml),
resolves Debian packages from configured APT archives, writes a lock file for
reproducible builds, stages extra artifacts, and builds a root filesystem tree
inside an isolated sandbox so maintainer scripts run in a controlled environment.

Typical workflow:
    1) init             - create a manifest from a vendor preset, explicit archive, or --import path
    2) import           - optionally reuse archives, local packages, and named parent specs from another locked manifest
    3) require/forbid   - add package requirements and constraints
    4) update           - refresh archive metadata, import fingerprints, and Manifest.<arch>.lock
    5) build            - extract and configure packages into a target directory
"#
)]
pub struct App {
    /// Turns off all output except errors
    #[arg(short, long, conflicts_with = "debug", action)]
    pub quiet: bool,

    /// Turns on debugging output (repeat -d for more)
    #[arg(short, long, conflicts_with = "quiet", action = clap::ArgAction::Count)]
    pub debug: u8,

    /// Number of concurrent downloads
    #[arg(
        short = 'n',
        long = "downloads",
        value_name = "NUM",
        default_value = "20"
    )]
    pub concurrency: NonZero<usize>,

    /// Target architecture (e.g. amd64, arm64)
    #[arg(long, value_name = "ARCH", default_value = DEFAULT_ARCH)]
    pub arch: String,

    /// Cache directory to keep downloaded files
    #[arg(
        long = "cache-dir",
        value_name = "DIR",
        conflicts_with = "no_cache",
        display_order = 0
    )]
    pub cache_dir: Option<PathBuf>,

    /// Disable caching downloaded files
    #[arg(
        long = "no-cache",
        conflicts_with = "cache_dir",
        display_order = 0,
        action,
        display_order = 0
    )]
    pub no_cache: bool,

    /// Skip TLS certificate and hostname verification (not recommended)
    #[arg(short = 'k', long = "insecure", action, display_order = 0, action)]
    pub insecure: bool,

    /// Force HTTP/1.1 for transport requests
    #[arg(long = "http1.1", global = true, display_order = 0, action)]
    pub http11: bool,

    /// HTTP transport timeout in seconds (default 30 seconds)
    #[arg(long = "timeout", global = true, display_order = 0)]
    pub timeout: Option<u64>,

    /// Auth source (file:<path>, <path>, or vault:<mount>/<path>); defaults to auth.toml next to the manifest if present
    #[arg(short = 'a', long = "auth", value_name = "AUTH", display_order = 0)]
    pub auth: Option<String>,

    /// Path to the manifest file
    #[arg(global = true, short, long, default_value = Manifest::DEFAULT_FILE, display_order = 0)]
    pub manifest: PathBuf,

    #[command(subcommand)]
    pub cmd: Commands,
}

debrepo::cli_commands! {
    pub enum Commands<App> {
        Init(deb_cli::cmd::Init),
        Import(deb_cli::cmd::ImportCmd),
        Archive(deb_cli::cmd::ArchiveCmd),
        Deb(deb_cli::cmd::DebCmd),
        Artifact(deb_cli::cmd::ArtifactCmd),
        Require(deb_cli::cmd::Require),
        Forbid(deb_cli::cmd::Forbid),
        Remove(deb_cli::cmd::Remove),
        Stage(deb_cli::cmd::Stage),
        Unstage(deb_cli::cmd::Unstage),
        List(deb_cli::cmd::List),
        Update(deb_cli::cmd::Update),
        Build(deb_cli::cmd::Build),
        Search(deb_cli::cmd::Search),
        Spec(deb_cli::cmd::Spec),
        Package(deb_cli::cmd::PackageCmd),
        Source(deb_cli::cmd::SourceCmd),
        Edit(deb_cli::cmd::Edit),
        #[command(hide = true)]
        Tool(deb_cli::cmd::Tool),
    }
}
