use crate::{core, doctor, hook_handlers, mcp_stdio, setup, shell, status, sync_cli, tools, uninstall};
use anyhow::Result;

fn fire_shell_telemetry(tool_name: String, command_preview: Option<String>) {
    let project_context = std::env::current_dir()
        .ok()
        .map(|dir| crate::git_context::discover_project_context(&dir));

    core::telemetry_queue::fire_sync(crate::models::TelemetryIngestRequest {
        tool_name,
        tokens_original: 0,
        tokens_saved: 0,
        duration_ms: 0,
        mode: Some("shell".to_string()),
        repository_fingerprint: project_context.as_ref().and_then(|context| {
            context
                .fingerprint
                .has_safe_identity()
                .then(|| context.fingerprint.clone())
        }),
        checkout_binding: project_context
            .as_ref()
            .map(|context| context.checkout_binding.clone()),
        project_slug: project_context
            .as_ref()
            .map(|context| context.project_slug.clone())
            .filter(|slug| !slug.is_empty()),
        command_preview,
    });
}

fn exit_unsupported(command: &str, replacement: &str) -> ! {
    eprintln!("nebu-ctx: `{command}` is no longer a supported thin-client CLI surface.");
    if !replacement.is_empty() {
        eprintln!("  Use: {replacement}");
    }
    std::process::exit(1);
}

pub fn run() {
    let args: Vec<String> = std::env::args().collect();

    if args.len() > 1 {
        let rest = args[2..].to_vec();
        let command = args[1].as_str();

        if matches!(command, "heatmap" | "stats") {
            super::exit_hosted_analytics_only(command);
        }

        match command {
            "-c" | "exec" => {
                let raw = rest.first().map(|a| a == "--raw").unwrap_or(false);
                let cmd_args = if raw { &args[3..] } else { &args[2..] };
                if cmd_args.is_empty() {
                    eprintln!("Usage: nebu-ctx -c [--raw] \"command\" or nebu-ctx -c [--raw] <exe> [args...]");
                    std::process::exit(1);
                }
                let command = if cmd_args.len() == 1 {
                    cmd_args[0].clone()
                } else {
                    shell::join_command(cmd_args)
                };
                if std::env::var("NEBU_CTX_ACTIVE").is_ok()
                    || std::env::var("NEBU_CTX_DISABLED").is_ok()
                {
                    passthrough(&command);
                }
                if raw {
                    std::env::set_var("NEBU_CTX_RAW", "1");
                } else {
                    std::env::set_var("NEBU_CTX_COMPRESS", "1");
                }
                let code = if cmd_args.len() > 1 {
                    shell::exec_argv_compressed(cmd_args)
                } else {
                    shell::exec(&command)
                };
                core::stats::flush();
                fire_shell_telemetry(
                    core::stats::normalize_command(&command),
                    core::sanitize::telemetry_command_preview(&command),
                );
                std::process::exit(code);
            }
            "-t" | "--track" => {
                let cmd_args = &args[2..];
                if cmd_args.is_empty() {
                    eprintln!("Usage: nebu-ctx -t \"command\" or nebu-ctx -t <exe> [args...]");
                    std::process::exit(1);
                }
                let tracked_name = cmd_args
                    .first()
                    .map(|s| core::stats::normalize_command(s))
                    .unwrap_or_else(|| "shell".to_string());
                let code = if cmd_args.len() > 1 {
                    shell::exec_argv(cmd_args)
                } else {
                    let command = cmd_args[0].clone();
                    if std::env::var("NEBU_CTX_ACTIVE").is_ok()
                        || std::env::var("NEBU_CTX_DISABLED").is_ok()
                    {
                        passthrough(&command);
                    }
                    shell::exec(&command)
                };
                core::stats::flush();
                let command_preview = if cmd_args.len() > 1 {
                    core::sanitize::telemetry_command_preview(&shell::join_command(cmd_args))
                } else {
                    cmd_args
                        .first()
                        .and_then(|command| core::sanitize::telemetry_command_preview(command))
                };
                fire_shell_telemetry(tracked_name, command_preview);
                std::process::exit(code);
            }
            "init" => {
                eprintln!("nebu-ctx: `init` is deprecated; use `setup` instead.");
                super::cmd_init(&rest);
                return;
            }
            "shell" | "--shell" => {
                shell::interactive();
                return;
            }
            "gain" => exit_unsupported("gain", "ctx(domain=analytics, action=report)"),
            "token-report" | "report-tokens" => {
                exit_unsupported("token-report", "nebu-ctx status --json")
            }
            "cep" => exit_unsupported("cep", "ctx(domain=analytics, action=report)"),
            "serve" => exit_unsupported("serve", "use server/src/NebuCtx.Server.Host instead"),
            "proxy" => exit_unsupported("proxy", "no thin-client replacement"),
            "setup" => {
                if let Some(mode) = rest.first().map(|s| s.as_str()) {
                    if matches!(mode, "bash" | "zsh" | "fish" | "powershell" | "pwsh") {
                        super::cmd_init(&rest);
                        return;
                    }
                }
                if rest.iter().any(|a| a == "--agent")
                    || rest.iter().any(|a| a == "--dry-run")
                    || rest.iter().any(|a| a == "--global" || a == "-g")
                {
                    super::cmd_init(&rest);
                    return;
                }
                let non_interactive = rest.iter().any(|a| a == "--non-interactive");
                let yes = rest.iter().any(|a| a == "--yes" || a == "-y");
                let fix = rest.iter().any(|a| a == "--fix");
                let json = rest.iter().any(|a| a == "--json");

                if non_interactive || fix || json || yes {
                    let opts = setup::SetupOptions {
                        non_interactive,
                        yes,
                        fix,
                        json,
                    };
                    match setup::run_setup_with_options(opts) {
                        Ok(report) => {
                            if json {
                                println!(
                                    "{}",
                                    serde_json::to_string_pretty(&report)
                                        .unwrap_or_else(|_| "{}".to_string())
                                );
                            }
                            if !report.success {
                                std::process::exit(1);
                            }
                        }
                        Err(e) => {
                            eprintln!("{e}");
                            std::process::exit(1);
                        }
                    }
                } else {
                    setup::run_setup();
                }
                return;
            }
            "bootstrap" => {
                exit_unsupported("bootstrap", "nebu-ctx setup --non-interactive --fix --yes")
            }
            "project-bootstrap" => exit_unsupported("project-bootstrap", "no thin-client replacement"),
            "status" => {
                let code = status::run_cli(&rest);
                if code != 0 {
                    std::process::exit(code);
                }
                return;
            }
            "sync" => {
                let code = sync_cli::run_cli(&rest);
                if code != 0 {
                    std::process::exit(code);
                }
                return;
            }
            "read" => exit_unsupported("read", "ctx_read(path=..., mode=...)"),
            "diff" => exit_unsupported("diff", "ctx_read(...) via MCP"),
            "grep" => exit_unsupported("grep", "ctx_search(pattern=...)"),
            "find" | "ls" => exit_unsupported("ls", "ctx_tree(path=...)"),
            "deps" | "discover" | "filter" => exit_unsupported(command, "ctx(...) via MCP"),
            "graph" => {
                let mut action = "build";
                let mut path_arg: Option<&str> = None;
                for arg in &rest {
                    if arg == "build" {
                        action = "build";
                    } else {
                        path_arg = Some(arg.as_str());
                    }
                }
                let root = path_arg
                    .map(String::from)
                    .or_else(|| {
                        std::env::current_dir()
                            .ok()
                            .map(|p| p.to_string_lossy().to_string())
                    })
                    .unwrap_or_else(|| ".".to_string());
                match action {
                    "build" => {
                        let index = core::graph_index::load_or_build(&root);
                        println!(
                            "Graph built: {} files, {} edges",
                            index.files.len(),
                            index.edges.len()
                        );
                    }
                    _ => {
                        eprintln!("Usage: nebu-ctx graph [build] [path]");
                    }
                }
                return;
            }
            "session" | "wrapped" | "sessions" | "benchmark" | "cache" | "tee"
            | "terse" | "slow-log" | "cheat" | "cheatsheet" | "cheat-sheet" => {
                exit_unsupported(command, "no thin-client replacement")
            }
            "config" => exit_unsupported("config", "edit ~/.nebu-ctx/config.toml manually"),
            "theme" => exit_unsupported("theme", "edit ~/.nebu-ctx/config.toml manually"),

            "doctor" => {
                let code = doctor::run_cli(&rest);
                if code != 0 {
                    std::process::exit(code);
                }
                return;
            }
            "failures" | "failure-memory" | "bugs" | "bug-memory" => {
                super::connect::cmd_bug_memory(&rest);
                return;
            }
            "buddy" | "pet" => {
                super::connect::cmd_buddy(&rest);
                return;
            }
            "hook" => {
                let action = rest.first().map(|s| s.as_str()).unwrap_or("help");
                match action {
                    "rewrite" => hook_handlers::handle_rewrite(),
                    "redirect" => hook_handlers::handle_redirect(),
                    "copilot" => hook_handlers::handle_copilot(),
                    "codex-pretooluse" => hook_handlers::handle_codex_pretooluse(),
                    "codex-session-start" => hook_handlers::handle_codex_session_start(),
                    "rewrite-inline" => hook_handlers::handle_rewrite_inline(),
                    "stop" => hook_handlers::handle_stop(),
                    "idle-flush" => hook_handlers::handle_idle_flush(),
                    "post-tool-use" => hook_handlers::handle_post_tool_use(),
                    "tool-activity" => hook_handlers::handle_tool_activity(),
                    "pre-compact" => hook_handlers::handle_pre_compact(),
                    "session-start" => hook_handlers::handle_session_start(),
                    "user-prompt-submit" => hook_handlers::handle_user_prompt_submit(),
                    "assistant-output-submit" => hook_handlers::handle_assistant_output_submit(),
                    "telemetry" => {
                        let tool_name = rest
                            .get(1)
                            .cloned()
                            .unwrap_or_else(|| "unknown".to_string());
                        let tokens_original: i64 =
                            rest.get(2).and_then(|s| s.parse().ok()).unwrap_or(0);
                        let tokens_saved: i64 =
                            rest.get(3).and_then(|s| s.parse().ok()).unwrap_or(0);
                        let project_context = std::env::current_dir()
                            .ok()
                            .map(|dir| crate::git_context::discover_project_context(&dir));
                        core::telemetry_queue::fire_sync(crate::models::TelemetryIngestRequest {
                            tool_name: core::stats::normalize_command(&tool_name),
                            tokens_original,
                            tokens_saved,
                            duration_ms: 0,
                            mode: Some("plugin".to_string()),
                            repository_fingerprint: project_context.as_ref().and_then(|context| {
                                context
                                    .fingerprint
                                    .has_safe_identity()
                                    .then(|| context.fingerprint.clone())
                            }),
                            checkout_binding: project_context
                                .as_ref()
                                .map(|context| context.checkout_binding.clone()),
                            project_slug: project_context
                                .as_ref()
                                .map(|context| context.project_slug.clone())
                                .filter(|slug| !slug.is_empty()),
                            command_preview: None,
                        });
                    }
                    _ => {
                        eprintln!("Usage: nebu-ctx hook <rewrite|redirect|copilot|codex-pretooluse|codex-session-start|rewrite-inline|stop|idle-flush|post-tool-use|tool-activity|pre-compact|session-start|user-prompt-submit|assistant-output-submit|telemetry>");
                        eprintln!("  Internal commands used by agent hooks (Claude, Cursor, Copilot, etc.)");
                        std::process::exit(1);
                    }
                }
                return;
            }
            "report-issue" | "report" => {
                exit_unsupported("report-issue", "use gh issue create --repo MarkBovee/nebu-ctx")
            }
            "uninstall" => {
                uninstall::run();
                return;
            }
            "bypass" => {
                if rest.is_empty() {
                    eprintln!("Usage: nebu-ctx bypass \"command\"");
                    eprintln!("Runs the command with zero compression (raw passthrough).");
                    std::process::exit(1);
                }
                let command = if rest.len() == 1 {
                    rest[0].clone()
                } else {
                    shell::join_command(&args[2..])
                };
                std::env::set_var("NEBU_CTX_RAW", "1");
                let code = shell::exec(&command);
                std::process::exit(code);
            }
            "safety-levels" | "safety" => {
                println!("{}", core::compression_safety::format_safety_table());
                return;
            }
            "connect" => {
                super::connect::cmd_connect(&rest);
                return;
            }
            "disconnect" => {
                super::connect::cmd_disconnect();
                return;
            }
            "--version" | "-V" => {
                println!("nebu-ctx {}", env!("CARGO_PKG_VERSION"));
                return;
            }
            "--help" | "-h" => {
                print_help();
                return;
            }
            "mcp" => {}
            "on" => {
                eprintln!("nebu-ctx: `nebu-ctx on` is a shell function, not a binary command.");
                eprintln!("  Run: source ~/.nebu-ctx/shell-hook.fish  (fish)");
                eprintln!("  Or add the shell hook to your shell profile via: nebu-ctx setup");
                std::process::exit(1);
            }
            "off" => {
                eprintln!("nebu-ctx: `nebu-ctx off` is a shell function, not a binary command.");
                eprintln!("  Run: source ~/.nebu-ctx/shell-hook.fish  (fish)");
                eprintln!("  Or add the shell hook to your shell profile via: nebu-ctx setup");
                std::process::exit(1);
            }
            "on-brief" => {
                status::print_on_brief();
                return;
            }
            _ => {
                eprintln!("nebu-ctx: unknown command '{}'\n", args[1]);
                print_help();
                std::process::exit(1);
            }
        }
    }

    if let Err(e) = run_mcp_server() {
        eprintln!("nebu-ctx: {e}");
        std::process::exit(1);
    }
}

fn passthrough(command: &str) -> ! {
    let (shell, flag) = shell::shell_and_flag();
    let status = shell::spawn_shell_command(&shell, &flag, command, false)
        .env("NEBU_CTX_ACTIVE", "1")
        .status()
        .map(|s| s.code().unwrap_or(1))
        .unwrap_or(127);
    std::process::exit(status);
}

fn run_mcp_server() -> Result<()> {
    use rmcp::ServiceExt;
    use tracing_subscriber::EnvFilter;

    std::env::set_var("NEBU_CTX_MCP_SERVER", "1");
    ensure_mcp_host_connection_configured()?;

    let rt = tokio::runtime::Runtime::new()?;
    rt.block_on(async {
        tracing_subscriber::fmt()
            .with_env_filter(EnvFilter::from_default_env())
            .with_writer(std::io::stderr)
            .init();

        tracing::info!(
            "nebu-ctx v{} MCP server starting",
            env!("CARGO_PKG_VERSION")
        );

        let server = tools::create_server();
        core::telemetry_queue::start_drain_task();
        let transport =
            mcp_stdio::HybridStdioTransport::new_server(tokio::io::stdin(), tokio::io::stdout());
        let service = server.serve(transport).await?;
        service.waiting().await?;

        core::stats::flush();
        core::mode_predictor::ModePredictor::flush();
        core::feedback::FeedbackStore::flush();

        Ok(())
    })
}

fn ensure_mcp_host_connection_configured() -> Result<()> {
    if crate::config::load_connection()?.is_some() {
        return Ok(());
    }

    anyhow::bail!(
        "nebu-ctx host connection is not configured yet.\n\
Run: nebu-ctx status\n\
Connect to a local host:   nebu-ctx connect --endpoint http://127.0.0.1:4242 --token <token>\n\
Connect to a network host: nebu-ctx connect --endpoint http://192.168.1.50:4242 --token <token>\n\
Port 4242 is the MCP/host port. After connecting, retry your editor or agent."
    );
}

fn print_help() {
    println!(
        "nebu-ctx {version} — Thin Context Runtime for AI Agents

Public MCP surface: ctx_read, ctx_search, ctx_tree, ctx_shell, ctx

USAGE:
    nebu-ctx                          Start MCP server (stdio)
    nebu-ctx -t \"command\"             Track command (full output + stats, no compression)
    nebu-ctx -c \"command\"             Execute shell command with compressed output
    nebu-ctx -c <exe> [args...]        Execute argv directly to avoid re-quoting hazards
    nebu-ctx -c --raw \"command\"       Execute without compression (full output)
    nebu-ctx exec \"command\"           Same as -c

COMMANDS:
    setup                             Install shell/editor integration
    setup <shell>                     Print shell hook to stdout
    setup --global                    Install shell aliases to rc/profile file
    setup --agent <name>              Configure supported agent/editor integration
    connect [--endpoint URL --token TOKEN]
                                      Save and validate host connection
    disconnect                        Remove saved host connection
    status [--json]                   Show setup and host connection status
    sync [status|flush] [--json]      Inspect or replay queued server-bound sync items
    doctor [--fix] [--json]           Run diagnostics (and optionally repair)
    hook <...>                        Internal hook entrypoints
    on-brief                          Print shell startup brief
    uninstall                         Remove shell hook, MCP configs, and data directory

NOTES:
    Memory, analytics, and workflow state route through ctx(domain=...).
    Memory actions require a configured host connection via `nebu-ctx connect`.
    Legacy local helper commands now fail fast outside thin-client surface.

ENVIRONMENT:
    NEBU_CTX_DISABLED=1            Bypass ALL compression + prevent shell hook from loading
    NEBU_CTX_ENABLED=0             Prevent shell hook auto-start (nebu-ctx-on still works)
    NEBU_CTX_RAW=1                 Same as --raw for current command
    NEBU_CTX_AUTONOMY=false        Disable autonomous features
    NEBU_CTX_COMPRESS=1            Force compression (even for excluded commands)

OPTIONS:
    --version, -V                  Show version
    --help, -h                     Show this help

EXAMPLES:
    nebu-ctx -c \"git status --short --untracked-files=all\"
    nebu-ctx -c gh pr list
    nebu-ctx -c --raw \"cargo test --manifest-path client/Cargo.toml\"
    nebu-ctx setup
    nebu-ctx connect --endpoint http://127.0.0.1:4242 --token <token>
    nebu-ctx status --json
    nebu-ctx sync status --json
    nebu-ctx sync flush
    nebu-ctx doctor --fix --json

EVAL SETUP (starship/zoxide style — always in sync with binary version):
    # bash: add to ~/.bashrc
    eval \"$(nebu-ctx setup bash)\"
    # zsh: add to ~/.zshrc
    eval \"$(nebu-ctx setup zsh)\"
    # fish: add to ~/.config/fish/config.fish
    nebu-ctx setup fish | source
    # powershell: add to $PROFILE
    nebu-ctx setup powershell | Invoke-Expression
    nebu-ctx-on                    Enable shell aliases in track mode (full output + stats)
    nebu-ctx-off                   Disable all shell aliases
    nebu-ctx-mode track            Track mode: full output, stats recorded (default)
    nebu-ctx-mode compress         Compress mode: all output compressed (power users)
    nebu-ctx-mode off              Same as nebu-ctx-off
    nebu-ctx-status                Show whether compression is active
    nebu-ctx doctor                Check PATH, config, MCP, and local edge health
    nebu-ctx doctor --fix --json   Repair + machine-readable report
    nebu-ctx status --json         Machine-readable current status
    nebu-ctx sync status --json    Inspect queued offline sync items
    nebu-ctx sync flush            Replay queued offline sync items once

HOST CONNECTION:
    connect [--endpoint <url>] [--token <token>]  Save and validate a server connection
    status                         Includes host connection status
    disconnect                     Remove the saved server connection

TROUBLESHOOTING:
    Commands broken?     nebu-ctx-off             (fixes current session)
    Permanent fix?       nebu-ctx uninstall       (removes all hooks)
    Manual fix?          Edit ~/.zshrc, remove the \"nebu-ctx shell hook\" block
    Binary missing?      Aliases auto-fallback to original commands (safe)
    Preview setup?       nebu-ctx setup --global --dry-run

WEBSITE: https://nebu-ctx.com
GITHUB:  https://github.com/MarkBovee/nebu-ctx
",
        version = env!("CARGO_PKG_VERSION"),
    );
}
