pub mod aws;
pub mod bazel;
pub mod biome;
pub mod brew;
pub mod cargo;
pub mod clippy;
pub mod cmake;
pub mod dotnet;
pub mod json;
pub mod log;
pub mod curl;
pub mod diff;
pub mod docker;
pub mod env;
pub mod ember;
pub mod eslint;
pub mod find;
pub mod focus_compress;
pub mod gh;
pub mod git;
pub mod go;
pub mod golangci_lint;
pub mod grep;
pub mod helm;
pub mod jest;
pub mod journalctl;
pub mod jq;
pub mod kubectl;
pub mod ls;
pub mod make;
pub mod maven;
pub mod mypy;
pub mod next;
pub mod npm;
pub mod nx;
pub mod pip;
pub mod playwright;
pub mod pnpm;
pub mod prettier;
pub mod prisma;
pub mod ruff;
pub mod stylelint;
pub mod swift;
pub mod turbo;
pub mod uv;
pub mod vite;
pub mod webpack;
pub mod psql;
pub mod pytest;
pub mod python;
pub mod rake;
pub mod read;
pub mod rspec;
pub mod rubocop;
pub mod terraform;
pub mod tree;
pub mod tsc;
pub mod util;
pub mod vitest;
pub mod wget;
pub mod rsync;
pub mod ffmpeg;

/// A specialized handler for a known command.
/// Handlers may inject extra flags (`rewrite_args`) and compact the output (`filter`).
pub trait Handler: Send + Sync {
    /// Optionally rewrite the argument list before execution (e.g. inject --message-format json).
    fn rewrite_args(&self, args: &[String]) -> Vec<String> {
        args.to_vec()
    }

    /// Filter the combined stdout+stderr output into a compact representation.
    fn filter(&self, output: &str, args: &[String]) -> String;
}

/// Returns a handler for the given command name.
/// Falls through a three-level lookup:
/// 1. Exact registry match
/// 2. Static alias / pattern table (covers versioned binaries, wrappers, common aliases)
/// 3. BERT similarity routing for truly unknown commands
pub fn get_handler(cmd: &str) -> Option<Box<dyn Handler>> {
    // Level 0: user-defined TOML filters (.ccr/filters.toml or ~/.config/ccr/filters.toml)
    let user_filters = crate::user_filters::load_user_filters();
    if let Some(filter_def) = user_filters.commands.get(cmd) {
        return Some(Box::new(crate::user_filters::UserFilterHandler::new(filter_def.clone())));
    }

    get_handler_exact(cmd)
        .or_else(|| get_handler_alias(cmd))
        .or_else(|| get_handler_bert(cmd))
}

pub(super) fn get_handler_exact(cmd: &str) -> Option<Box<dyn Handler>> {
    match cmd {
        // Existing handlers
        "cargo" => Some(Box::new(cargo::CargoHandler)),
        "curl" => Some(Box::new(curl::CurlHandler)),
        "git" => Some(Box::new(git::GitHandler)),
        "docker" | "docker-compose" => Some(Box::new(docker::DockerHandler)),
        "npm" | "yarn" => Some(Box::new(npm::NpmHandler)),
        "pnpm" => Some(Box::new(pnpm::PnpmHandler)),
        "ls" => Some(Box::new(ls::LsHandler)),
        "cat" => Some(Box::new(read::ReadHandler)),
        "grep" | "rg" => Some(Box::new(grep::GrepHandler)),
        "find" => Some(Box::new(find::FindHandler)),
        // Batch 1: TypeScript / JavaScript
        "tsc" => Some(Box::new(tsc::TscHandler)),
        "vitest" => Some(Box::new(vitest::VitestHandler)),
        "jest" => Some(Box::new(jest::JestHandler)),
        "eslint" => Some(Box::new(eslint::EslintHandler)),
        // Batch 2: Python
        "pytest" => Some(Box::new(pytest::PytestHandler)),
        "pip" | "pip3" => Some(Box::new(pip::PipHandler)),
        "uv" => Some(Box::new(uv::UvHandler)),
        "ruff" => Some(Box::new(ruff::RuffHandler)),
        "mypy" | "mypy3" => Some(Box::new(mypy::MypyHandler)),
        "python" | "python3" => Some(Box::new(python::PythonHandler)),
        // Batch 3: DevOps / Cloud
        "kubectl" => Some(Box::new(kubectl::KubectlHandler)),
        "gh" => Some(Box::new(gh::GhHandler)),
        "terraform" | "tofu" => Some(Box::new(terraform::TerraformHandler)),
        "aws" => Some(Box::new(aws::AwsHandler)),
        "make" | "gmake" => Some(Box::new(make::MakeHandler)),
        // Batch 4: System / Utility
        "psql" | "pgcli" => Some(Box::new(psql::PsqlHandler)),
        "tree" => Some(Box::new(tree::TreeHandler)),
        "diff" => Some(Box::new(diff::DiffHandler)),
        "jq" => Some(Box::new(jq::JqHandler)),
        "env" | "printenv" => Some(Box::new(env::EnvHandler)),
        // Batch 5: High-priority new handlers
        "go" => Some(Box::new(go::GoHandler)),
        "mvn" => Some(Box::new(maven::MavenHandler)),
        "gradle" | "./gradlew" | "gradlew" => Some(Box::new(maven::GradleHandler)),
        "brew" => Some(Box::new(brew::BrewHandler)),
        "helm" => Some(Box::new(helm::HelmHandler)),
        "journalctl" => Some(Box::new(journalctl::JournalctlHandler)),
        "json" => Some(Box::new(json::JsonHandler)),
        "log" => Some(Box::new(log::LogHandler)),
        // Batch 6: New handlers
        "ember" => Some(Box::new(ember::EmberHandler)),
        "clippy" | "cargo-clippy" => Some(Box::new(clippy::ClippyHandler)),
        "next" | "next.js" => Some(Box::new(next::NextHandler)),
        "playwright" => Some(Box::new(playwright::PlaywrightHandler)),
        "prisma" => Some(Box::new(prisma::PrismaHandler)),
        "golangci-lint" | "golangci_lint" => Some(Box::new(golangci_lint::GolangCiLintHandler)),
        "prettier" => Some(Box::new(prettier::PrettierHandler)),
        // Frontend build tools / monorepo runners
        "nx" => Some(Box::new(nx::NxHandler)),
        "vite" => Some(Box::new(vite::ViteHandler)),
        "webpack" | "webpack-cli" => Some(Box::new(webpack::WebpackHandler)),
        "turbo" => Some(Box::new(turbo::TurboHandler)),
        // CSS / universal linters
        "stylelint" => Some(Box::new(stylelint::StylelintHandler)),
        "biome" => Some(Box::new(biome::BiomeHandler)),
        // Ruby ecosystem
        "rspec" => Some(Box::new(rspec::RspecHandler)),
        "rubocop" => Some(Box::new(rubocop::RubocopHandler)),
        "rake" => Some(Box::new(rake::RakeHandler)),
        // Network utilities
        "wget" => Some(Box::new(wget::WgetHandler)),
        // File transfer / media
        "rsync" => Some(Box::new(rsync::RsyncHandler)),
        "ffmpeg" | "ffprobe" => Some(Box::new(ffmpeg::FfmpegHandler)),
        // New language build tools
        "swift" => Some(Box::new(swift::SwiftHandler)),
        "dotnet" => Some(Box::new(dotnet::DotnetHandler)),
        "cmake" => Some(Box::new(cmake::CmakeHandler)),
        "bazel" => Some(Box::new(bazel::BazelHandler)),
        _ => None,
    }
}


// ── Level 2: Static alias / pattern routing ───────────────────────────────────

/// Maps command name patterns to canonical handler keys.
/// Covers versioned binaries, wrapper scripts, and well-known aliases.
const STATIC_ALIASES: &[(&str, &str)] = &[
    // Python variants
    ("python3.8",  "python"), ("python3.9",  "python"), ("python3.10", "python"),
    ("python3.11", "python"), ("python3.12", "python"), ("python3.13", "python"),
    ("py",         "python"),
    // pip variants
    ("pip3.9",  "pip"), ("pip3.10", "pip"), ("pip3.11", "pip"), ("pip3.12", "pip"),
    ("poetry",  "pip"), ("pdm",     "pip"), ("conda",   "pip"),
    // uv variants
    ("uvx",     "uv"),
    // pytest variants
    ("py.test",  "pytest"), ("pytest3", "pytest"),
    // JS runtimes that run jest-style tests
    ("bun",  "jest"),
    ("deno", "jest"),
    // Build / task runners
    ("npx nx",      "nx"),
    ("./gradlew",   "gradle"), ("gradlew",   "gradle"),
    ("./mvnw",      "mvn"),    ("mvnw",      "mvn"),
    ("ninja",       "make"),   ("bmake",     "make"),
    // Next.js variants
    ("next-router",     "next"),
    // Playwright variants
    ("npx playwright",  "playwright"),
    // Prettier variants
    ("prettier2",       "prettier"),
    // Vite variants
    ("vitest",          "vitest"),  // kept separate; vite dev/build → vite
    // Turbo variants
    ("npx turbo",       "turbo"),   ("./node_modules/.bin/turbo", "turbo"),
    // Webpack variants
    ("npx webpack",     "webpack"), ("./node_modules/.bin/webpack", "webpack"),
    // Biome variants
    ("@biomejs/biome",  "biome"),
    // Go linter variants
    ("golangci",        "golangci-lint"),
    // Node.js local-bin executables (when not on PATH but in node_modules)
    ("./node_modules/.bin/eslint",    "eslint"),
    ("./node_modules/.bin/tsc",       "tsc"),
    ("./node_modules/.bin/prettier",  "prettier"),
    ("./node_modules/.bin/stylelint", "stylelint"),
    ("./node_modules/.bin/biome",     "biome"),
    ("./node_modules/.bin/jest",      "jest"),
    ("./node_modules/.bin/vitest",    "vitest"),
    // Python linters that aren't always on PATH by their own name
    ("flake8",    "mypy"),  // same file:line:col: severity: message format
    ("flake8-3",  "mypy"),
    ("pylint",    "mypy"),  // similar diagnostic line format
    ("ruff-check","ruff"),
    // Ruby ecosystem aliases
    // NOTE: "bundle" is intentionally NOT aliased to "rake" here.
    // `bundle exec rubocop` / `bundle exec rspec` should be handled by
    // the transparent_prefix stripping ("bundle exec" stripped → rubocop/rspec
    // routed directly). A "bundle"→"rake" alias was misrouting lint runs.
    ("rubocop-rails",   "rubocop"),
    // Kubernetes wrappers
    ("k",           "kubectl"), ("kubectl.exe", "kubectl"),
    ("minikube",    "kubectl"), ("kind",        "kubectl"),
    // Helm variants
    ("helm3",             "helm"),
    ("pnpx",              "pnpm"),
    // Terraform variants
    ("terraform1",  "terraform"),
    // Cloud CLIs
    ("gcloud",      "aws"), // similar output pattern
    ("az",          "aws"),
    // Swift variants
    ("swift-build", "swift"),
    ("swift-test",  "swift"),
    // .NET CLI variants
    ("dotnet-cli",  "dotnet"),
    // CMake variants
    ("cmake3",      "cmake"),
    // Bazel variants
    ("bazelisk",    "bazel"),
    ("bzl",         "bazel"),
];

pub(super) fn get_handler_alias(cmd: &str) -> Option<Box<dyn Handler>> {
    // Exact alias lookup
    for &(alias, target) in STATIC_ALIASES {
        if alias == cmd {
            return get_handler_exact(target);
        }
    }
    // Pattern: any `python3.X` not in the static list
    if cmd.starts_with("python3.") || cmd.starts_with("python2.") {
        return get_handler_exact("python");
    }
    // Pattern: any `pip3.X`
    if cmd.starts_with("pip3.") || cmd.starts_with("pip2.") {
        return get_handler_exact("pip");
    }
    None
}

// ── Level 3: BERT similarity routing ─────────────────────────────────────────

/// Wraps an inner handler to disable rewrite_args (filter output only, no arg injection).
/// Used for MEDIUM confidence BERT routes where we're not confident enough to modify args.
struct FilterOnlyHandler {
    inner: Box<dyn Handler>,
}

impl Handler for FilterOnlyHandler {
    fn rewrite_args(&self, args: &[String]) -> Vec<String> {
        args.to_vec()  // no-op: don't modify args when confidence is medium
    }
    fn filter(&self, output: &str, args: &[String]) -> String {
        self.inner.filter(output, args)
    }
}

/// Handler representatives: (canonical_name, display_label) for embedding.
/// We embed the canonical name and compare against the unknown command.
const HANDLER_REPS: &[(&str, &str)] = &[
    ("cargo build test check clippy",   "cargo"),
    ("git commit push pull diff log",   "git"),
    ("docker run logs ps images build", "docker"),
    ("npm install test run script",     "npm"),
    ("kubectl get logs describe apply", "kubectl"),
    ("terraform plan apply init",       "terraform"),
    ("pytest test assertion failure",   "pytest"),
    ("jest test describe it expect",    "jest"),
    ("go build test run mod",           "go"),
    ("mvn build test install compile",  "mvn"),
    ("helm install upgrade list diff",  "helm"),
    ("brew install update upgrade",     "brew"),
    ("aws ec2 s3 lambda iam",           "aws"),
    ("make build clean install target", "make"),
    ("psql query select insert",        "psql"),
    ("next build dev lint start",       "next"),
    ("playwright test browser e2e",     "playwright"),
    ("prisma generate migrate schema",  "prisma"),
    ("golangci-lint run check issues",  "golangci-lint"),
    ("prettier format check write",     "prettier"),
    ("pnpm install add run exec",       "pnpm"),
    ("clippy warning lint rust",        "clippy"),
    ("json parse schema object array",  "json"),
    ("log output lines errors warnings","log"),
    ("uv install add sync lock venv",   "uv"),
    ("ruff check format lint python",   "ruff"),
    ("mypy type check error annotation","mypy"),
    ("nx run build test affected graph","nx"),
    ("swift build test package run compile",         "swift"),
    ("dotnet build test run restore publish",        "dotnet"),
    ("cmake build configure generate compile make",  "cmake"),
    ("bazel build test run query target action",     "bazel"),
];

const BERT_THRESHOLD_HIGH: f32 = 0.70;  // Full handler (filter + rewrite_args)
const BERT_THRESHOLD_MED:  f32 = 0.55;  // Filter only (no rewrite_args)
const BERT_MARGIN_HIGH:    f32 = 0.15;  // Gap between top-1 and top-2 for HIGH
const BERT_MARGIN_MED:     f32 = 0.08;  // Gap between top-1 and top-2 for MED
const BERT_SUBCOMMAND_BOOST: f32 = 0.08; // Boost when subcommand matches known pattern

/// Maps known subcommand words to the handler labels they strongly suggest.
/// If `cmd` contains a space and the subcommand matches, apply a boost to that handler's score.
const SUBCOMMAND_HINTS: &[(&str, &str)] = &[
    // test-runner subcommands
    ("test",    "pytest"),
    ("test",    "jest"),
    ("test",    "vitest"),
    ("test",    "go"),
    // build tools
    ("build",   "cargo"),
    ("build",   "go"),
    ("build",   "docker"),
    ("build",   "next"),
    ("build",   "mvn"),
    // install / add
    ("install", "npm"),
    ("install", "pnpm"),
    ("install", "brew"),
    ("install", "pip"),
    ("install", "helm"),
    // run / exec
    ("run",     "go"),
    ("run",     "cargo"),
    ("run",     "docker"),
    // lint
    ("lint",    "eslint"),
    ("lint",    "golangci-lint"),
    ("lint",    "clippy"),
    ("check",   "cargo"),
    ("check",   "tsc"),
    // deploy / infra
    ("plan",    "terraform"),
    ("apply",   "terraform"),
    ("apply",   "kubectl"),
];

fn get_handler_bert(cmd: &str) -> Option<Box<dyn Handler>> {
    if cmd.contains('/') || cmd.starts_with('-') || cmd.is_empty() {
        return None;
    }

    // Split into binary and subcommand (e.g. "bloop test" → binary="bloop", sub=Some("test"))
    let mut parts = cmd.splitn(2, ' ');
    let binary = parts.next().unwrap_or(cmd);
    let subcommand = parts.next().unwrap_or("").to_lowercase();

    // Embed all rep phrases + the binary name
    let reps: Vec<&str> = HANDLER_REPS.iter().map(|(rep, _)| *rep).collect();
    let mut all_texts = reps.clone();
    all_texts.push(binary);  // embed just the binary, not the full cmd with args

    let embeddings = panda_core::summarizer::embed_batch(&all_texts).ok()?;
    let cmd_emb = embeddings.last()?;
    let rep_embs = &embeddings[..embeddings.len() - 1];

    // Compute similarities with optional subcommand boost
    let mut scores: Vec<(usize, f32)> = rep_embs
        .iter()
        .enumerate()
        .map(|(i, emb)| {
            let mut sim = util::cosine_similarity(emb, cmd_emb);
            // Apply subcommand boost if the subcommand matches this handler
            if !subcommand.is_empty() {
                let target_label = HANDLER_REPS[i].1;
                let matches = SUBCOMMAND_HINTS
                    .iter()
                    .any(|(sub, lbl)| *sub == subcommand.as_str() && *lbl == target_label);
                if matches {
                    sim += BERT_SUBCOMMAND_BOOST;
                    sim = sim.min(1.0);
                }
            }
            (i, sim)
        })
        .collect();

    // Sort descending by score to find top-1 and top-2
    scores.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));

    let (best_idx, best_sim) = scores[0];
    let second_sim = scores.get(1).map(|(_, s)| *s).unwrap_or(0.0);
    let margin = best_sim - second_sim;

    let target = HANDLER_REPS[best_idx].1;

    if best_sim >= BERT_THRESHOLD_HIGH && margin >= BERT_MARGIN_HIGH {
        // HIGH confidence: full handler including rewrite_args
        get_handler_exact(target)
    } else if best_sim >= BERT_THRESHOLD_MED && margin >= BERT_MARGIN_MED {
        // MEDIUM confidence: filter only, no arg rewriting
        get_handler_exact(target).map(|h| -> Box<dyn Handler> {
            Box::new(FilterOnlyHandler { inner: h })
        })
    } else {
        None
    }
}
