//! Core CLI library + python extension entrypoint for flow/mithril-cli.

mod api;
mod cli;
mod commands;
mod config;
mod constants;
mod util;

use anyhow::Result;
use clap::FromArgMatches;

pub use cli::{Cli, Commands, InstanceCommands, KubernetesCommands};

/// Initialize logging with MITHRIL_CLI_LOG environment variable support.
/// Uses try_init() to safely handle being called multiple times.
pub fn init_logging() {
    let _ = env_logger::Builder::from_env(
        env_logger::Env::default().filter_or("MITHRIL_CLI_LOG", "warn"),
    )
    .try_init();
}

/// Core CLI execution logic, shared between binary and pyo3 extension.
pub async fn run_cli(args: Vec<String>) -> Result<i32> {
    util::setup_signal_handlers();
    // Preserve the exact invocation (ml/mithril/path) so setup can
    // re-invoke launch reliably without hardcoding a binary name.
    let invoked_as = args.first().map(String::as_str).unwrap_or("ml");

    // Handle clap errors (help, version, parse errors) by printing and returning exit code
    // Use Cli::build_command() to get our custom Command with help templates applied
    let cli = match Cli::build_command().try_get_matches_from(&args) {
        Ok(mut matches) => Cli::from_arg_matches_mut(&mut matches)?,
        Err(e) => {
            // Clap errors include help output, version, and parse errors
            // Print them and return appropriate exit code
            let _ = e.print();
            return Ok(e.exit_code());
        }
    };

    match cli.command {
        Some(Commands::Setup { check }) => {
            if check {
                return commands::setup::run_check().await;
            }
            commands::setup::run(invoked_as).await?;
        }
        Some(Commands::Ssh { args }) => {
            commands::ssh::run(
                args.bid_name,
                args.node,
                args.show,
                args.no_wait,
                args.command,
            )
            .await?;
        }
        Some(Commands::Instance(subcmd)) => match subcmd {
            InstanceCommands::List {
                all,
                state,
                limit,
                json,
            } => {
                commands::instance::list::run(all, state, limit, json).await?;
            }
            InstanceCommands::Create {
                instance_type,
                region,
                name,
                max_price_per_hour,
                num_instances,
                project,
                k8s,
                wait,
                dry_run,
                watch,
                json,
            } => {
                commands::instance::create::run(
                    instance_type,
                    region,
                    name,
                    max_price_per_hour,
                    num_instances,
                    project,
                    k8s,
                    wait,
                    dry_run,
                    watch,
                    json,
                )
                .await?;
            }
            InstanceCommands::Delete {
                bid_name,
                yes,
                all,
                name_pattern,
            } => {
                commands::instance::delete::run(bid_name, yes, all, name_pattern).await?;
            }
            InstanceCommands::Info { bid_name, json } => {
                commands::instance::info::run(bid_name, json).await?;
            }
            InstanceCommands::ListTypes {
                region,
                verbose,
                json,
            } => {
                commands::instance::list_types::run(region, verbose, json).await?;
            }
            InstanceCommands::Ssh { args } => {
                commands::ssh::run(
                    args.bid_name,
                    args.node,
                    args.show,
                    args.no_wait,
                    args.command,
                )
                .await?;
            }
        },
        Some(Commands::K8s(subcmd)) => match subcmd {
            KubernetesCommands::List { all, json } => {
                commands::kubernetes::list::run(all, json).await?;
            }
            KubernetesCommands::Info { cluster, json } => {
                commands::kubernetes::info::run(cluster, json).await?;
            }
            KubernetesCommands::Ssh {
                cluster,
                show,
                identity,
                command,
            } => {
                commands::kubernetes::ssh::run(cluster, show, identity, command).await?;
            }
            KubernetesCommands::UpdateKubeconfig {
                cluster,
                identity,
                yes,
                no_backup,
                skip_validation,
            } => {
                commands::kubernetes::update_kubeconfig::run(
                    cluster,
                    identity,
                    yes,
                    no_backup,
                    skip_validation,
                )
                .await?;
            }
        },
        None => {
            // No mcli-specific command; delegate to flow CLI
            return Ok(-1);
        }
    }

    Ok(0)
}

// pyo3 extension module, only built by maturin
#[cfg(feature = "extension-module")]
mod python {
    use super::*;
    use pyo3::prelude::*;

    #[pyfunction]
    fn run(py: Python<'_>, args: Vec<String>) -> PyResult<i32> {
        crate::init_logging();

        py.detach(|| {
            let rt = tokio::runtime::Runtime::new()
                .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?;

            rt.block_on(async { run_cli(args).await })
                .map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))
        })
    }

    #[pymodule]
    pub fn _mcli(m: &Bound<'_, PyModule>) -> PyResult<()> {
        m.add_function(wrap_pyfunction!(run, m)?)?;
        m.add_function(wrap_pyfunction!(crate::config::python::py_load_config, m)?)?;
        m.add_class::<crate::config::MithrilConfig>()?;
        Ok(())
    }
}

#[cfg(feature = "extension-module")]
pub use python::_mcli;
