use clap::{Args, Parser, Subcommand};

/// The CLI entry point for the closure application.
#[derive(Parser, Debug)]
#[command(name = "closure")]
#[command(about = "Manage coding agent configurations and credentials")]
pub struct Cli {
    #[command(subcommand)]
    pub command: Option<Commands>,

    #[arg(long, global = true)]
    pub tui: bool,
}

/// Available top-level commands.
#[derive(Subcommand, Debug)]
pub enum Commands {
    /// Manage Claude Code agent configurations.
    #[command(alias = "cc")]
    ClaudeCode {
        #[command(subcommand)]
        command: AgentCommands,
    },
    /// Manage Codex agent configurations.
    #[command(alias = "cx")]
    Codex {
        #[command(subcommand)]
        command: AgentCommands,
    },
    /// Authenticate with or lock the encrypted vault.
    Auth {
        #[command(subcommand)]
        command: Option<AuthCommands>,
    },
    /// Initialize a new encrypted vault
    Init,
    /// Sync workdata with a git-backed sync directory
    Sync,
    /// Inspect and restore local backup snapshots
    Backup {
        #[command(subcommand)]
        command: BackupCommands,
    },
    /// Run local health checks for the vault and sync setup
    Doctor,
    /// Verify vault structure and integrity
    Vault {
        #[command(subcommand)]
        command: VaultCommands,
    },
    /// Set the git remote for the sync directory
    #[command(name = "set-upstream")]
    SetUpstream { url: String },
    /// Export Claude Code session files to a ZIP archive.
    Session {
        #[command(subcommand)]
        command: SessionCommands,
    },
}

/// Commands for managing a coding agent.
#[derive(Subcommand, Debug)]
pub enum AgentCommands {
    /// Manage provider configurations for the agent.
    #[command(alias = "p")]
    Provider {
        #[command(subcommand)]
        command: ProviderCommands,
    },
}

/// Commands for managing provider endpoints.
#[derive(Subcommand, Debug)]
pub enum ProviderCommands {
    /// Select a provider to use as the active endpoint.
    #[command(alias = "s")]
    Select { name: String },
    /// List all configured providers.
    #[command(aliases = ["l", "ls"])]
    List,
    /// Add a new provider configuration.
    #[command(alias = "a")]
    Add {
        name: String,
        #[command(flatten)]
        args: ProviderAddEditArgs,
    },
    /// Edit an existing provider configuration.
    #[command(alias = "e")]
    Edit {
        name: String,
        #[command(flatten)]
        args: ProviderAddEditArgs,
    },
    /// Remove a provider configuration.
    #[command(alias = "r")]
    Remove { name: String },
    /// Show full details of a provider.
    #[command(alias = "sh")]
    Show { name: String },
    /// Check endpoint connectivity and measure TTFT.
    #[command(alias = "ch")]
    Check { name: String },
    /// Add a provider from a built-in preset template.
    #[command(alias = "pt")]
    Preset {
        /// Template name (openai-compatible, anthropic-compatible, deepseek)
        template: String,
        /// Name for the new provider
        name: String,
        #[command(flatten)]
        args: ProviderAddEditArgs,
    },
    /// List available preset templates.
    #[command(name = "list-presets", aliases = ["lsp", "presets"])]
    ListPresets,
}

/// Arguments for adding or editing a provider configuration.
#[derive(Args, Debug, Clone)]
pub struct ProviderAddEditArgs {
    /// Model name
    #[arg(long)]
    pub model: Option<String>,

    /// API key (Codex only)
    #[arg(long)]
    pub key: Option<String>,

    /// Base URL (Codex only)
    #[arg(long)]
    pub url: Option<String>,

    /// Model provider name, e.g. "OpenAI" (Codex only)
    #[arg(long = "provider")]
    pub model_provider: Option<String>,

    /// Review model name (Codex only)
    #[arg(long = "review-model")]
    pub review_model: Option<String>,

    /// Environment variable KEY=VALUE (Claude Code only, repeatable)
    #[arg(long = "env", value_parser = parse_env_kv)]
    pub env: Vec<(String, String)>,
}

fn parse_env_kv(arg: &str) -> Result<(String, String), String> {
    let (k, v) = arg
        .split_once('=')
        .ok_or_else(|| format!("expected KEY=VALUE, got: {}", arg))?;
    Ok((k.to_string(), v.to_string()))
}

/// Commands for vault authentication operations.
#[derive(Subcommand, Debug)]
pub enum AuthCommands {
    /// Log out and lock the vault.
    Logout,
}

#[derive(Subcommand, Debug)]
pub enum BackupCommands {
    /// List available backups
    List,
    /// Inspect a backup snapshot
    Inspect { id: String },
    /// Restore a backup snapshot
    Restore { id: String },
}

#[derive(Subcommand, Debug)]
pub enum VaultCommands {
    /// Verify vault files and checksums
    Verify,
}

#[derive(Subcommand, Debug)]
pub enum SessionCommands {
    /// Export sessions to a ZIP archive
    Export {
        /// Output ZIP file path
        output: std::path::PathBuf,
    },
}
