//! The `bynk` driver command-line interface.
//!
//! Kept thin: the driver shells `bynkc` and the Node toolchain. v0.46 ships a
//! single subcommand, `doctor`; `new` and `dev` join it in later slices.

use std::path::PathBuf;

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

use crate::doctor::{Capability, DoctorOptions};
use crate::report::Format;

#[derive(Parser, Debug)]
#[command(name = "bynk", version, about = "The Bynk driver", long_about = None)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Command,
}

#[derive(Subcommand, Debug)]
pub enum Command {
    /// Check whether your machine is ready to compile, test, and deploy Bynk —
    /// and print the exact remedy for anything missing.
    ///
    /// Bare `bynk doctor` is informational: it surveys every capability and
    /// exits 0 unless `bynkc` itself is unusable. `--only <capability>` gates on
    /// one capability (exits non-zero if its tools are missing); `--strict`
    /// turns every warning into a failure, for CI.
    Doctor {
        /// Project directory to inspect (for project-local `node_modules/.bin`
        /// resolution). Defaults to the current directory.
        #[arg(default_value = ".")]
        input: PathBuf,
        /// Scope the check — and the exit code — to one capability.
        #[arg(long, value_enum)]
        only: Option<CapabilityArg>,
        /// Treat every warning (optional gaps, npx provisionability, minor
        /// version skew) as a failure. For an all-green CI gate.
        #[arg(long)]
        strict: bool,
        /// Output format. `human` (default) is a grouped table; `short` and
        /// `json` are the stable scriptable surface.
        #[arg(long, value_enum, default_value = "human")]
        format: FormatArg,
    },
    /// Build the project and serve it locally with `wrangler dev` — one step in
    /// place of the manual compile + `cd` + `wrangler dev` recipe.
    ///
    /// Compiles into a managed `.bynk/dev/` build dir, picks the worker to serve
    /// (one context → served; `--context` to choose; ambiguous → lists them),
    /// and runs `wrangler dev` from inside it in local mode (Miniflare) — no
    /// namespace provisioning needed. Everything after `--` is forwarded to
    /// `wrangler dev` verbatim.
    Dev {
        /// Project directory to serve from (anywhere inside the project; the
        /// root is found by walking up for `bynk.toml`). Defaults to `.`.
        #[arg(default_value = ".")]
        path: PathBuf,
        /// Which context's worker to serve, for multi-context projects.
        #[arg(long)]
        context: Option<String>,
        /// Serve with the V8 inspector enabled (slice 3, ADR 0104): `wrangler dev`
        /// starts with `--inspector-port` so a JavaScript debugger can attach.
        /// Breakpoints set in `.bynk` sources resolve through the emitted source
        /// maps, composed into the worker bundle. Prints the inspector URL on start.
        #[arg(long)]
        inspect: bool,
        /// Inspector port for `--inspect` (default 9229).
        #[arg(long, default_value_t = 9229)]
        inspect_port: u16,
        /// Arguments after `--`, forwarded to `wrangler dev` (e.g. `-- --port 8788`).
        #[arg(last = true)]
        wrangler_args: Vec<String>,
    },
    /// Scaffold a new project: a complete, runnable single-context HTTP service
    /// you can serve immediately with `bynk dev`.
    ///
    /// Writes a `bynk.toml`, a `.gitignore`, and `src/<name>.bynk` into a new
    /// directory. Pure offline file-writing — it shells nothing and needs no
    /// toolchain, so you can run it before `bynkc`, Node, or `wrangler` are
    /// installed. The project name defaults to the target directory's final
    /// component; `--name` overrides it and must be a legal Bynk identifier.
    New {
        /// Directory to create for the new project (e.g. `hello` or `./hello`).
        path: PathBuf,
        /// Project name / context identifier. Defaults to PATH's final
        /// component; must be a legal Bynk identifier (a letter followed by
        /// letters, digits, or underscores).
        #[arg(long)]
        name: Option<String>,
    },
}

/// `--only` selector. Mirrors [`Capability`] minus the internal distinctions.
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
pub enum CapabilityArg {
    /// `bynkc` compile / check / fmt.
    Compile,
    /// `bynk test` — Node + tsc|tsx.
    Test,
    /// dev / deploy to Cloudflare — Node + wrangler.
    Deploy,
    /// Editor support — bynkc-lsp.
    Editor,
    /// Build Bynk from source — a Rust toolchain.
    Build,
}

impl From<CapabilityArg> for Capability {
    fn from(a: CapabilityArg) -> Self {
        match a {
            CapabilityArg::Compile => Capability::Compile,
            CapabilityArg::Test => Capability::Test,
            CapabilityArg::Deploy => Capability::Deploy,
            CapabilityArg::Editor => Capability::Editor,
            CapabilityArg::Build => Capability::BuildFromSource,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, ValueEnum)]
pub enum FormatArg {
    #[default]
    Human,
    Short,
    Json,
}

impl From<FormatArg> for Format {
    fn from(f: FormatArg) -> Self {
        match f {
            FormatArg::Human => Format::Human,
            FormatArg::Short => Format::Short,
            FormatArg::Json => Format::Json,
        }
    }
}

/// Build the [`DoctorOptions`] from the parsed flags.
pub fn doctor_options(only: Option<CapabilityArg>, strict: bool) -> DoctorOptions {
    DoctorOptions {
        only: only.map(Into::into),
        strict,
    }
}
