#!/usr/bin/env nu
# Single CLI entry — replaces legacy nulib/provisioning runner (ADR-025 Phase 4).
#
# Single-route architecture: every command goes through dispatch_command, which
# lazy-loads per-domain handlers on demand. The star-imports that dominated
# cold-start in the legacy runner are gone; only the dispatcher surface + a
# handful of init helpers are parsed on startup.
#
# Daemon and cache become orthogonal concerns applied INSIDE handlers (or their
# lazy dependencies), not separate routes.

export-env {
    let lib_dirs_raw = ($env.NU_LIB_DIRS? | default "")
    let current_lib_dirs = if ($lib_dirs_raw | type) == "string" {
        if ($lib_dirs_raw | is-empty) {
            []
        } else {
            ($lib_dirs_raw | split row ":")
        }
    } else {
        $lib_dirs_raw
    }

    let default_paths = [
        "/opt/provisioning/core/nulib"
        "/usr/local/provisioning/core/nulib"
    ]

    $env.NU_LIB_DIRS = ($default_paths | append $current_lib_dirs)

    if ( (version).installed_plugins | str contains "tera" ) {
        (plugin use tera)
    }
}

# ADR-025 Phase 4 perf insight: Nushell selective imports (`use x [sym]`) still
# parse the entire source module. To actually defer parse cost we must move
# `use` statements INSIDE function bodies — they're then evaluated only when
# the function is called, not at file-parse time. Parsing this file itself
# only sees two `def` headers and one `export-env` block.

# Pass-through: Nushell parameter parsing handles interleaved flags, so we
# just return args as-is. Preserved as a seam for future normalization.
def reorder_args [args: list]: nothing -> list { $args }

export def "main help" [
    ...args: string
    --notitles
    --out: string
] {
    use lib_provisioning/utils/init.nu [show_titles]
    use lib_provisioning/utils/interface.nu [end_run]
    use main_provisioning/ops.nu [provisioning_options]

    if $notitles == null or not $notitles { show_titles }
    if ($out | is-not-empty) { $env.PROVISIONING_NO_TERMINAL = false }
    let category = if ($args | length) > 0 { ($args | get 0) } else { "" }
    print (provisioning_options $category)
    if not $env.PROVISIONING_DEBUG { end_run "" }
}

def main [
    ...args: string
    --infra (-i): string
    --settings (-s): string
    --serverpos (-p): int
    --outfile (-o): string
    --template(-t): string
    --check (-c)
    --upload (-u)
    --yes (-y)
    --wait
    --keepstorage
    --select: string
    --onsel: string
    --infras: string
    --new (-n): string
    --debug (-x)
    --xm
    --xc
    --xr
    --xld
    --nc
    --metadata
    --notitles
    --environment: string
    --dep-option: string
    --dep-url: string
    --dry-run
    --force (-f)
    --all
    --keep-latest: int
    --workspace (-w): string
    --activate
    --interactive
    --org: string
    --apply
    --verbose
    --pretty
    -v
    --version (-V)
    --info
    --about
    --helpinfo (-h)
    --out: string
    --view
    --inputfile: string
    --include_notuse
    --services: string
]: nothing -> nothing {
    # Function-local imports: parsed only when main() is called, not at
    # file-parse time. Keeps cold-start for help-like shortcuts minimal.
    use lib_provisioning/utils/interface.nu [_ansi _print end_run]
    use lib_provisioning/utils/init.nu [provisioning_init]
    use lib_provisioning/defs/about.nu [about_info]
    use main_provisioning/flags.nu [parse_common_flags]
    use main_provisioning/ops.nu [provisioning_options]
    use main_provisioning/dispatcher.nu [dispatch_command]

    let reordered_args = (reorder_args $args)

    let has_yes_in_args     = ($reordered_args | any {|x| $x == "--yes"     or $x == "-y"})
    let has_check_in_args   = ($reordered_args | any {|x| $x == "--check"   or $x == "-c"})
    let has_upload_in_args  = ($reordered_args | any {|x| $x == "--upload"  or $x == "-u"})
    let has_force_in_args   = ($reordered_args | any {|x| $x == "--force"   or $x == "-f"})
    let has_verbose_in_args = ($reordered_args | any {|x| $x == "--verbose" or $x == "-v"})
    let has_wait_in_args    = ($reordered_args | any {|x| $x == "--wait"})

    let final_yes     = ($yes     or $has_yes_in_args)
    let final_check   = ($check   or $has_check_in_args)
    let final_upload  = ($upload  or $has_upload_in_args)
    let final_force   = ($force   or $has_force_in_args)
    let final_verbose = ($verbose or $has_verbose_in_args)
    let final_wait    = ($wait    or $has_wait_in_args)

    provisioning_init $helpinfo "" $reordered_args

    let parsed_flags = (parse_common_flags {
        version: $version, v: $v, info: $info, about: $about,
        debug: $debug, metadata: $metadata, xc: $xc, xr: $xr, xld: $xld,
        check: $final_check, upload: $final_upload, yes: $final_yes, wait: $final_wait, keepstorage: $keepstorage,
        nc: $nc, include_notuse: $include_notuse,
        out: $out, notitles: $notitles, view: $view,
        infra: $infra, infras: $infras, settings: $settings, outfile: $outfile,
        template: $template, select: $select, onsel: $onsel, serverpos: $serverpos,
        new: $new, environment: $environment,
        dep_option: $dep_option, dep_url: $dep_url,
        dry_run: $dry_run, force: $final_force, all: $all, keep_latest: $keep_latest,
        activate: $activate, interactive: $interactive,
        org: $org, apply: $apply, verbose: $final_verbose, pretty: $pretty,
        services: $services, workspace: $workspace
    })

    if $parsed_flags.show_version { ^$env.PROVISIONING_NAME -v ; exit }
    if $parsed_flags.show_info    { ^$env.PROVISIONING_NAME -i ; exit }
    if $parsed_flags.show_about   { _print (about_info) ; exit }

    let is_help_command = (
        ($reordered_args | length) == 0 or
        ($reordered_args | get 0) in [
            "help", "-h", "--help",
            "sc", "shortcuts", "quickstart", "quick",
            "from-scratch", "scratch",
            "customize", "custom",
            "guide", "guides", "howto",
            "setup", "st",
            "workspace", "ws",
            "mod", "module", "discover", "disc",
            "dt", "dp", "dc",
            "discover-taskservs", "disc-t",
            "discover-providers", "disc-p",
            "discover-clusters", "disc-c",
            "lyr", "layer", "version", "pack",
            "nuinfo", "env", "allenv",
            "validate", "val", "show", "config-template",
            "cache",
            "list", "l", "ls",
            "plugin", "plugins",
            "qr", "ssh", "sops",
            "providers",
            "status", "health", "diagnostics", "next", "phase"
        ]
    )

    let skip_bootstrap = (
        (($reordered_args | length) > 0 and
        ($reordered_args | get 0) in [
            "nu",
            "platform", "plat", "p",
            "vm", "vmi", "vmh", "vml",
            "server", "s",
            "taskserv", "task", "t",
            "cluster", "cl",
            "bootstrap",
            "create", "c",
            "delete", "d",
            "update", "u",
            "build", "b", "bi", "build-image"
        ]) or
        $final_check
    )

    if (not $is_help_command) and (not $skip_bootstrap) {
        use lib_provisioning/platform/bootstrap.nu *
        let bootstrap_result = (bootstrap-platform --auto-start --timeout=60 --verbose=($final_verbose))
        if not $bootstrap_result.all_healthy {
            _print ""
            _print $"(_ansi red)❌ Platform services not healthy(_ansi reset)"
            _print ""
            _print "Failed services:"
            for service in ($bootstrap_result.services | where {|s| $s.status != "healthy"}) {
                _print $"  - ($service.name): ($service.action)"
            }
            _print ""
            _print "To start services manually:"
            _print "  cd provisioning/platform && docker-compose up -d"
            _print ""
            exit 1
        }
    }

    if ($env.PROVISIONING_DEBUG? | default false) {
        print $"DEBUG provisioning-cli: reordered_args = ($reordered_args)" >&2
        print $"DEBUG provisioning-cli: parsed_flags.infra = (($parsed_flags | get -o infra | default 'MISSING'))" >&2
    }

    # Help: short-circuit before dispatcher to avoid recursive exec loops.
    if (($reordered_args | length) > 0) and (($reordered_args | get 0) in ["help" "h"]) {
        let category = if ($reordered_args | length) > 1 { ($reordered_args | get 1) } else { "" }
        print (provisioning_options $category)
        if not ($env.PROVISIONING_DEBUG? | default false) { end_run "" }
        return
    }

    # Info/discovery/utility commands bypass workspace enforcement.
    if (($reordered_args | length) > 0) and (($reordered_args | get 0) in [
        "guide", "guides", "sc", "howto", "shortcuts", "quickstart", "quick",
        "from-scratch", "scratch", "customize", "custom",
        "mod", "module", "discover", "disc",
        "dt", "dp", "dc",
        "discover-taskservs", "disc-t",
        "discover-providers", "disc-p",
        "discover-clusters", "disc-c",
        "lyr", "layer", "version",
        "nuinfo", "env", "allenv",
        "validate", "val", "show", "cache",
        "plugin", "plugins",
        "qr", "nuinfo",
        "status", "health", "diagnostics", "next", "phase"
    ]) {
        dispatch_command $reordered_args $parsed_flags
        if not $env.PROVISIONING_DEBUG { end_run "" }
        return
    }

    # -mod <module> mode: bash wrapper extracts `-mod <name>` into
    # PROVISIONING_MODULE and forwards remaining args. We invoke that module's
    # `main` directly, bypassing the dispatcher.
    if ($env.PROVISIONING_MODULE? | default "" | is-not-empty) {
        let module = $env.PROVISIONING_MODULE

        match $module {
            "server" => {
                use servers/create.nu *
                let tera_available = ((plugin list | where name == "tera" | length) > 0)
                if $tera_available {
                    if ($env.PROVISIONING_DEBUG? | default false) {
                        _print "DEBUG: Loading tera plugin (-mod server)..." >&2
                    }
                    (plugin use tera)
                    if ($env.PROVISIONING_DEBUG? | default false) {
                        _print "DEBUG: Tera plugin loaded for -mod server" >&2
                    }
                }
                main ...$reordered_args --check=$final_check --wait=$final_wait --infra=($infra | default "") --settings=($settings | default "") --outfile=($outfile | default "") --debug=$debug --xm=$xm --xc=$xc --xr=$xr --xld=$xld --metadata=$metadata --notitles=$notitles --out=($out | default "")
            }
            "taskserv" | "task" => {
                use taskservs/create.nu *
                main ...$reordered_args --check=$final_check --upload=$final_upload --wait=$final_wait --debug=$debug
            }
            "cluster" => {
                use clusters/create.nu *
                main ...$reordered_args --check=$final_check --debug=$debug
            }
            "images" => {
                use images/create.nu *
                use images/list.nu *
                use images/update.nu *
                use images/delete.nu *
                use images/state.nu *
                use images/watch.nu *
                let subcommand = if ($reordered_args | length) > 0 { $reordered_args | get 0 } else { "help" }
                match $subcommand {
                    "create" | "c" => {
                        let role = if ($reordered_args | length) > 1 { $reordered_args | get 1 } else { "" }
                        let infra_arg = if ($infra | is-not-empty) { $infra } else { "" }
                        image-create $role --infra=$infra_arg --check=$final_check
                    }
                    "list" | "l" => {
                        let provider = if ($infra | is-not-empty) { $infra } else { "" }
                        image-list --provider=$provider
                    }
                    "update" | "u" => {
                        let role = if ($reordered_args | length) > 1 { $reordered_args | get 1 } else { "" }
                        let infra_arg = if ($infra | is-not-empty) { $infra } else { "" }
                        image-update $role --infra=$infra_arg --check=$final_check
                    }
                    "delete" | "d" => {
                        let role = if ($reordered_args | length) > 1 { $reordered_args | get 1 } else { "" }
                        image-delete $role --yes=$final_yes
                    }
                    "state" | "s" => {
                        image-state-list --provider=$infra
                    }
                    "watch" | "w" => {
                        let interval = if ($reordered_args | length) > 1 { $reordered_args | get 1 } else { "30" }
                        image-watch --interval=($interval | into int)
                    }
                    "help" | "h" | _ => {
                        print "Image Management Commands"
                        print "======================="
                        print ""
                        print "Usage: provisioning build image <command> [options]"
                        print ""
                        print "Commands:"
                        print "  create <role>     - Build snapshot for role"
                        print "  list              - Show all role states"
                        print "  update <role>     - Rebuild stale snapshot"
                        print "  delete <role>     - Remove snapshot + state"
                        print "  state             - List all state files"
                        print "  watch             - Monitor role freshness"
                        print ""
                        print "Options:"
                        print "  --infra <path>    - Infrastructure directory"
                        print "  --check           - Validate without executing"
                        print "  --yes             - Skip confirmation"
                        print ""
                    }
                }
            }
            _ => {
                print $"Unknown module: ($module)"
                exit 1
            }
        }
    } else {
        dispatch_command $reordered_args $parsed_flags
    }

    if not ($env.PROVISIONING_DEBUG? | default false) { end_run "" }
}
