//! Command-line argument parsing for the main `repo2markdown` binary.
//!
//! This module defines the CLI shape and translates parsed arguments into internal rendering and
//! logging configuration.

use std::path::PathBuf;

use clap::Parser;
use repo2markdown::{
    logger::Verbosity,
    renderer::{DEFAULT_MAX_FILE_SIZE, RenderOptions},
};

#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
pub struct Cli {
    /// Root path to which file paths displayed in the generated markdown are relative.
    #[arg(long, default_value = ".")]
    pub root: PathBuf,

    /// Base path used to resolve relative input paths.
    #[arg(long, default_value = ".")]
    pub origin: PathBuf,

    /// Project title to use in the markdown header.
    #[arg(long)]
    pub name: Option<String>,

    /// Wrap the entire output in a markdown code fence.
    #[arg(long)]
    pub fenced: bool,

    /// Maximum file size, in bytes, to include in the generated markdown.
    #[arg(long, default_value_t = DEFAULT_MAX_FILE_SIZE, value_name = "BYTES")]
    max_file_size: u64,

    /// Include a placeholder entry when a binary file is skipped.
    #[arg(long)]
    placeholder_binary_files: bool,

    /// Include a placeholder entry when a large file is skipped.
    #[arg(long)]
    placeholder_large_files: bool,

    /// Suppress warnings and informational logs.
    #[arg(long, conflicts_with = "verbose")]
    quiet: bool,

    /// Include informational logs.
    #[arg(long, conflicts_with = "quiet")]
    verbose: bool,
}

impl Cli {
    /// Converts CLI rendering flags into renderer options.
    ///
    /// These options control file-size limits and whether skipped files are represented by
    /// placeholder entries in the generated Markdown.
    pub fn render_options(&self) -> RenderOptions {
        RenderOptions {
            max_file_size: self.max_file_size,
            placeholder_for_binary_files: self.placeholder_binary_files,
            placeholder_for_large_files: self.placeholder_large_files,
        }
    }

    /// Returns the logging verbosity requested by the user.
    ///
    /// `--quiet` suppresses warnings and info messages, `--verbose` enables both warnings and info
    /// messages, and the default mode emits warnings only.
    pub fn verbosity(&self) -> Verbosity {
        if self.quiet {
            Verbosity::Quiet
        } else if self.verbose {
            Verbosity::Verbose
        } else {
            Verbosity::Normal
        }
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    use clap::Parser;
    use repo2markdown::{logger::Verbosity, renderer::RenderOptions};

    use super::Cli;

    #[test]
    fn cli_uses_default_paths_and_normal_verbosity() {
        let cli = Cli::try_parse_from(["repo2markdown"]).unwrap();

        assert_eq!(cli.root, PathBuf::from("."));
        assert_eq!(cli.origin, PathBuf::from("."));
        assert_eq!(cli.name, None);
        assert_eq!(cli.verbosity(), Verbosity::Normal);
    }

    #[test]
    fn cli_accepts_root_origin_and_name() {
        let cli = Cli::try_parse_from([
            "repo2markdown",
            "--root",
            "/repo",
            "--origin",
            "/repo/src",
            "--name",
            "My Project",
        ])
        .unwrap();

        assert_eq!(cli.root, PathBuf::from("/repo"));
        assert_eq!(cli.origin, PathBuf::from("/repo/src"));
        assert_eq!(cli.name.as_deref(), Some("My Project"));
    }

    #[test]
    fn cli_accepts_render_options() {
        let cli = Cli::try_parse_from([
            "repo2markdown",
            "--max-file-size",
            "5",
            "--placeholder-binary-files",
            "--placeholder-large-files",
        ])
        .unwrap();

        let options = cli.render_options();

        assert_eq!(options.max_file_size, 5);
        assert!(options.placeholder_for_binary_files);
        assert!(options.placeholder_for_large_files);
    }

    #[test]
    fn cli_defaults_unspecified_render_options() {
        let cli = Cli::try_parse_from(["repo2markdown"]).unwrap();

        assert_eq!(cli.render_options(), RenderOptions::default());
    }

    #[test]
    fn cli_accepts_quiet_verbosity() {
        let cli = Cli::try_parse_from(["repo2markdown", "--quiet"]).unwrap();

        assert_eq!(cli.verbosity(), Verbosity::Quiet);
    }

    #[test]
    fn cli_accepts_verbose_verbosity() {
        let cli = Cli::try_parse_from(["repo2markdown", "--verbose"]).unwrap();

        assert_eq!(cli.verbosity(), Verbosity::Verbose);
    }

    #[test]
    fn cli_rejects_quiet_and_verbose_together() {
        let result = Cli::try_parse_from(["repo2markdown", "--quiet", "--verbose"]);

        assert!(result.is_err());
    }

    #[test]
    fn cli_rejects_invalid_max_file_size() {
        let result = Cli::try_parse_from(["repo2markdown", "--max-file-size", "not-a-number"]);

        assert!(result.is_err());
    }

    #[test]
    fn cli_does_not_use_fenced_output_by_default() {
        let cli = Cli::try_parse_from(["repo2markdown"]).unwrap();

        assert!(!cli.fenced);
    }

    #[test]
    fn cli_accepts_fenced_output_option() {
        let cli = Cli::try_parse_from(["repo2markdown", "--fenced"]).unwrap();

        assert!(cli.fenced);
    }
}
