mod api;
mod commands;
mod config;
mod errors;
mod output;
mod ui;
mod utils;

use anyhow::Result;
use api::ApiClient;
use clap::{Parser, Subcommand, ValueEnum};
use config::Config;
use output::OutputFormat;
use std::path::PathBuf;

#[derive(Debug, Clone, Copy, ValueEnum)]
enum Format {
    Pretty,
    Json,
    Yaml,
}

impl From<Format> for OutputFormat {
    fn from(f: Format) -> Self {
        match f {
            Format::Pretty => OutputFormat::Pretty,
            Format::Json => OutputFormat::Json,
            Format::Yaml => OutputFormat::Yaml,
        }
    }
}

#[derive(Parser)]
#[command(name = "hindsight")]
#[command(about = "Hindsight CLI - Semantic memory system", long_about = None)]
#[command(version)]
#[command(before_help = get_before_help())]
#[command(after_help = get_after_help())]
struct Cli {
    /// Output format (pretty, json, yaml)
    #[arg(short = 'o', long, global = true, default_value = "pretty")]
    output: Format,

    /// Show verbose output including full requests and responses
    #[arg(short = 'v', long, global = true)]
    verbose: bool,

    #[command(subcommand)]
    command: Commands,
}

fn get_after_help() -> String {
    let config = config::Config::load().ok();
    let (api_url, source) = match &config {
        Some(c) => (c.api_url.as_str(), c.source.to_string()),
        None => ("http://localhost:8888", "default".to_string()),
    };
    format!(
        "Current API URL: {} (from {})\n\nRun 'hindsight configure' to change the API URL.",
        api_url, source
    )
}

fn get_before_help() -> &'static str {
    ui::get_logo()
}

#[derive(Subcommand)]
enum Commands {
    /// Manage banks (list, create, update, profile, stats, mission, graph, delete)
    #[command(subcommand)]
    Bank(BankCommands),

    /// Manage memories (list, get, recall, reflect, retain, clear)
    #[command(subcommand)]
    Memory(MemoryCommands),

    /// Manage documents (list, get, delete)
    #[command(subcommand)]
    Document(DocumentCommands),

    /// Manage entities (list, get, regenerate)
    #[command(subcommand)]
    Entity(EntityCommands),

    /// Manage tags (list)
    #[command(subcommand)]
    Tag(TagCommands),

    /// Manage chunks (get)
    #[command(subcommand)]
    Chunk(ChunkCommands),

    /// Manage async operations (list, get, cancel)
    #[command(subcommand)]
    Operation(OperationCommands),

    /// Manage mental models (user-curated summaries)
    #[command(subcommand)]
    MentalModel(MentalModelCommands),

    /// Manage directives (behavioral rules)
    #[command(subcommand)]
    Directive(DirectiveCommands),

    /// Check API health status
    Health,

    /// Get Prometheus metrics
    Metrics,

    /// Get API version information
    Version,

    /// Interactive TUI explorer (k9s-style) for navigating banks, memories, entities, and performing recall/reflect
    #[command(alias = "tui")]
    Explore,

    /// Launch the web-based control plane UI
    Ui,

    /// Configure the CLI (API URL, API key, etc.)
    #[command(after_help = "Configuration priority:\n  1. Environment variables (HINDSIGHT_API_URL, HINDSIGHT_API_KEY) - highest priority\n  2. Config file (~/.hindsight/config)\n  3. Default (http://localhost:8888)")]
    Configure {
        /// API URL to connect to (interactive prompt if not provided)
        #[arg(long)]
        api_url: Option<String>,
        /// API key for authentication (sent as Bearer token)
        #[arg(long)]
        api_key: Option<String>,
    },
}

#[derive(Subcommand)]
enum BankCommands {
    /// List all banks
    List,

    /// Create a new bank
    Create {
        /// Bank ID
        bank_id: String,

        /// Bank name
        #[arg(short = 'n', long)]
        name: Option<String>,

        /// Mission statement
        #[arg(short = 'm', long)]
        mission: Option<String>,

        /// Skepticism trait (1-5)
        #[arg(long, value_parser = clap::value_parser!(i64).range(1..=5))]
        skepticism: Option<i64>,

        /// Literalism trait (1-5)
        #[arg(long, value_parser = clap::value_parser!(i64).range(1..=5))]
        literalism: Option<i64>,

        /// Empathy trait (1-5)
        #[arg(long, value_parser = clap::value_parser!(i64).range(1..=5))]
        empathy: Option<i64>,
    },

    /// Update bank properties (partial update)
    Update {
        /// Bank ID
        bank_id: String,

        /// Bank name
        #[arg(short = 'n', long)]
        name: Option<String>,

        /// Mission statement
        #[arg(short = 'm', long)]
        mission: Option<String>,

        /// Skepticism trait (1-5)
        #[arg(long, value_parser = clap::value_parser!(i64).range(1..=5))]
        skepticism: Option<i64>,

        /// Literalism trait (1-5)
        #[arg(long, value_parser = clap::value_parser!(i64).range(1..=5))]
        literalism: Option<i64>,

        /// Empathy trait (1-5)
        #[arg(long, value_parser = clap::value_parser!(i64).range(1..=5))]
        empathy: Option<i64>,
    },

    /// Get bank disposition and profile
    Disposition {
        /// Bank ID
        bank_id: String,
    },

    /// Get memory statistics for a bank
    Stats {
        /// Bank ID
        bank_id: String,
    },

    /// Set bank name
    Name {
        /// Bank ID
        bank_id: String,

        /// Bank name
        name: String,
    },

    /// Set bank mission
    Mission {
        /// Bank ID
        bank_id: String,

        /// Mission statement
        mission: String,
    },

    /// Set or merge bank background (deprecated: use mission instead)
    #[command(hide = true)]
    Background {
        /// Bank ID
        bank_id: String,

        /// Background content
        content: String,

        /// Skip automatic disposition inference
        #[arg(long)]
        no_update_disposition: bool,
    },

    /// Get memory graph data
    Graph {
        /// Bank ID
        bank_id: String,

        /// Filter by fact type (world, experience, opinion)
        #[arg(short = 't', long)]
        fact_type: Option<String>,

        /// Maximum nodes to return
        #[arg(short = 'l', long, default_value = "1000")]
        limit: i64,
    },

    /// Delete a bank and all its data
    Delete {
        /// Bank ID
        bank_id: String,

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

    /// Trigger consolidation to create/update observations
    Consolidate {
        /// Bank ID
        bank_id: String,

        /// Wait for consolidation to complete (poll for status)
        #[arg(long)]
        wait: bool,

        /// Poll interval in seconds (only used with --wait)
        #[arg(long, default_value = "10")]
        poll_interval: u64,
    },

    /// Clear all observations for a bank
    ClearObservations {
        /// Bank ID
        bank_id: String,

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

    /// Get bank configuration (hierarchical overrides)
    Config {
        /// Bank ID
        bank_id: String,

        /// Show only bank-specific overrides (not full resolved config)
        #[arg(long)]
        overrides_only: bool,
    },

    /// Update bank configuration (set hierarchical overrides)
    SetConfig {
        /// Bank ID
        bank_id: String,

        /// LLM provider override
        #[arg(long)]
        llm_provider: Option<String>,

        /// LLM model override
        #[arg(long)]
        llm_model: Option<String>,

        /// LLM API key override
        #[arg(long)]
        llm_api_key: Option<String>,

        /// LLM base URL override
        #[arg(long)]
        llm_base_url: Option<String>,
    },

    /// Reset bank configuration to defaults (remove all overrides)
    ResetConfig {
        /// Bank ID
        bank_id: String,

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

#[derive(Subcommand)]
enum MemoryCommands {
    /// List memory units with pagination
    List {
        /// Bank ID
        bank_id: String,

        /// Filter by fact type (world, experience, opinion)
        #[arg(short = 't', long)]
        fact_type: Option<String>,

        /// Full-text search query
        #[arg(short = 'q', long)]
        query: Option<String>,

        /// Maximum number of results
        #[arg(short = 'l', long, default_value = "100")]
        limit: i64,

        /// Offset for pagination
        #[arg(short = 's', long, default_value = "0")]
        offset: i64,
    },

    /// Get a specific memory unit by ID
    Get {
        /// Bank ID
        bank_id: String,

        /// Memory unit ID
        memory_id: String,
    },

    /// Recall memories using semantic search
    Recall {
        /// Bank ID
        bank_id: String,

        /// Search query
        query: String,

        /// Fact types to search (world, experience, opinion)
        #[arg(short = 't', long, value_delimiter = ',', default_values = &["world", "experience", "opinion"])]
        fact_type: Vec<String>,

        /// Thinking budget (low, mid, high)
        #[arg(short = 'b', long, default_value = "mid")]
        budget: String,

        /// Maximum tokens for results
        #[arg(long, default_value = "4096")]
        max_tokens: i64,

        /// Show trace information
        #[arg(long)]
        trace: bool,

        /// Include chunks in results
        #[arg(long)]
        include_chunks: bool,

        /// Maximum tokens for chunks (only used with --include-chunks)
        #[arg(long, default_value = "8192")]
        chunk_max_tokens: i64,
    },

    /// Generate answers using bank identity (reflect/reasoning)
    Reflect {
        /// Bank ID
        bank_id: String,

        /// Query to reflect on
        query: String,

        /// Thinking budget (low, mid, high)
        #[arg(short = 'b', long, default_value = "mid")]
        budget: String,

        /// Additional context
        #[arg(short = 'c', long)]
        context: Option<String>,

        /// Maximum tokens for the response (server default: 4096)
        #[arg(short = 'm', long)]
        max_tokens: Option<i64>,

        /// Path to JSON schema file for structured output
        #[arg(short = 's', long)]
        schema: Option<PathBuf>,
    },

    /// Store (retain) a single memory
    Retain {
        /// Bank ID
        bank_id: String,

        /// Memory content
        content: String,

        /// Document ID (auto-generated if not provided)
        #[arg(short = 'd', long)]
        doc_id: Option<String>,

        /// Context for the memory
        #[arg(short = 'c', long)]
        context: Option<String>,

        /// Queue for background processing
        #[arg(long)]
        r#async: bool,
    },

    /// Bulk import memories from files (retain)
    RetainFiles {
        /// Bank ID
        bank_id: String,

        /// Path to file or directory
        path: PathBuf,

        /// Search directories recursively
        #[arg(short = 'r', long, default_value = "true")]
        recursive: bool,

        /// Context for all memories
        #[arg(short = 'c', long)]
        context: Option<String>,

        /// Queue for background processing
        #[arg(long)]
        r#async: bool,
    },

    /// Delete a memory unit
    Delete {
        /// Bank ID
        bank_id: String,

        /// Memory unit ID
        unit_id: String,
    },

    /// Clear all memories for a bank
    Clear {
        /// Bank ID
        bank_id: String,

        /// Fact type to clear (world, agent, opinion). If not specified, clears all types.
        #[arg(short = 't', long, value_parser = ["world", "agent", "opinion"])]
        fact_type: Option<String>,

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

#[derive(Subcommand)]
enum DocumentCommands {
    /// List documents for a bank
    List {
        /// Bank ID
        bank_id: String,

        /// Search query to filter documents
        #[arg(short = 'q', long)]
        query: Option<String>,

        /// Filter by date (yesterday, today, YYYY-MM-DD, or all)
        #[arg(short = 'd', long)]
        date: Option<String>,

        /// Maximum number of results
        #[arg(short = 'l', long, default_value = "100")]
        limit: i32,

        /// Offset for pagination
        #[arg(short = 's', long, default_value = "0")]
        offset: i32,
    },

    /// Get a specific document by ID
    Get {
        /// Bank ID
        bank_id: String,

        /// Document ID
        document_id: String,
    },

    /// Delete a document and all its memory units
    Delete {
        /// Bank ID
        bank_id: String,

        /// Document ID
        document_id: String,
    },
}

#[derive(Subcommand)]
enum EntityCommands {
    /// List entities for a bank
    List {
        /// Bank ID
        bank_id: String,

        /// Maximum number of results
        #[arg(short = 'l', long, default_value = "100")]
        limit: i64,
    },

    /// Get detailed information about an entity
    Get {
        /// Bank ID
        bank_id: String,

        /// Entity ID
        entity_id: String,
    },

    /// Regenerate observations for an entity
    Regenerate {
        /// Bank ID
        bank_id: String,

        /// Entity ID
        entity_id: String,
    },
}

#[derive(Subcommand)]
enum OperationCommands {
    /// List async operations for a bank
    List {
        /// Bank ID
        bank_id: String,
    },

    /// Get the status of a specific operation
    Get {
        /// Bank ID
        bank_id: String,

        /// Operation ID
        operation_id: String,
    },

    /// Cancel a pending async operation
    Cancel {
        /// Bank ID
        bank_id: String,

        /// Operation ID
        operation_id: String,
    },
}

#[derive(Subcommand)]
enum TagCommands {
    /// List tags in a bank
    List {
        /// Bank ID
        bank_id: String,

        /// Wildcard search query (e.g., 'user:*')
        #[arg(short = 'q', long)]
        query: Option<String>,

        /// Maximum number of results
        #[arg(short = 'l', long, default_value = "100")]
        limit: i64,

        /// Offset for pagination
        #[arg(short = 's', long, default_value = "0")]
        offset: i64,
    },
}

#[derive(Subcommand)]
enum ChunkCommands {
    /// Get a specific chunk by ID
    Get {
        /// Chunk ID
        chunk_id: String,
    },
}

#[derive(Subcommand)]
enum MentalModelCommands {
    /// List mental models for a bank
    List {
        /// Bank ID
        bank_id: String,
    },

    /// Get a specific mental model
    Get {
        /// Bank ID
        bank_id: String,

        /// Mental model ID
        mental_model_id: String,
    },

    /// Create a new mental model
    Create {
        /// Bank ID
        bank_id: String,

        /// Mental model name
        name: String,

        /// Source query to generate the mental model from
        source_query: String,

        /// Optional custom ID for the mental model (alphanumeric lowercase with hyphens)
        #[arg(long)]
        id: Option<String>,
    },

    /// Update a mental model
    Update {
        /// Bank ID
        bank_id: String,

        /// Mental model ID
        mental_model_id: String,

        /// New name
        #[arg(long)]
        name: Option<String>,
    },

    /// Delete a mental model
    Delete {
        /// Bank ID
        bank_id: String,

        /// Mental model ID
        mental_model_id: String,

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

    /// Refresh a mental model (re-run the source query)
    Refresh {
        /// Bank ID
        bank_id: String,

        /// Mental model ID
        mental_model_id: String,
    },
}

#[derive(Subcommand)]
enum DirectiveCommands {
    /// List directives for a bank
    List {
        /// Bank ID
        bank_id: String,
    },

    /// Get a specific directive
    Get {
        /// Bank ID
        bank_id: String,

        /// Directive ID
        directive_id: String,
    },

    /// Create a new directive
    Create {
        /// Bank ID
        bank_id: String,

        /// Directive name
        name: String,

        /// Directive content (the text to inject into prompts)
        content: String,
    },

    /// Update a directive
    Update {
        /// Bank ID
        bank_id: String,

        /// Directive ID
        directive_id: String,

        /// New name
        #[arg(long)]
        name: Option<String>,

        /// New content
        #[arg(long)]
        content: Option<String>,
    },

    /// Delete a directive
    Delete {
        /// Bank ID
        bank_id: String,

        /// Directive ID
        directive_id: String,

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

fn main() {
    if let Err(_) = run() {
        std::process::exit(1);
    }
}

fn run() -> Result<()> {
    let cli = Cli::parse();

    let output_format: OutputFormat = cli.output.into();
    let verbose = cli.verbose;

    // Handle configure command before loading full config (it doesn't need API client)
    if let Commands::Configure { api_url, api_key } = cli.command {
        return handle_configure(api_url, api_key, output_format);
    }

    // Handle ui command - needs config but not API client
    if let Commands::Ui = cli.command {
        return handle_ui(output_format);
    }

    // Load configuration
    let config = Config::from_env().unwrap_or_else(|e| {
        ui::print_error(&format!("Configuration error: {}", e));
        errors::print_config_help();
        std::process::exit(1);
    });

    let api_url = config.api_url().to_string();
    let api_key = config.api_key.clone();

    // Create API client
    let client = ApiClient::new(api_url.clone(), api_key).unwrap_or_else(|e| {
        errors::handle_api_error(e, &api_url);
    });

    // Execute command and handle errors
    let result: Result<()> = match cli.command {
        Commands::Configure { .. } => unreachable!(), // Handled above
        Commands::Ui => unreachable!(), // Handled above
        Commands::Explore => commands::explore::run(&client),

        // Health, Metrics, and Version
        Commands::Health => commands::health::health(&client, verbose, output_format),
        Commands::Metrics => commands::health::metrics(&client, verbose, output_format),
        Commands::Version => commands::health::version(&client, verbose, output_format),

        // Bank commands
        Commands::Bank(bank_cmd) => match bank_cmd {
            BankCommands::List => commands::bank::list(&client, verbose, output_format),
            BankCommands::Create { bank_id, name, mission, skepticism, literalism, empathy } => {
                commands::bank::create(&client, &bank_id, name, mission, skepticism, literalism, empathy, verbose, output_format)
            }
            BankCommands::Update { bank_id, name, mission, skepticism, literalism, empathy } => {
                commands::bank::update(&client, &bank_id, name, mission, skepticism, literalism, empathy, verbose, output_format)
            }
            BankCommands::Disposition { bank_id } => commands::bank::disposition(&client, &bank_id, verbose, output_format),
            BankCommands::Stats { bank_id } => commands::bank::stats(&client, &bank_id, verbose, output_format),
            BankCommands::Name { bank_id, name } => commands::bank::update_name(&client, &bank_id, &name, verbose, output_format),
            BankCommands::Mission { bank_id, mission } => {
                commands::bank::mission(&client, &bank_id, &mission, verbose, output_format)
            }
            BankCommands::Background { bank_id, content, no_update_disposition } => {
                commands::bank::update_background(&client, &bank_id, &content, no_update_disposition, verbose, output_format)
            }
            BankCommands::Graph { bank_id, fact_type, limit } => {
                commands::bank::graph(&client, &bank_id, fact_type, limit, verbose, output_format)
            }
            BankCommands::Delete { bank_id, yes } => {
                commands::bank::delete(&client, &bank_id, yes, verbose, output_format)
            }
            BankCommands::Consolidate { bank_id, wait, poll_interval } => {
                commands::bank::consolidate(&client, &bank_id, wait, poll_interval, verbose, output_format)
            }
            BankCommands::ClearObservations { bank_id, yes } => {
                commands::bank::clear_observations(&client, &bank_id, yes, verbose, output_format)
            }
            BankCommands::Config { bank_id, overrides_only } => {
                commands::bank::config(&client, &bank_id, overrides_only, verbose, output_format)
            }
            BankCommands::SetConfig { bank_id, llm_provider, llm_model, llm_api_key, llm_base_url } => {
                commands::bank::set_config(&client, &bank_id, llm_provider, llm_model, llm_api_key, llm_base_url, verbose, output_format)
            }
            BankCommands::ResetConfig { bank_id, yes } => {
                commands::bank::reset_config(&client, &bank_id, yes, verbose, output_format)
            }
        },

        // Memory commands
        Commands::Memory(memory_cmd) => match memory_cmd {
            MemoryCommands::List { bank_id, fact_type, query, limit, offset } => {
                commands::memory::list(&client, &bank_id, fact_type, query, limit, offset, verbose, output_format)
            }
            MemoryCommands::Get { bank_id, memory_id } => {
                commands::memory::get(&client, &bank_id, &memory_id, verbose, output_format)
            }
            MemoryCommands::Recall { bank_id, query, fact_type, budget, max_tokens, trace, include_chunks, chunk_max_tokens } => {
                commands::memory::recall(&client, &bank_id, query, fact_type, budget, max_tokens, trace, include_chunks, chunk_max_tokens, verbose, output_format)
            }
            MemoryCommands::Reflect { bank_id, query, budget, context, max_tokens, schema } => {
                commands::memory::reflect(&client, &bank_id, query, budget, context, max_tokens, schema, verbose, output_format)
            }
            MemoryCommands::Retain { bank_id, content, doc_id, context, r#async } => {
                commands::memory::retain(&client, &bank_id, content, doc_id, context, r#async, verbose, output_format)
            }
            MemoryCommands::RetainFiles { bank_id, path, recursive, context, r#async } => {
                commands::memory::retain_files(&client, &bank_id, path, recursive, context, r#async, verbose, output_format)
            }
            MemoryCommands::Delete { bank_id, unit_id } => {
                commands::memory::delete(&client, &bank_id, &unit_id, verbose, output_format)
            }
            MemoryCommands::Clear { bank_id, fact_type, yes } => {
                commands::memory::clear(&client, &bank_id, fact_type, yes, verbose, output_format)
            }
        },

        // Document commands
        Commands::Document(doc_cmd) => match doc_cmd {
            DocumentCommands::List { bank_id, query, date, limit, offset } => {
                commands::document::list(&client, &bank_id, query, date, limit, offset, verbose, output_format)
            }
            DocumentCommands::Get { bank_id, document_id } => {
                commands::document::get(&client, &bank_id, &document_id, verbose, output_format)
            }
            DocumentCommands::Delete { bank_id, document_id } => {
                commands::document::delete(&client, &bank_id, &document_id, verbose, output_format)
            }
        },

        // Entity commands
        Commands::Entity(entity_cmd) => match entity_cmd {
            EntityCommands::List { bank_id, limit } => {
                commands::entity::list(&client, &bank_id, limit, verbose, output_format)
            }
            EntityCommands::Get { bank_id, entity_id } => {
                commands::entity::get(&client, &bank_id, &entity_id, verbose, output_format)
            }
            EntityCommands::Regenerate { bank_id, entity_id } => {
                commands::entity::regenerate(&client, &bank_id, &entity_id, verbose, output_format)
            }
        },

        // Tag commands
        Commands::Tag(tag_cmd) => match tag_cmd {
            TagCommands::List { bank_id, query, limit, offset } => {
                commands::tag::list(&client, &bank_id, query, limit, offset, verbose, output_format)
            }
        },

        // Chunk commands
        Commands::Chunk(chunk_cmd) => match chunk_cmd {
            ChunkCommands::Get { chunk_id } => {
                commands::chunk::get(&client, &chunk_id, verbose, output_format)
            }
        },

        // Operation commands
        Commands::Operation(op_cmd) => match op_cmd {
            OperationCommands::List { bank_id } => {
                commands::operation::list(&client, &bank_id, verbose, output_format)
            }
            OperationCommands::Get { bank_id, operation_id } => {
                commands::operation::get(&client, &bank_id, &operation_id, verbose, output_format)
            }
            OperationCommands::Cancel { bank_id, operation_id } => {
                commands::operation::cancel(&client, &bank_id, &operation_id, verbose, output_format)
            }
        },

        // Mental model commands
        Commands::MentalModel(mm_cmd) => match mm_cmd {
            MentalModelCommands::List { bank_id } => {
                commands::mental_model::list(&client, &bank_id, verbose, output_format)
            }
            MentalModelCommands::Get { bank_id, mental_model_id } => {
                commands::mental_model::get(&client, &bank_id, &mental_model_id, verbose, output_format)
            }
            MentalModelCommands::Create { bank_id, name, source_query, id } => {
                commands::mental_model::create(&client, &bank_id, &name, &source_query, id.as_deref(), verbose, output_format)
            }
            MentalModelCommands::Update { bank_id, mental_model_id, name } => {
                commands::mental_model::update(&client, &bank_id, &mental_model_id, name, verbose, output_format)
            }
            MentalModelCommands::Delete { bank_id, mental_model_id, yes } => {
                commands::mental_model::delete(&client, &bank_id, &mental_model_id, yes, verbose, output_format)
            }
            MentalModelCommands::Refresh { bank_id, mental_model_id } => {
                commands::mental_model::refresh(&client, &bank_id, &mental_model_id, verbose, output_format)
            }
        },

        // Directive commands
        Commands::Directive(dir_cmd) => match dir_cmd {
            DirectiveCommands::List { bank_id } => {
                commands::directive::list(&client, &bank_id, verbose, output_format)
            }
            DirectiveCommands::Get { bank_id, directive_id } => {
                commands::directive::get(&client, &bank_id, &directive_id, verbose, output_format)
            }
            DirectiveCommands::Create { bank_id, name, content } => {
                commands::directive::create(&client, &bank_id, &name, &content, verbose, output_format)
            }
            DirectiveCommands::Update { bank_id, directive_id, name, content } => {
                commands::directive::update(&client, &bank_id, &directive_id, name, content, verbose, output_format)
            }
            DirectiveCommands::Delete { bank_id, directive_id, yes } => {
                commands::directive::delete(&client, &bank_id, &directive_id, yes, verbose, output_format)
            }
        },
    };

    // Handle API errors with nice messages
    if let Err(e) = result {
        errors::handle_api_error(e, &api_url);
    }

    Ok(())
}

fn handle_configure(api_url: Option<String>, api_key: Option<String>, output_format: OutputFormat) -> Result<()> {
    // Load current config to show current state
    let current_config = Config::load().ok();

    if output_format == OutputFormat::Pretty {
        ui::print_info("Hindsight CLI Configuration");
        println!();

        // Show current configuration
        if let Some(ref config) = current_config {
            println!("  Current API URL: {}", config.api_url);
            if let Some(ref key) = config.api_key {
                // Mask the API key for display
                let masked = if key.len() > 8 {
                    format!("{}...{}", &key[..4], &key[key.len()-4..])
                } else {
                    "****".to_string()
                };
                println!("  Current API Key: {}", masked);
            }
            println!("  Source: {}", config.source);
            println!();
        }
    }

    // Get the new API URL (from argument or prompt)
    let new_api_url = match api_url {
        Some(url) => url,
        None => {
            // Interactive prompt
            let current = current_config.as_ref().map(|c| c.api_url.as_str());
            config::prompt_api_url(current)?
        }
    };

    // Validate the URL
    if !new_api_url.starts_with("http://") && !new_api_url.starts_with("https://") {
        ui::print_error(&format!(
            "Invalid API URL: {}. Must start with http:// or https://",
            new_api_url
        ));
        return Ok(());
    }

    // Use provided api_key, or keep existing one if not provided
    let new_api_key = api_key.or_else(|| current_config.as_ref().and_then(|c| c.api_key.clone()));

    // Save to config file
    let config_path = Config::save_config(&new_api_url, new_api_key.as_deref())?;

    if output_format == OutputFormat::Pretty {
        ui::print_success(&format!("Configuration saved to {}", config_path.display()));
        println!();
        println!("  API URL: {}", new_api_url);
        if let Some(ref key) = new_api_key {
            let masked = if key.len() > 8 {
                format!("{}...{}", &key[..4], &key[key.len()-4..])
            } else {
                "****".to_string()
            };
            println!("  API Key: {}", masked);
        }
        println!();
        println!("Note: Environment variables HINDSIGHT_API_URL and HINDSIGHT_API_KEY will override these settings.");
    } else {
        let result = serde_json::json!({
            "api_url": new_api_url,
            "api_key_set": new_api_key.is_some(),
            "config_path": config_path.display().to_string(),
        });
        output::print_output(&result, output_format)?;
    }

    Ok(())
}

fn handle_ui(output_format: OutputFormat) -> Result<()> {
    use std::process::Command;

    // Load configuration to get the API URL
    let config = Config::load().unwrap_or_else(|e| {
        ui::print_error(&format!("Configuration error: {}", e));
        errors::print_config_help();
        std::process::exit(1);
    });

    let api_url = config.api_url();

    if output_format == OutputFormat::Pretty {
        ui::print_info("Launching Hindsight Control Plane UI...");
        println!();
        println!("  API URL: {}", api_url);
        println!();
    }

    // Run npx @vectorize-io/hindsight-control-plane --api-url {api_url}
    let status = Command::new("npx")
        .arg("@vectorize-io/hindsight-control-plane")
        .arg("--api-url")
        .arg(api_url)
        .status();

    match status {
        Ok(exit_status) => {
            if !exit_status.success() {
                if let Some(code) = exit_status.code() {
                    std::process::exit(code);
                } else {
                    std::process::exit(1);
                }
            }
        }
        Err(e) => {
            ui::print_error(&format!("Failed to launch control plane UI: {}", e));
            ui::print_info("Make sure you have Node.js and npm installed.");
            ui::print_info("You can also install the control plane globally: npm install -g @vectorize-io/hindsight-control-plane");
            std::process::exit(1);
        }
    }

    Ok(())
}
