use std::path::PathBuf;

use clap::Parser;
use radicle::{
    identity::{Visibility, project::ProjectName},
    node::policy::Scope,
    prelude::RepoId,
};
use radicle_term::Interactive;

use crate::terminal::args::ScopeParser;

const ABOUT: &str = "Initialize a Radicle repository";

#[derive(Debug, Parser)]
#[command(about = ABOUT, disable_version_flag = true)]
pub struct Args {
    /// Directory to be initialized
    pub(super) path: Option<PathBuf>,
    /// Name of the repository
    #[arg(long)]
    pub(super) name: Option<ProjectName>,
    /// Description of the repository
    #[arg(long)]
    pub(super) description: Option<String>,
    /// The default branch of the repository
    #[arg(long = "default-branch")]
    pub(super) branch: Option<String>,
    /// Repository follow scope
    #[arg(
        long,
        default_value_t = Scope::All,
        value_name = "SCOPE",
        value_parser = ScopeParser,
    )]
    pub(super) scope: Scope,
    /// Set repository visibility to *private*
    #[arg(long, conflicts_with = "public")]
    private: bool,
    /// Set repository visibility to *public*
    #[arg(long, conflicts_with = "private")]
    public: bool,
    /// Set up repository as an existing Radicle repository
    ///
    /// [example values: rad:z3Tr6bC7ctEg2EHmLvknUr29mEDLH, z3Tr6bC7ctEg2EHmLvknUr29mEDLH]
    #[arg(long, value_name = "RID")]
    pub(super) existing: Option<RepoId>,
    /// Set up the upstream of the default branch
    #[arg(short = 'u', long)]
    pub(super) set_upstream: bool,
    /// Set up the Radicle key as a signing key for this repository
    #[arg(long)]
    pub(super) setup_signing: bool,
    /// Don't ask for confirmation during setup
    #[arg(long)]
    no_confirm: bool,
    /// Don't seed this repository after initializing it
    #[arg(long)]
    no_seed: bool,
    /// Verbose mode
    #[arg(short, long)]
    pub(super) verbose: bool,
}

impl Args {
    pub(super) fn interactive(&self) -> Interactive {
        if self.no_confirm {
            Interactive::No
        } else {
            Interactive::Yes
        }
    }

    pub(super) fn visibility(&self) -> Option<Visibility> {
        if self.private {
            debug_assert!(!self.public, "BUG: `private` and `public` should conflict");
            Some(Visibility::private([]))
        } else if self.public {
            Some(Visibility::Public)
        } else {
            None
        }
    }

    pub(super) fn seed(&self) -> bool {
        !self.no_seed
    }
}

#[cfg(test)]
mod test {
    use super::Args;
    use clap::Parser;
    use clap::error::ErrorKind;

    #[test]
    fn should_parse_rid_non_urn() {
        let args = Args::try_parse_from(["init", "--existing", "z3Tr6bC7ctEg2EHmLvknUr29mEDLH"]);
        assert!(args.is_ok())
    }

    #[test]
    fn should_parse_rid_urn() {
        let args =
            Args::try_parse_from(["init", "--existing", "rad:z3Tr6bC7ctEg2EHmLvknUr29mEDLH"]);
        assert!(args.is_ok())
    }

    #[test]
    fn should_not_parse_rid_url() {
        let err =
            Args::try_parse_from(["init", "--existing", "rad://z3Tr6bC7ctEg2EHmLvknUr29mEDLH"])
                .unwrap_err();
        assert_eq!(err.kind(), ErrorKind::ValueValidation);
    }
}
