//! Command-line interface for mongosh
//!
//! This module handles:
//! - Command-line argument parsing using clap
//! - Configuration loading and validation
//! - Application initialization and startup
//! - Mode selection (interactive vs script execution)
//! - Connection string parsing

use clap::{Parser, Subcommand};
use std::path::PathBuf;

use crate::config::Config;
use crate::error::Result;

/// Extract database name from MongoDB connection URI
///
/// # Arguments
/// * `uri` - MongoDB connection URI
///
/// # Returns
/// * `Option<String>` - Database name if found in URI
fn extract_database_from_uri(uri: &str) -> Option<String> {
    // Parse URI to extract database name
    // Format: mongodb://[username:password@]host[:port][/database][?options]

    // Find the part after the last '/' and before '?'
    if let Some(after_slash) = uri.split("://").nth(1) {
        // Find the database part: after host/port and before query params
        if let Some(path_part) = after_slash.split('/').nth(1) {
            // Remove query parameters if any
            let db_name = path_part.split('?').next().unwrap_or("");
            if !db_name.is_empty() {
                return Some(db_name.to_string());
            }
        }
    }
    None
}

/// MongoDB Shell - A high-performance Rust implementation
#[derive(Parser, Debug)]
#[command(
    name = "mongosh",
    version,
    about = "MongoDB Shell written in Rust",
    long_about = "A high-performance MongoDB Shell implementation in Rust with support for
interactive REPL, script execution, and all MongoDB operations."
)]
pub struct CliArgs {
    /// MongoDB connection URI
    ///
    /// Format: mongodb://[username:password@]host[:port][/database][?options]
    #[arg(value_name = "URI")]
    pub uri: Option<String>,

    /// Database name to use
    #[arg(short = 'd', long, value_name = "NAME")]
    pub database: Option<String>,

    /// Username for authentication
    #[arg(short = 'u', long, value_name = "USERNAME")]
    pub username: Option<String>,

    /// Password for authentication
    #[arg(short = 'p', long, value_name = "PASSWORD")]
    pub password: Option<String>,

    /// Authentication database
    #[arg(long, value_name = "NAME", default_value = "admin")]
    pub auth_database: String,

    /// Execute JavaScript file
    #[arg(short = 'f', long = "file", value_name = "FILE")]
    pub script_file: Option<PathBuf>,

    /// Evaluate JavaScript expression
    #[arg(long = "eval", value_name = "EXPR")]
    pub eval: Option<String>,

    /// Configuration file path
    #[arg(short = 'c', long = "config", value_name = "FILE")]
    pub config_file: Option<PathBuf>,

    /// Output format (json, json-pretty, table, compact)
    #[arg(long, value_name = "FORMAT")]
    pub format: Option<String>,

    /// Disable colored output
    #[arg(long = "no-color")]
    pub no_color: bool,

    /// Quiet mode (minimal output)
    #[arg(short = 'q', long)]
    pub quiet: bool,

    /// Verbose mode (detailed logging)
    #[arg(short = 'v', long)]
    pub verbose: bool,

    /// Very verbose mode (debug logging)
    #[arg(long = "vv")]
    pub very_verbose: bool,

    /// Connection timeout in seconds
    #[arg(long, value_name = "SECONDS")]
    pub timeout: Option<u64>,

    /// Disable automatic connection
    #[arg(long)]
    pub no_connect: bool,

    /// Enable TLS/SSL
    #[arg(long)]
    pub tls: bool,

    /// TLS certificate file
    #[arg(long, value_name = "FILE")]
    pub tls_cert_file: Option<PathBuf>,

    /// TLS CA certificate file
    #[arg(long, value_name = "FILE")]
    pub tls_ca_file: Option<PathBuf>,

    /// Disable TLS certificate validation
    #[arg(long)]
    pub tls_insecure: bool,

    /// Subcommands
    #[command(subcommand)]
    pub command: Option<Commands>,
}

/// Subcommands for mongosh
#[derive(Subcommand, Debug)]
pub enum Commands {
    /// Show version information
    Version,

    /// Generate shell completion script
    Completion {
        /// Shell type (bash, zsh, fish, powershell)
        #[arg(value_name = "SHELL")]
        shell: String,
    },

    /// Show configuration
    Config {
        /// Show effective configuration
        #[arg(long)]
        show: bool,

        /// Validate configuration file
        #[arg(long)]
        validate: bool,
    },
}

/// CLI interface handler
pub struct CliInterface {
    /// Parsed command-line arguments
    args: CliArgs,

    /// Loaded configuration
    config: Config,
}

impl CliInterface {
    /// Create a new CLI interface
    ///
    /// # Returns
    /// * `Result<Self>` - New CLI interface or error
    pub fn new() -> Result<Self> {
        let args = CliArgs::parse();
        let config = Self::load_config(&args)?;

        Ok(Self { args, config })
    }

    /// Parse command-line arguments
    ///
    /// # Returns
    /// * `CliArgs` - Parsed arguments
    pub fn parse_args() -> CliArgs {
        CliArgs::parse()
    }

    /// Load configuration from file and merge with arguments
    ///
    /// # Arguments
    /// * `args` - Command-line arguments
    ///
    /// # Returns
    /// * `Result<Config>` - Loaded configuration or error
    fn load_config(_args: &CliArgs) -> Result<Config> {
        // For now, just return default config
        // TODO: Load from file and environment in future
        Ok(Config::default())
    }

    /// Run the CLI application
    ///
    /// This is the main entry point that determines the execution mode
    /// and starts either the REPL or script execution.
    ///
    /// # Returns
    /// * `Result<()>` - Success or error
    pub async fn run(&self) -> Result<()> {
        todo!("Determine execution mode and start appropriate handler")
    }

    /// Get the MongoDB connection URI
    ///
    /// Constructs the URI from arguments or uses default from config
    ///
    /// # Returns
    /// * `String` - Connection URI
    pub fn get_connection_uri(&self) -> String {
        if let Some(uri) = &self.args.uri {
            uri.clone()
        } else {
            self.build_connection_uri()
        }
    }

    /// Build connection URI from individual arguments
    ///
    /// # Returns
    /// * `String` - Constructed connection URI
    fn build_connection_uri(&self) -> String {
        todo!("Build MongoDB connection URI from individual arguments")
    }

    /// Get the database name to use
    ///
    /// Priority:
    /// 1. --database/-d command line argument
    /// 2. Database name from connection URI
    /// 3. Default to "test"
    ///
    /// # Returns
    /// * `String` - Database name
    pub fn get_database(&self) -> String {
        // First check if database is explicitly provided via CLI argument
        if let Some(db) = &self.args.database {
            return db.clone();
        }

        // Then try to extract from URI if provided
        if let Some(uri) = &self.args.uri {
            if let Some(db) = extract_database_from_uri(uri) {
                return db;
            }
        }

        // Finally, fall back to default
        "test".to_string()
    }

    /// Check if running in interactive mode
    ///
    /// # Returns
    /// * `bool` - True if interactive mode
    pub fn is_interactive(&self) -> bool {
        self.args.script_file.is_none() && self.args.eval.is_none()
    }

    /// Check if running in script mode
    ///
    /// # Returns
    /// * `bool` - True if script mode
    pub fn is_script_mode(&self) -> bool {
        self.args.script_file.is_some() || self.args.eval.is_some()
    }

    /// Get the configuration
    ///
    /// # Returns
    /// * `&Config` - Reference to configuration
    pub fn config(&self) -> &Config {
        &self.config
    }

    /// Get mutable reference to configuration
    ///
    /// # Returns
    /// * `&mut Config` - Mutable reference to configuration
    pub fn config_mut(&mut self) -> &mut Config {
        &mut self.config
    }

    /// Get the CLI arguments
    ///
    /// # Returns
    /// * `&CliArgs` - Reference to arguments
    pub fn args(&self) -> &CliArgs {
        &self.args
    }

    /// Apply CLI arguments to configuration
    ///
    /// Overrides configuration values with CLI arguments where provided
    ///
    /// # Arguments
    /// * `config` - Configuration to modify
    fn apply_args_to_config(config: &mut Config, args: &CliArgs) {
        todo!("Apply CLI arguments to override config values")
    }

    /// Validate configuration and arguments
    ///
    /// # Returns
    /// * `Result<()>` - Ok if valid, error otherwise
    pub fn validate(&self) -> Result<()> {
        todo!("Validate configuration and argument combinations")
    }

    /// Handle subcommands
    ///
    /// # Returns
    /// * `Result<bool>` - True if subcommand was handled, false to continue
    pub async fn handle_subcommand(&self) -> Result<bool> {
        match &self.args.command {
            Some(Commands::Version) => {
                self.show_version();
                Ok(true)
            }
            Some(Commands::Completion { shell }) => {
                self.generate_completion(shell)?;
                Ok(true)
            }
            Some(Commands::Config { show, validate }) => {
                self.handle_config_command(*show, *validate)?;
                Ok(true)
            }
            None => Ok(false),
        }
    }

    /// Show version information
    fn show_version(&self) {
        println!("mongosh version {}", env!("CARGO_PKG_VERSION"));
        println!("Rust version: {}", env!("CARGO_PKG_RUST_VERSION"));
    }

    /// Generate shell completion script
    ///
    /// # Arguments
    /// * `shell` - Shell type
    ///
    /// # Returns
    /// * `Result<()>` - Success or error
    fn generate_completion(&self, shell: &str) -> Result<()> {
        todo!("Generate shell completion script for specified shell")
    }

    /// Handle config subcommand
    ///
    /// # Arguments
    /// * `show` - Whether to show configuration
    /// * `validate` - Whether to validate configuration
    ///
    /// # Returns
    /// * `Result<()>` - Success or error
    fn handle_config_command(&self, show: bool, validate: bool) -> Result<()> {
        if show {
            println!("{:#?}", self.config);
        }
        if validate {
            self.config.validate()?;
            println!("Configuration is valid");
        }
        Ok(())
    }

    /// Print banner with version and connection info
    pub fn print_banner(&self) {
        if !self.args.quiet {
            println!(
                "MongoDB Shell - Rust Edition v{}",
                env!("CARGO_PKG_VERSION")
            );
            println!("Connecting to: {}", self.get_connection_uri());
            println!();
        }
    }
}

impl Default for CliInterface {
    fn default() -> Self {
        Self::new().expect("Failed to create CLI interface")
    }
}

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

    #[test]
    fn test_cli_args_parsing() {
        // Test with no arguments
        let args = CliArgs::try_parse_from(vec!["mongosh"]).unwrap();
        assert!(args.uri.is_none());
        assert!(args.database.is_none());
    }

    #[test]
    fn test_cli_args_with_uri() {
        let args = CliArgs::try_parse_from(vec!["mongosh", "mongodb://localhost:27017"]).unwrap();
        assert_eq!(args.uri, Some("mongodb://localhost:27017".to_string()));
    }

    #[test]
    fn test_cli_args_with_flags() {
        let args = CliArgs::try_parse_from(vec!["mongosh", "--no-color", "--quiet"]).unwrap();
        assert!(args.no_color);
        assert!(args.quiet);
    }

    #[test]
    fn test_is_interactive_mode() {
        let args = CliArgs::try_parse_from(vec!["mongosh"]).unwrap();
        let config = Config::default();
        let cli = CliInterface { args, config };
        assert!(cli.is_interactive());
        assert!(!cli.is_script_mode());
    }

    #[test]
    fn test_extract_database_from_uri() {
        assert_eq!(
            extract_database_from_uri("mongodb://localhost:27017/mydb"),
            Some("mydb".to_string())
        );
        assert_eq!(
            extract_database_from_uri("mongodb://localhost:27017/mydb?retryWrites=true"),
            Some("mydb".to_string())
        );
        assert_eq!(
            extract_database_from_uri("mongodb://user:pass@localhost:27017/admin"),
            Some("admin".to_string())
        );
        assert_eq!(extract_database_from_uri("mongodb://localhost:27017"), None);
        assert_eq!(
            extract_database_from_uri("mongodb://localhost:27017/"),
            None
        );
    }

    #[test]
    fn test_get_database_priority() {
        // Test with explicit database argument
        let args = CliArgs::try_parse_from(vec!["mongosh", "-d", "mydb"]).unwrap();
        let config = Config::default();
        let cli = CliInterface { args, config };
        assert_eq!(cli.get_database(), "mydb");

        // Test with database in URI
        let args = CliArgs::try_parse_from(vec!["mongosh", "mongodb://localhost/admin"]).unwrap();
        let config = Config::default();
        let cli = CliInterface { args, config };
        assert_eq!(cli.get_database(), "admin");

        // Test explicit argument overrides URI
        let args =
            CliArgs::try_parse_from(vec!["mongosh", "mongodb://localhost/admin", "-d", "mydb"])
                .unwrap();
        let config = Config::default();
        let cli = CliInterface { args, config };
        assert_eq!(cli.get_database(), "mydb");

        // Test default
        let args = CliArgs::try_parse_from(vec!["mongosh"]).unwrap();
        let config = Config::default();
        let cli = CliInterface { args, config };
        assert_eq!(cli.get_database(), "test");
    }
}
