mod alerts;
mod analytics;
mod app;
mod cli;
mod commands;
mod config;
mod data;
mod db;
mod indicators;
mod models;
mod notify;
mod price;
mod regime;
mod tui;
mod web;

use anyhow::{bail, Result};
use clap::Parser;

use crate::cli::{Cli, Command};
use crate::config::load_config_with_first_run_prompt;
use crate::db::backend::open_from_config;
use crate::db::default_db_path;

fn run_agent_journal(
    backend: &crate::db::backend::BackendConnection,
    command: Option<cli::JournalCommand>,
) -> Result<()> {
    match command {
        None => commands::journal::run_list(backend, Some(20), None, None, None, None, false),
        Some(cli::JournalCommand::Entry { command }) => match command {
            cli::JournalEntryCommand::Add {
                value,
                date,
                tag,
                symbol,
                conviction,
                json,
            } => commands::journal::run_add(
                backend,
                &value,
                date.as_deref(),
                tag.as_deref(),
                symbol.as_deref(),
                conviction.as_deref(),
                json,
            ),
            cli::JournalEntryCommand::List {
                limit,
                since,
                tag,
                symbol,
                filter_status,
                json,
            } => commands::journal::run_list(
                backend,
                limit,
                since.as_deref(),
                tag.as_deref(),
                symbol.as_deref(),
                filter_status.as_deref(),
                json,
            ),
            cli::JournalEntryCommand::Search {
                query,
                since,
                limit,
                json,
            } => commands::journal::run_search(backend, &query, since.as_deref(), limit, json),
            cli::JournalEntryCommand::Update {
                id,
                content,
                status,
                json,
            } => commands::journal::run_update(
                backend,
                id,
                content.as_deref(),
                status.as_deref(),
                json,
            ),
            cli::JournalEntryCommand::Remove { id, json } => {
                commands::journal::run_remove(backend, id, json)
            }
            cli::JournalEntryCommand::Tags { json } => commands::journal::run_tags(backend, json),
            cli::JournalEntryCommand::Stats { json } => commands::journal::run_stats(backend, json),
        },
        Some(cli::JournalCommand::Prediction { command }) => match command {
            cli::JournalPredictionCommand::Add {
                value,
                timeframe_pos,
                confidence_pos,
                symbol,
                conviction,
                timeframe,
                confidence,
                source_agent,
                target_date,
                resolution_criteria,
                json,
            } => commands::predict::run(
                backend,
                "add",
                Some(&value),
                None,
                symbol.as_deref(),
                conviction.as_deref(),
                timeframe.as_deref().or(timeframe_pos.as_deref()),
                confidence.or(confidence_pos),
                source_agent.as_deref(),
                target_date.as_deref(),
                resolution_criteria.as_deref(),
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::JournalPredictionCommand::List {
                filter,
                timeframe,
                symbol,
                limit,
                json,
            } => commands::predict::run(
                backend,
                "list",
                None,
                None,
                symbol.as_deref(),
                None,
                timeframe.as_deref(),
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                filter.as_deref(),
                None,
                limit,
                json,
            ),
            cli::JournalPredictionCommand::Score {
                id,
                id_pos,
                outcome,
                outcome_pos,
                notes,
                notes_pos,
                lesson,
                json,
            } => {
                let merged_outcome = outcome.or(outcome_pos);
                let merged_notes = notes.or(notes_pos);
                commands::predict::run(
                    backend,
                    "score",
                    None,
                    id.or(id_pos),
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    merged_outcome.as_deref(),
                    merged_notes.as_deref(),
                    lesson.as_deref(),
                    None,
                    None,
                    None,
                    json,
                )
            }
            cli::JournalPredictionCommand::Stats { json } => commands::predict::run(
                backend, "stats", None, None, None, None, None, None, None, None, None, None, None,
                None, None, None, None, json,
            ),
            cli::JournalPredictionCommand::Scorecard { date, limit, json } => {
                commands::predict::run(
                    backend,
                    "scorecard",
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    date.as_deref(),
                    limit,
                    json,
                )
            }
        },
        Some(cli::JournalCommand::Conviction { command }) => match command {
            cli::JournalConvictionCommand::Set {
                symbol,
                score_pos,
                score,
                notes,
                notes_pos,
                json,
            } => {
                let score_val = score.or(score_pos).ok_or_else(|| {
                    anyhow::anyhow!(
                        "Missing score. Usage: pftui journal conviction set SYMBOL <SCORE> [NOTES] or --score N [--notes ...]"
                    )
                })?;
                let merged_notes = notes.or(notes_pos);
                commands::conviction::run_set(
                    backend,
                    &symbol,
                    score_val,
                    merged_notes.as_deref(),
                    json,
                )
            }
            cli::JournalConvictionCommand::List { json } => {
                commands::conviction::run_list(backend, json)
            }
            cli::JournalConvictionCommand::History {
                symbol,
                limit,
                json,
            } => commands::conviction::run_history(backend, &symbol, limit, json),
            cli::JournalConvictionCommand::Changes { days, json } => {
                let d = days
                    .as_deref()
                    .and_then(|v| v.parse::<usize>().ok())
                    .unwrap_or(7);
                commands::conviction::run_changes(backend, d, json)
            }
        },
        Some(cli::JournalCommand::Notes { command }) => match command {
            cli::JournalNotesCommand::Add {
                value,
                date,
                section,
                json,
            } => commands::notes::run(
                backend,
                "add",
                Some(&value),
                None,
                date.as_deref(),
                section.as_deref(),
                None,
                None,
                json,
            ),
            cli::JournalNotesCommand::List { since, limit, json } => commands::notes::run(
                backend,
                "list",
                None,
                None,
                None,
                None,
                since.as_deref(),
                limit,
                json,
            ),
            cli::JournalNotesCommand::Search {
                query,
                since,
                limit,
                json,
            } => commands::notes::run(
                backend,
                "search",
                Some(&query),
                None,
                None,
                None,
                since.as_deref(),
                limit,
                json,
            ),
            cli::JournalNotesCommand::Remove { id, json } => commands::notes::run(
                backend,
                "remove",
                None,
                Some(id),
                None,
                None,
                None,
                None,
                json,
            ),
        },
        Some(cli::JournalCommand::Scenario { command }) => match command {
            cli::JournalScenarioCommand::Add {
                value,
                probability,
                description,
                impact,
                triggers,
                precedent,
                status,
                json,
            } => commands::scenario::run(
                backend,
                "add",
                Some(&value),
                None,
                None,
                probability,
                description.as_deref(),
                impact.as_deref(),
                triggers.as_deref(),
                precedent.as_deref(),
                status.as_deref(),
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::JournalScenarioCommand::List {
                status,
                limit,
                json,
            } => commands::scenario::run(
                backend,
                "list",
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                status.as_deref(),
                None,
                None,
                None,
                None,
                None,
                limit,
                json,
            ),
            cli::JournalScenarioCommand::Update {
                value,
                note_pos,
                probability,
                description,
                impact,
                triggers,
                precedent,
                status,
                driver,
                notes,
                json,
            } => {
                let merged_notes = driver.or(notes).or(note_pos);
                commands::scenario::run(
                    backend,
                    "update",
                    Some(&value),
                    None,
                    None,
                    probability,
                    description.as_deref(),
                    impact.as_deref(),
                    triggers.as_deref(),
                    precedent.as_deref(),
                    status.as_deref(),
                    merged_notes.as_deref(),
                    merged_notes.as_deref(),
                    None,
                    None,
                    None,
                    None,
                    json,
                )
            }
            cli::JournalScenarioCommand::Remove { value, json } => commands::scenario::run(
                backend,
                "remove",
                Some(&value),
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::JournalScenarioCommand::History { value, limit, json } => commands::scenario::run(
                backend,
                "history",
                Some(&value),
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                limit,
                json,
            ),
            cli::JournalScenarioCommand::Signal { command } => match command {
                cli::JournalScenarioSignalCommand::Add {
                    value,
                    scenario,
                    source,
                    status,
                    json,
                } => commands::scenario::run(
                    backend,
                    "signal-add",
                    Some(&value),
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    status.as_deref(),
                    None,
                    None,
                    None,
                    source.as_deref(),
                    scenario.as_deref(),
                    None,
                    json,
                ),
                cli::JournalScenarioSignalCommand::List {
                    scenario,
                    status,
                    limit,
                    json,
                } => commands::scenario::run(
                    backend,
                    "signal-list",
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    status.as_deref(),
                    None,
                    None,
                    None,
                    None,
                    scenario.as_deref(),
                    limit,
                    json,
                ),
                cli::JournalScenarioSignalCommand::Update {
                    signal_id,
                    evidence,
                    status,
                    json,
                } => commands::scenario::run(
                    backend,
                    "signal-update",
                    None,
                    None,
                    Some(signal_id),
                    None,
                    None,
                    None,
                    None,
                    None,
                    status.as_deref(),
                    None,
                    None,
                    evidence.as_deref(),
                    None,
                    None,
                    None,
                    json,
                ),
                cli::JournalScenarioSignalCommand::Remove { signal_id, json } => {
                    commands::scenario::run(
                        backend,
                        "signal-remove",
                        None,
                        None,
                        Some(signal_id),
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        json,
                    )
                }
            },
        },
    }
}

fn main() -> Result<()> {
    let cli = Cli::parse();
    let cached_only = cli.cached_only;

    if matches!(cli.command, Some(Command::Console)) {
        return commands::console::run(cached_only);
    }

    let config = load_config_with_first_run_prompt()?;
    let db_path = default_db_path();

    let backend = open_from_config(&config, &db_path)?;

    let result = match cli.command {
        None => {
            // Auto-detect: run setup if no portfolio data
            if !commands::setup::has_portfolio_data(&backend) {
                commands::setup::run(&config, false)?;
                // Reload config (setup may have changed portfolio_mode)
                let config = load_config_with_first_run_prompt()?;
                let mut app = app::App::new(&config, db_path);
                app.init();
                let result = tui::run(&mut app);
                app.shutdown();
                result
            } else {
                // Launch TUI
                let mut app = app::App::new(&config, db_path);
                app.init();
                let result = tui::run(&mut app);
                app.shutdown();
                result
            }
        }

        Some(Command::Console) => unreachable!("console handled before backend initialization"),

        Some(Command::Journal { command }) => run_agent_journal(&backend, command),

        Some(Command::Data { command }) => match command {
            cli::DataCommand::Refresh { notify } => {
                if cached_only {
                    println!("Cached-only mode enabled; skipping refresh network calls.");
                    Ok(())
                } else {
                    commands::refresh::run(&backend, &config, notify)
                }
            }
            cli::DataCommand::Status { json, .. } => commands::status::run_backend(&backend, json),
            cli::DataCommand::Dashboard { command } => match command {
                cli::DashboardCommand::Macro { json } => {
                    commands::macro_cmd::run(&backend, &config, json, cached_only)
                }
                cli::DashboardCommand::Oil { json } => {
                    commands::oil::run(&backend, json, cached_only)
                }
                cli::DashboardCommand::Crisis { json } => {
                    commands::crisis::run(&backend, json, cached_only)
                }
                cli::DashboardCommand::Sector { json } => {
                    commands::sector::run(&backend, &config, json)
                }
                cli::DashboardCommand::Heatmap { json } => commands::heatmap::run(&backend, json),
                cli::DashboardCommand::Global {
                    country,
                    indicator,
                    json,
                } => {
                    commands::global::run(&backend, country.as_deref(), indicator.as_deref(), json)
                }
            },
            cli::DataCommand::News {
                source,
                search,
                hours,
                limit,
                json,
            } => commands::news::run(
                &backend,
                source.as_deref(),
                search.as_deref(),
                hours,
                limit,
                json,
            ),
            cli::DataCommand::Sentiment {
                symbol,
                history,
                json,
            } => commands::sentiment::run(symbol.as_deref(), history, json),
            cli::DataCommand::Calendar { days, impact, json } => {
                commands::calendar::run(days, impact.as_deref(), json)
            }
            cli::DataCommand::Fedwatch { json } => commands::fedwatch::run(&backend, json),
            cli::DataCommand::Economy { indicator, json } => {
                commands::economy::run(&backend, indicator.as_deref(), json)
            }
            cli::DataCommand::Predictions {
                category,
                search,
                limit,
                json,
            } => commands::predictions::run(
                &backend,
                category.as_deref(),
                search.as_deref(),
                limit,
                json,
            ),
            cli::DataCommand::Options {
                symbol,
                expiry,
                limit,
                json,
            } => commands::options::run(&symbol, expiry.as_deref(), limit, json),
            cli::DataCommand::EtfFlows { days, fund, json } => {
                commands::etf_flows::run(days, fund, json)
            }
            cli::DataCommand::Supply { symbol, json } => {
                commands::supply::run(&backend, symbol, json)
            }
            cli::DataCommand::Sovereign { json } => commands::sovereign::run(json),
        },
        Some(Command::System { command }) => match command {
            cli::SystemCommand::Config {
                action,
                field,
                value,
                json,
            } => commands::config_cmd::run(&action, field.as_deref(), value.as_deref(), json),
            cli::SystemCommand::DbInfo { json } => {
                commands::db_info::run(&backend, &db_path, config.database_url.as_deref(), json)
            }
            cli::SystemCommand::Doctor { json } => {
                let runtime = tokio::runtime::Runtime::new()?;
                runtime.block_on(async { commands::doctor::run(json).await })
            }
            cli::SystemCommand::Export { format, output } => {
                commands::export::run(&backend, &format, &config, output.as_deref())
            }
            cli::SystemCommand::Import { path, mode } => {
                let import_mode = match mode {
                    cli::ImportModeArg::Replace => commands::import::ImportMode::Replace,
                    cli::ImportModeArg::Merge => commands::import::ImportMode::Merge,
                };
                commands::import::run(&backend, &config, &path, import_mode)
            }
            cli::SystemCommand::Snapshot {
                width,
                height,
                plain,
            } => commands::snapshot::run(&config, Some(width), Some(height), plain),
            cli::SystemCommand::Setup => commands::setup::run(&config, true),
            cli::SystemCommand::Demo => commands::demo::run(&config),
            cli::SystemCommand::Web {
                port,
                bind,
                no_auth,
            } => {
                let runtime = tokio::runtime::Runtime::new()?;
                runtime.block_on(async {
                    web::run_server(
                        db_path.to_string_lossy().to_string(),
                        config,
                        &bind,
                        port,
                        !no_auth,
                    )
                    .await
                })
            }
            cli::SystemCommand::MigrateJournal {
                path,
                dry_run,
                default_tag,
                default_status,
                json,
            } => commands::migrate_journal::run(
                &backend,
                &path,
                dry_run,
                default_tag.as_deref(),
                &default_status,
                json,
            ),
        },
        Some(Command::Portfolio { command }) => match command {
            None => commands::summary::run(&backend, &config, None, None, None, true, false),
            Some(cli::PortfolioCommand::Summary {
                group_by,
                period,
                what_if,
                json,
            }) => commands::summary::run(
                &backend,
                &config,
                group_by.as_ref(),
                period.as_ref(),
                what_if.as_deref(),
                true,
                json,
            ),
            Some(cli::PortfolioCommand::Value { json }) => {
                commands::value::run(&backend, &config, json)
            }
            Some(cli::PortfolioCommand::Brief { json }) => {
                commands::brief::run_backend(&backend, &config, true, json, cached_only)
            }
            Some(cli::PortfolioCommand::Eod { json }) => {
                commands::eod::run(&backend, &config, json)
            }
            Some(cli::PortfolioCommand::Performance {
                since,
                period,
                vs,
                json,
            }) => commands::performance::run(
                &backend,
                &config,
                since.as_deref(),
                period.as_deref(),
                vs.as_deref(),
                json,
            ),
            Some(cli::PortfolioCommand::History { date, group_by }) => {
                commands::history::run(&backend, &config, &date, group_by.as_ref())
            }
            Some(cli::PortfolioCommand::Target { command }) => match command {
                cli::PortfolioTargetCommand::Set {
                    symbol,
                    target,
                    band,
                } => {
                    let sym = symbol
                        .as_ref()
                        .ok_or_else(|| anyhow::anyhow!("--symbol required for 'set'"))?;
                    let tgt = target
                        .as_ref()
                        .ok_or_else(|| anyhow::anyhow!("--target required for 'set'"))?;
                    commands::target::run(&backend, sym, tgt, band.as_deref())
                }
                cli::PortfolioTargetCommand::List { json } => {
                    commands::target::list(&backend, json)
                }
                cli::PortfolioTargetCommand::Remove { symbol } => {
                    let sym = symbol
                        .as_ref()
                        .ok_or_else(|| anyhow::anyhow!("--symbol required for 'remove'"))?;
                    commands::target::remove(&backend, sym)
                }
            },
            Some(cli::PortfolioCommand::Drift { json }) => commands::drift::run(&backend, json),
            Some(cli::PortfolioCommand::Rebalance { json }) => {
                commands::rebalance::run(&backend, json)
            }
            Some(cli::PortfolioCommand::StressTest { scenario, json }) => {
                commands::stress_test::run(&backend, &config, &scenario, json)
            }
            Some(cli::PortfolioCommand::Dividends {
                action,
                value,
                amount,
                pay_date,
                ex_date,
                currency,
                notes,
                json,
            }) => {
                let args = commands::dividends::DividendsArgs {
                    value,
                    amount,
                    pay_date,
                    ex_date,
                    currency,
                    notes,
                    json,
                };
                commands::dividends::run(&backend, &action, args)
            }
            Some(cli::PortfolioCommand::Annotate {
                symbol,
                thesis,
                invalidation,
                review_date,
                target,
                show,
                list,
                remove,
                json,
            }) => {
                let args = commands::annotate::AnnotateArgs {
                    symbol: symbol.as_deref(),
                    thesis: thesis.as_deref(),
                    invalidation: invalidation.as_deref(),
                    review_date: review_date.as_deref(),
                    target: target.as_deref(),
                    show,
                    list,
                    remove,
                    json,
                };
                commands::annotate::run(&backend, args)
            }
            Some(cli::PortfolioCommand::Group {
                action,
                name,
                symbols,
                json,
            }) => commands::group::run(
                &backend,
                &config,
                &action,
                name.as_deref(),
                symbols.as_deref(),
                json,
            ),
            Some(cli::PortfolioCommand::Opportunity { command }) => match command {
                cli::PortfolioOpportunityCommand::Add {
                    value,
                    date,
                    asset,
                    missed_gain_pct,
                    missed_gain_usd,
                    avoided_loss_pct,
                    avoided_loss_usd,
                    rational,
                    notes,
                    json,
                } => commands::opportunity::run(
                    &backend,
                    "add",
                    value.as_deref(),
                    date.as_deref(),
                    asset.as_deref(),
                    missed_gain_pct,
                    missed_gain_usd,
                    avoided_loss_pct,
                    avoided_loss_usd,
                    rational,
                    notes.as_deref(),
                    None,
                    None,
                    json,
                ),
                cli::PortfolioOpportunityCommand::List {
                    since,
                    asset,
                    limit,
                    json,
                } => commands::opportunity::run(
                    &backend,
                    "list",
                    None,
                    None,
                    asset.as_deref(),
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    since.as_deref(),
                    limit,
                    json,
                ),
                cli::PortfolioOpportunityCommand::Stats { since, json } => {
                    commands::opportunity::run(
                        &backend,
                        "stats",
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        since.as_deref(),
                        None,
                        json,
                    )
                }
            },
            Some(cli::PortfolioCommand::Profiles { command }) => match command {
                cli::PortfolioProfilesCommand::List { json } => {
                    commands::portfolio::run("list", None, json)
                }
                cli::PortfolioProfilesCommand::Current { json } => {
                    commands::portfolio::run("current", None, json)
                }
                cli::PortfolioProfilesCommand::Create { name, json } => {
                    commands::portfolio::run("create", name.as_deref(), json)
                }
                cli::PortfolioProfilesCommand::Switch { name, json } => {
                    commands::portfolio::run("switch", name.as_deref(), json)
                }
                cli::PortfolioProfilesCommand::Remove { name, json } => {
                    commands::portfolio::run("remove", name.as_deref(), json)
                }
            },
            Some(cli::PortfolioCommand::Watchlist {
                action,
                approaching,
                json,
            }) => match action {
                Some(cli::WatchlistCommand::Add {
                    symbol,
                    category,
                    bulk,
                    target,
                    direction,
                }) => {
                    use crate::models::asset_names::infer_category;

                    let symbols: Vec<String> = if let Some(bulk_str) = bulk {
                        bulk_str
                            .split(',')
                            .map(|s| s.trim().to_uppercase())
                            .filter(|s| !s.is_empty())
                            .collect()
                    } else if let Some(sym) = symbol {
                        vec![sym.to_uppercase()]
                    } else {
                        bail!("Provide a symbol or use --bulk SYMBOL1,SYMBOL2,...");
                    };
                    if symbols.is_empty() {
                        bail!("No valid symbols provided");
                    }
                    if let Some(ref t) = target {
                        let cleaned = t.replace(['$', ','], "");
                        if rust_decimal::Decimal::from_str_exact(&cleaned).is_err() {
                            bail!(
                                "Invalid target price: '{}'. Use a number (e.g. 300, 55000.50)",
                                t
                            );
                        }
                        if direction != "above" && direction != "below" {
                            bail!("Invalid direction: '{}'. Use 'above' or 'below'", direction);
                        }
                        if symbols.len() > 1 {
                            bail!("--target can only be set for a single symbol, not with --bulk");
                        }
                    }

                    let mut added = 0;
                    let runtime = tokio::runtime::Runtime::new()?;
                    for upper in &symbols {
                        let cat = match &category {
                            Some(c) => c.parse().unwrap_or_else(|_| infer_category(upper)),
                            None => infer_category(upper),
                        };
                        let yahoo_sym = match cat {
                            crate::models::asset::AssetCategory::Crypto => {
                                if upper.ends_with("-USD") {
                                    upper.clone()
                                } else {
                                    format!("{}-USD", upper)
                                }
                            }
                            _ => upper.clone(),
                        };
                        match runtime.block_on(price::yahoo::fetch_price(&yahoo_sym)) {
                            Ok(_) => {
                                db::watchlist::add_to_watchlist_backend(&backend, upper, cat)?;
                                let name = crate::models::asset_names::resolve_name(upper);
                                let display = if name.is_empty() { upper.clone() } else { name };
                                println!("Added {} ({}) to watchlist as {}", upper, display, cat);
                            }
                            Err(e) => {
                                eprintln!(
                                    "Warning: skipping {} — price lookup failed ({}). Symbol may be invalid.",
                                    upper, e
                                );
                                continue;
                            }
                        }
                        if let Some(ref t) = target {
                            let cleaned = t.replace(['$', ','], "");
                            db::watchlist::set_watchlist_target_backend(
                                &backend,
                                upper,
                                Some(&cleaned),
                                Some(&direction),
                            )?;
                            println!("  Target: {} {} {}", upper, direction, cleaned);
                            let rule_text = format!("{} {} {}", upper, direction, cleaned);
                            db::alerts::add_alert_backend(
                                &backend, "price", upper, &direction, &cleaned, &rule_text,
                            )?;
                            println!("  Alert created: {}", rule_text);
                        }
                        added += 1;
                    }
                    if added > 1 {
                        println!("\n{} symbols added to watchlist.", added);
                    }
                    Ok(())
                }
                Some(cli::WatchlistCommand::Remove { symbol }) => {
                    let upper = symbol.to_uppercase();
                    if db::watchlist::remove_from_watchlist_backend(&backend, &upper)? {
                        println!("Removed {} from watchlist", upper);
                    } else {
                        println!("{} was not in the watchlist", upper);
                    }
                    Ok(())
                }
                Some(cli::WatchlistCommand::List { approaching, json }) => {
                    commands::watchlist_cli::run(
                        &backend,
                        &config,
                        approaching.as_deref(),
                        json,
                        cached_only,
                    )
                }
                None => commands::watchlist_cli::run(
                    &backend,
                    &config,
                    approaching.as_deref(),
                    json,
                    cached_only,
                ),
            },
            Some(cli::PortfolioCommand::SetCash { symbol, amount }) => {
                commands::set_cash::run(&backend, &symbol, &amount)
            }
            Some(cli::PortfolioCommand::Transaction { command }) => match command {
                cli::PortfolioTransactionCommand::Add {
                    symbol,
                    category,
                    tx_type,
                    quantity,
                    price,
                    currency,
                    date,
                    notes,
                } => {
                    if config.is_percentage_mode() {
                        bail!("add-tx is not available in percentage mode.\nRun `pftui setup` to switch to full mode.");
                    }
                    commands::add_tx::run(
                        &backend, symbol, category, tx_type, quantity, price, currency, date, notes,
                    )
                }
                cli::PortfolioTransactionCommand::Remove { id } => {
                    if config.is_percentage_mode() {
                        bail!("remove-tx is not available in percentage mode.\nRun `pftui setup` to switch to full mode.");
                    }
                    commands::remove_tx::run(&backend, id)
                }
                cli::PortfolioTransactionCommand::List { notes, json } => {
                    if config.is_percentage_mode() {
                        bail!("list-tx is not available in percentage mode (no transactions).\nRun `pftui setup` to switch to full mode.");
                    }
                    commands::list_tx::run(&backend, notes, json)
                }
            },
        },

        Some(Command::Agent { command }) => match command {
            crate::cli::AgentCommand::Message { command } => match command {
                cli::AgentMessageCommand::Send {
                    value,
                    batch,
                    package_id,
                    package_title,
                    from,
                    to,
                    priority,
                    category,
                    layer,
                    json,
                } => commands::agent_msg::run(
                    &backend,
                    "send",
                    value.as_deref(),
                    &batch,
                    None,
                    package_id.as_deref(),
                    package_title.as_deref(),
                    from.as_deref(),
                    to.as_deref(),
                    priority.as_deref(),
                    category.as_deref(),
                    layer.as_deref(),
                    false,
                    None,
                    None,
                    None,
                    json,
                ),
                cli::AgentMessageCommand::List {
                    from,
                    to,
                    layer,
                    unacked,
                    since,
                    package_id,
                    limit,
                    json,
                } => commands::agent_msg::run(
                    &backend,
                    "list",
                    None,
                    &[],
                    None,
                    package_id.as_deref(),
                    None,
                    from.as_deref(),
                    to.as_deref(),
                    None,
                    None,
                    layer.as_deref(),
                    unacked,
                    since.as_deref(),
                    None,
                    limit,
                    json,
                ),
                cli::AgentMessageCommand::Reply {
                    value,
                    id,
                    from,
                    priority,
                    category,
                    layer,
                    json,
                } => commands::agent_msg::run(
                    &backend,
                    "reply",
                    value.as_deref(),
                    &[],
                    id,
                    None,
                    None,
                    from.as_deref(),
                    None,
                    priority.as_deref(),
                    category.as_deref(),
                    layer.as_deref(),
                    false,
                    None,
                    None,
                    None,
                    json,
                ),
                cli::AgentMessageCommand::Flag {
                    value,
                    id,
                    from,
                    priority,
                    category,
                    layer,
                    json,
                } => commands::agent_msg::run(
                    &backend,
                    "flag",
                    value.as_deref(),
                    &[],
                    id,
                    None,
                    None,
                    from.as_deref(),
                    None,
                    priority.as_deref(),
                    category.as_deref(),
                    layer.as_deref(),
                    false,
                    None,
                    None,
                    None,
                    json,
                ),
                cli::AgentMessageCommand::Ack { id, json } => commands::agent_msg::run(
                    &backend,
                    "ack",
                    None,
                    &[],
                    id,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    false,
                    None,
                    None,
                    None,
                    json,
                ),
                cli::AgentMessageCommand::AckAll { to, json } => commands::agent_msg::run(
                    &backend,
                    "ack-all",
                    None,
                    &[],
                    None,
                    None,
                    None,
                    None,
                    to.as_deref(),
                    None,
                    None,
                    None,
                    false,
                    None,
                    None,
                    None,
                    json,
                ),
                cli::AgentMessageCommand::Purge { days, json } => commands::agent_msg::run(
                    &backend,
                    "purge",
                    None,
                    &[],
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    false,
                    None,
                    days,
                    None,
                    json,
                ),
            },
        },
        Some(Command::Analytics { command }) => match command {
            cli::AnalyticsCommand::Signals {
                symbol,
                signal_type,
                severity,
                limit,
                json,
            } => commands::analytics::run(
                &backend,
                "signals",
                None,
                None,
                None,
                symbol.as_deref(),
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                signal_type.as_deref(),
                severity.as_deref(),
                None,
                None,
                limit,
                json,
            ),
            cli::AnalyticsCommand::Summary { json } => commands::analytics::run(
                &backend,
                "summary",
                None,
                None,
                None,
                None,
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::AnalyticsCommand::Low { json } => commands::analytics::run(
                &backend,
                "low",
                None,
                None,
                None,
                None,
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::AnalyticsCommand::Medium { json } => commands::analytics::run(
                &backend,
                "medium",
                None,
                None,
                None,
                None,
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::AnalyticsCommand::High { json } => commands::analytics::run(
                &backend,
                "high",
                None,
                None,
                None,
                None,
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::AnalyticsCommand::Macro { command, json } => match command {
                None => commands::analytics::run(
                    &backend,
                    "macro",
                    None,
                    None,
                    None,
                    None,
                    &[],
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    false,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    json,
                ),
                Some(cli::AnalyticsMacroCommand::Metrics { country, json }) => {
                    commands::analytics::run(
                        &backend,
                        "macro",
                        Some("metrics"),
                        country.as_deref(),
                        None,
                        None,
                        &[],
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        false,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        json,
                    )
                }
                Some(cli::AnalyticsMacroCommand::Compare { left, right, json }) => {
                    commands::analytics::run(
                        &backend,
                        "macro",
                        Some("compare"),
                        left.as_deref(),
                        right.as_deref(),
                        None,
                        &[],
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        false,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        json,
                    )
                }
                Some(cli::AnalyticsMacroCommand::Cycles { json }) => commands::analytics::run(
                    &backend,
                    "macro",
                    Some("cycles"),
                    None,
                    None,
                    None,
                    &[],
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    false,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    json,
                ),
                Some(cli::AnalyticsMacroCommand::Outcomes { json }) => commands::analytics::run(
                    &backend,
                    "macro",
                    Some("outcomes"),
                    None,
                    None,
                    None,
                    &[],
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    false,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    json,
                ),
                Some(cli::AnalyticsMacroCommand::Parallels { json }) => commands::analytics::run(
                    &backend,
                    "macro",
                    Some("parallels"),
                    None,
                    None,
                    None,
                    &[],
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    false,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    json,
                ),
                Some(cli::AnalyticsMacroCommand::Log { limit, json }) => commands::analytics::run(
                    &backend,
                    "macro",
                    Some("log"),
                    None,
                    None,
                    None,
                    &[],
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    false,
                    None,
                    None,
                    None,
                    None,
                    None,
                    limit,
                    json,
                ),
                Some(cli::AnalyticsMacroCommand::Regime { command }) => match command {
                    cli::AnalyticsMacroRegimeCommand::Current { json } => {
                        commands::regime::run(&backend, "current", None, json)
                    }
                    cli::AnalyticsMacroRegimeCommand::History { limit, json } => {
                        commands::regime::run(&backend, "history", limit, json)
                    }
                    cli::AnalyticsMacroRegimeCommand::Transitions { limit, json } => {
                        commands::regime::run(&backend, "transitions", limit, json)
                    }
                },
            },
            cli::AnalyticsCommand::Alignment { symbol, json } => commands::analytics::run(
                &backend,
                "alignment",
                None,
                None,
                None,
                symbol.as_deref(),
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::AnalyticsCommand::Divergence { symbol, json } => commands::analytics::run(
                &backend,
                "divergence",
                None,
                None,
                None,
                symbol.as_deref(),
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::AnalyticsCommand::Digest { from, limit, json } => commands::analytics::run(
                &backend,
                "digest",
                None,
                None,
                None,
                None,
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                None,
                None,
                from.as_deref(),
                None,
                limit,
                json,
            ),
            cli::AnalyticsCommand::Recap { date, limit, json } => commands::analytics::run(
                &backend,
                "recap",
                None,
                None,
                None,
                None,
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                None,
                None,
                None,
                date.as_deref(),
                limit,
                json,
            ),
            cli::AnalyticsCommand::Gaps { json } => commands::analytics::run(
                &backend,
                "gaps",
                None,
                None,
                None,
                None,
                &[],
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                None,
                false,
                None,
                None,
                None,
                None,
                None,
                None,
                json,
            ),
            cli::AnalyticsCommand::Movers {
                threshold,
                overnight,
                json,
            } => commands::movers::run(&backend, &config, Some(&threshold), overnight, json),
            cli::AnalyticsCommand::Correlations { command } => match command {
                None => commands::correlations::run(
                    &backend,
                    Some("compute"),
                    None,
                    None,
                    30,
                    None,
                    false,
                    15,
                    false,
                ),
                Some(cli::AnalyticsCorrelationsCommand::Compute {
                    window,
                    period,
                    store,
                    limit,
                    json,
                }) => commands::correlations::run(
                    &backend,
                    Some("compute"),
                    None,
                    None,
                    window,
                    period.as_deref(),
                    store,
                    limit,
                    json,
                ),
                Some(cli::AnalyticsCorrelationsCommand::History {
                    symbol_a,
                    symbol_b,
                    window,
                    period,
                    limit,
                    json,
                }) => commands::correlations::run(
                    &backend,
                    Some("history"),
                    Some(&symbol_a),
                    Some(&symbol_b),
                    window,
                    period.as_deref(),
                    false,
                    limit,
                    json,
                ),
                Some(cli::AnalyticsCorrelationsCommand::Latest {
                    period,
                    limit,
                    json,
                }) => commands::correlations::run(
                    &backend,
                    Some("latest"),
                    None,
                    None,
                    30,
                    period.as_deref(),
                    false,
                    limit,
                    json,
                ),
            },
            cli::AnalyticsCommand::Scan {
                filter,
                save,
                load,
                list,
                news_keyword,
                trackline_breaches,
                json,
            } => commands::scan::run(
                &backend,
                &config,
                filter.as_deref(),
                save.as_deref(),
                load.as_deref(),
                list,
                news_keyword.as_deref(),
                trackline_breaches,
                json,
            ),
            cli::AnalyticsCommand::Research {
                query,
                news,
                freshness,
                count,
                json,
                fed,
                earnings,
                geopolitics,
                cot,
                etf,
                opec,
            } => {
                let freshness_checked = match freshness.as_deref() {
                    Some(v) => Some(commands::research::validate_freshness(v)?),
                    None => None,
                };
                let preset = commands::research::ResearchPresetArgs {
                    fed,
                    earnings,
                    geopolitics,
                    cot,
                    etf,
                    opec,
                };
                commands::research::run(
                    query.as_deref(),
                    news,
                    freshness_checked.as_deref(),
                    count,
                    json,
                    preset,
                )
            }
            cli::AnalyticsCommand::Trends { command } => match command {
                cli::AnalyticsTrendsCommand::Add {
                    value,
                    timeframe,
                    direction,
                    conviction,
                    category,
                    description,
                    asset_impact,
                    key_signal,
                    status,
                    json,
                } => commands::trends::run(
                    &backend,
                    "add",
                    value.as_deref(),
                    None,
                    timeframe.as_deref(),
                    direction.as_deref(),
                    conviction.as_deref(),
                    category.as_deref(),
                    description.as_deref(),
                    asset_impact.as_deref(),
                    key_signal.as_deref(),
                    status.as_deref(),
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    json,
                ),
                cli::AnalyticsTrendsCommand::List {
                    timeframe,
                    direction,
                    conviction,
                    category,
                    status,
                    limit,
                    json,
                } => commands::trends::run(
                    &backend,
                    "list",
                    None,
                    None,
                    timeframe.as_deref(),
                    direction.as_deref(),
                    conviction.as_deref(),
                    category.as_deref(),
                    None,
                    None,
                    None,
                    status.as_deref(),
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    limit,
                    json,
                ),
                cli::AnalyticsTrendsCommand::Update {
                    value,
                    id,
                    timeframe,
                    direction,
                    conviction,
                    category,
                    description,
                    asset_impact,
                    key_signal,
                    status,
                    json,
                } => commands::trends::run(
                    &backend,
                    "update",
                    value.as_deref(),
                    id,
                    timeframe.as_deref(),
                    direction.as_deref(),
                    conviction.as_deref(),
                    category.as_deref(),
                    description.as_deref(),
                    asset_impact.as_deref(),
                    key_signal.as_deref(),
                    status.as_deref(),
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    json,
                ),
                cli::AnalyticsTrendsCommand::Dashboard { json } => commands::trends::run(
                    &backend,
                    "dashboard",
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    None,
                    json,
                ),
                cli::AnalyticsTrendsCommand::Evidence { command } => match command {
                    cli::AnalyticsTrendsEvidenceCommand::Add {
                        id,
                        evidence,
                        value,
                        date,
                        direction_impact,
                        source,
                        json,
                    } => {
                        let evidence_text = evidence.or(value);
                        commands::trends::run(
                            &backend,
                            "evidence-add",
                            None,
                            id,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            date.as_deref(),
                            evidence_text.as_deref(),
                            direction_impact.as_deref(),
                            source.as_deref(),
                            None,
                            None,
                            None,
                            None,
                            None,
                            json,
                        )
                    }
                    cli::AnalyticsTrendsEvidenceCommand::List { id, limit, json } => {
                        commands::trends::run(
                            &backend,
                            "evidence-list",
                            None,
                            id,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            None,
                            limit,
                            json,
                        )
                    }
                },
                cli::AnalyticsTrendsCommand::Impact { command } => match command {
                    cli::AnalyticsTrendsImpactCommand::Add {
                        id,
                        symbol,
                        impact,
                        mechanism,
                        impact_timeframe,
                        date,
                        json,
                    } => commands::trends::run(
                        &backend,
                        "impact-add",
                        None,
                        id,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        date.as_deref(),
                        None,
                        None,
                        None,
                        symbol.as_deref(),
                        impact.as_deref(),
                        mechanism.as_deref(),
                        impact_timeframe.as_deref(),
                        None,
                        json,
                    ),
                    cli::AnalyticsTrendsImpactCommand::List {
                        id,
                        symbol,
                        limit,
                        json,
                    } => commands::trends::run(
                        &backend,
                        "impact-list",
                        None,
                        id,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        None,
                        symbol.as_deref(),
                        None,
                        None,
                        None,
                        limit,
                        json,
                    ),
                },
            },
            cli::AnalyticsCommand::Alerts { command } => {
                let (action, args) = match command {
                    cli::AnalyticsAlertsCommand::Add { rule } => (
                        "add",
                        commands::alerts::AlertsArgs {
                            rule: Some(rule),
                            id: None,
                            json: false,
                            status_filter: None,
                            today: false,
                        },
                    ),
                    cli::AnalyticsAlertsCommand::List {
                        status,
                        today,
                        json,
                    } => (
                        "list",
                        commands::alerts::AlertsArgs {
                            rule: None,
                            id: None,
                            json,
                            status_filter: status,
                            today,
                        },
                    ),
                    cli::AnalyticsAlertsCommand::Remove { id } => (
                        "remove",
                        commands::alerts::AlertsArgs {
                            rule: None,
                            id: Some(id),
                            json: false,
                            status_filter: None,
                            today: false,
                        },
                    ),
                    cli::AnalyticsAlertsCommand::Check { today, json } => (
                        "check",
                        commands::alerts::AlertsArgs {
                            rule: None,
                            id: None,
                            json,
                            status_filter: None,
                            today,
                        },
                    ),
                    cli::AnalyticsAlertsCommand::Ack { id } => (
                        "ack",
                        commands::alerts::AlertsArgs {
                            rule: None,
                            id: Some(id),
                            json: false,
                            status_filter: None,
                            today: false,
                        },
                    ),
                    cli::AnalyticsAlertsCommand::Rearm { id } => (
                        "rearm",
                        commands::alerts::AlertsArgs {
                            rule: None,
                            id: Some(id),
                            json: false,
                            status_filter: None,
                            today: false,
                        },
                    ),
                };
                commands::alerts::run(&backend, action, &args)
            }
        },
    };

    match (result, backend.flush()) {
        (Err(e), _) => Err(e),
        (Ok(_), Err(e)) => Err(e),
        (Ok(v), Ok(_)) => Ok(v),
    }
}
