use clap::{
    Args, Command, CommandFactory, Parser, Subcommand, ValueEnum, builder::styling::Styles,
};

/// Plain styling for help output (no colors or formatting)
const STYLES: Styles = Styles::plain();

/// Plain help template for subcommands (matching Cyclopts style)
const SUBCOMMAND_HELP_TEMPLATE: &str = "\
Usage: {usage}

{about}

Commands:
{subcommands}

Options:
{options}";

/// Plain help template for leaf commands (matching Cyclopts style)
const COMMAND_HELP_TEMPLATE: &str = "\
Usage: {usage}

{about}

{all-args}";

fn apply_help_templates(cmd: Command) -> Command {
    let has_subcommands = cmd.get_subcommands().next().is_some();
    let template = if has_subcommands {
        SUBCOMMAND_HELP_TEMPLATE
    } else {
        COMMAND_HELP_TEMPLATE
    };
    let mut cmd = cmd.help_template(template);

    let subcommand_names: Vec<_> = cmd
        .get_subcommands()
        .map(|s| s.get_name().to_owned())
        .collect();
    for name in subcommand_names {
        cmd = cmd.mut_subcommand(&name, apply_help_templates);
    }
    cmd
}

fn parse_price(s: &str) -> Result<f64, String> {
    let s = s.strip_prefix('$').unwrap_or(s);
    s.parse::<f64>().map_err(|e| e.to_string())
}

#[derive(Args)]
pub struct SshArgs {
    /// Spot bid name to connect to (if not provided, shows interactive picker)
    pub bid_name: Option<String>,

    /// Node index for multi-instance tasks (remote commands default to all nodes; interactive default is 0)
    #[arg(long)]
    pub node: Option<i32>,

    /// Show the raw SSH command instead of executing it
    #[arg(long)]
    pub show: bool,

    /// Don't wait for instance to become available (fail immediately if not running)
    #[arg(long)]
    pub no_wait: bool,

    /// Command to run on the remote instance (if not provided, starts interactive shell)
    #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
    pub command: Vec<String>,
}

#[derive(Parser)]
#[command(name = "ml", styles = STYLES)]
#[command(about = "Mithril CLI", long_about = None)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Option<Commands>,

    /// Pass-through arguments for delegation to flow CLI
    #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
    pub args: Vec<String>,
}

impl Cli {
    /// Build command with custom help templates applied to all subcommands.
    pub fn build_command() -> Command {
        apply_help_templates(<Self as CommandFactory>::command())
    }
}

#[derive(Subcommand)]
pub enum Commands {
    #[command(about = "\
Configure CLI with API key and default project.

This interactive command creates or updates the config file at:
  ${XDG_CONFIG_HOME:-~/.config}/mithril/config.yaml

ENVIRONMENT VARIABLES (override config file):
    MITHRIL_API_KEY    API key
    MITHRIL_PROJECT    Project FID
    MITHRIL_PROFILE    Profile name (default: current_profile)
    MITHRIL_API_URL    API endpoint (default: https://api.mithril.ai)

  Priority: env vars > config file > defaults.
  When MITHRIL_API_KEY and MITHRIL_PROJECT are set, no config file is needed.

PROFILES:
  The config file supports multiple named profiles. Run 'ml setup' again
  to create additional profiles or switch between them. Use MITHRIL_PROFILE
  to select a profile without changing the config file.

API KEY:
  Get your API key at: https://app.mithril.ai/account/api-keys

EXAMPLES:
  Interactive setup (creates config file):
    ml setup

  Check setup status (non-interactive):
    ml setup --check

  non-interactive usage (no setup needed):
    export MITHRIL_API_KEY=your-key
    export MITHRIL_PROJECT=proj_xxx
    ml instance list

  Use a specific profile:
    MITHRIL_PROFILE=staging ml instance list")]
    Setup {
        /// Check setup status without modifying configuration
        #[arg(long)]
        check: bool,
    },

    /// SSH into an instance by spot bid name, or interactively select an instance
    Ssh {
        #[command(flatten)]
        args: SshArgs,
    },

    /// Manage compute instances (spot bids)
    #[command(subcommand)]
    Instance(InstanceCommands),

    /// Manage Kubernetes clusters
    #[command(subcommand)]
    K8s(KubernetesCommands),
}

#[derive(Subcommand)]
pub enum InstanceCommands {
    /// List instances/bids
    List {
        /// Show all bids (default: active only)
        #[arg(long)]
        all: bool,

        /// Filter by status
        #[arg(short, long, value_enum)]
        state: Option<BidStatus>,

        /// Maximum number of bids to show
        #[arg(long)]
        limit: Option<i32>,

        /// Output JSON for automation
        #[arg(long)]
        json: bool,
    },

    /// Create a new instance/bid
    Create {
        /// GPU instance type (e.g., a100, 8xa100, h100)
        #[arg(short = 'i', long)]
        instance_type: String,

        /// Region (e.g., us-west1-b)
        #[arg(short, long)]
        region: String,

        /// Bid name (default: auto-generated)
        #[arg(short, long)]
        name: Option<String>,

        /// Maximum hourly price in USD (e.g., 8.0 or $8.0)
        #[arg(short = 'm', long, value_parser = parse_price)]
        max_price_per_hour: f64,

        /// Number of instances in the bid
        #[arg(short = 'N', long, default_value = "1")]
        num_instances: i32,

        /// Project name (skip interactive selection)
        #[arg(short = 'p', long)]
        project: Option<String>,

        /// Attach to Kubernetes cluster (name)
        #[arg(long)]
        k8s: Option<String>,

        /// Wait for instances to start running
        #[arg(long)]
        wait: bool,

        /// Validate configuration without submitting
        #[arg(short, long)]
        dry_run: bool,

        /// Watch instance progress interactively
        #[arg(short, long)]
        watch: bool,

        /// Output JSON for automation
        #[arg(long)]
        json: bool,
    },

    /// Delete (cancel) an instance/bid
    Delete {
        /// Bid name or FID (interactive if omitted)
        bid_name: Option<String>,

        /// Skip confirmation prompt
        #[arg(short, long)]
        yes: bool,

        /// Cancel all active bids
        #[arg(long)]
        all: bool,

        /// Cancel bids matching wildcard pattern (e.g., 'dev-*')
        #[arg(short = 'n', long)]
        name_pattern: Option<String>,
    },

    /// Show detailed info about an instance/bid
    Info {
        /// Bid name or FID (interactive if omitted)
        bid_name: Option<String>,

        /// Output JSON for automation
        #[arg(long)]
        json: bool,
    },

    /// List available instance types
    ListTypes {
        /// Filter by region
        #[arg(short, long)]
        region: Option<String>,

        /// Show detailed GPU/memory info
        #[arg(short, long)]
        verbose: bool,

        /// Output JSON for automation
        #[arg(long)]
        json: bool,
    },

    /// SSH into an instance by spot bid name, or interactively select an instance
    Ssh {
        #[command(flatten)]
        args: SshArgs,
    },
}

#[derive(Clone, ValueEnum)]
pub enum BidStatus {
    Allocated,
    Pending,
    Open,
    Starting,
    Running,
    Paused,
    Preempting,
    Completed,
    Failed,
    Cancelled,
}

#[derive(Subcommand)]
pub enum KubernetesCommands {
    /// List Kubernetes clusters
    List {
        /// Show all clusters including terminated
        #[arg(long)]
        all: bool,

        /// Output JSON for automation
        #[arg(long)]
        json: bool,
    },

    /// Show detailed information about a Kubernetes cluster
    Info {
        /// Cluster name or FID (interactive if omitted)
        cluster: Option<String>,

        /// Output JSON for automation
        #[arg(long)]
        json: bool,
    },

    /// SSH into Kubernetes cluster control node
    Ssh {
        /// Cluster name or FID (interactive if omitted)
        cluster: Option<String>,

        /// Show the raw SSH command instead of executing it
        #[arg(long)]
        show: bool,

        /// Path to SSH identity file (auto-detected if omitted)
        #[arg(short = 'i', long)]
        identity: Option<String>,

        /// Command to run on the remote host
        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
        command: Vec<String>,
    },

    /// Fetch k8s cluster credentials and update local kubeconfig
    UpdateKubeconfig {
        /// Cluster name or FID (interactive if omitted)
        cluster: Option<String>,

        /// Path to SSH identity file (auto-detected if omitted)
        #[arg(short = 'i', long)]
        identity: Option<String>,

        /// Skip confirmation prompt
        #[arg(short, long)]
        yes: bool,

        /// Skip creating backup of existing kubeconfig
        #[arg(long)]
        no_backup: bool,

        /// Skip validation with kubectl
        #[arg(long)]
        skip_validation: bool,
    },
}
