#!/usr/bin/env nu
# Info: Script to run Provisioning
# Author: JesusPerezLorenzo
# Release: 1.0.4
# Date: 6-2-2024

# CRITICAL: Must be in export-env block so it runs DURING PARSING,
# not after. This sets up NU_LIB_DIRS before modules are loaded.
export-env {
    # Initialize NU_LIB_DIRS, handling both string (from bash) and list (from Nushell)
    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
    }

    # Ensure known provisioning paths are in NU_LIB_DIRS
    let default_paths = [
        "/opt/provisioning/core/nulib"
        "/usr/local/provisioning/core/nulib"
    ]

    # Combine paths: use default paths first, then add any from current
    $env.NU_LIB_DIRS = ($default_paths | append $current_lib_dirs)
}

use std log
use lib_provisioning *
use env.nu *

#Load all main defs
use main_provisioning *

#module srv { use instances.nu * }

use servers/ssh.nu *
use servers/utils.nu *
use taskservs/utils.nu find_taskserv
use lib_provisioning/platform/bootstrap.nu *

# Helper: Reorder arguments to put flags before positional args
# This allows: provisioning workspace update --yes
# Instead of requiring: provisioning --yes workspace update
def reorder_args [args: list]: nothing -> list {
  let flags = ($args | where {|x| ($x | str starts-with "-")})
  let positionals = ($args | where {|x| not ($x | str starts-with "-")})
  ($flags | append $positionals)
}

# Help on provisioning commands
export def "main help" [
  ...args: string  # Optional category: infrastructure, orchestration, development, workspace, concepts
  --notitles       # not titles
  --out: string    # Print Output format: json, yaml, text (default)
] {
  if $notitles == null or not $notitles { show_titles }
  if ($out | is-not-empty) { $env.PROVISIONING_NO_TERMINAL = false }
  # Use only the first argument, ignore any extras (e.g., "orch status" -> "orch")
  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  # Other options, use help to get info
  --infra (-i): string     # Cloud directory
  --settings (-s): string # Settings path
  --serverpos (-p): int   # Server position in settings
  --outfile (-o): string  # Output file
  --template(-t): string  # Template path or name in PROVISION_KLOUDS_PATH
  --check (-c)     # Only check mode no servers will be created
  --yes (-y)       # confirm task
  --wait (-w)      # Wait servers to be created
  --keepstorage    # keep storage
  --select: string # Select with task as option
  --onsel: string  # On selection: e (edit) | v (view) | l (list) | t (tree)
  --infras: string # Infra list names separated by commas
  --new (-n): string    # New infrastructure name
  --debug (-x)     # Use Debug mode
  --xm             # Debug with PROVISIONING_METADATA
  --xc             # Debug for task and services locally PROVISIONING_DEBUG_CHECK
  --xr             # Debug for remote servers PROVISIONING_DEBUG_REMOTE
  --xld            # Log level with DEBUG PROVISIONING_LOG_LEVEL=debug
  --nc             # Not clean working settings
  --metadata       # Error with metadata (-xm)
  --notitles       # not tittles
  --environment: string    # Environment override (dev/test/prod)
  --dep-option: string     # Workspace dependency option: workspace-home, home-package, git-package, publish-repo
  --dep-url: string        # Dependency URL for git-package or publish-repo
  --dry-run                # Show what would be done without doing it (pack command)
  --force (-f)             # Skip confirmation prompts (pack/delete commands)
  --all                    # Process all items (pack clean command)
  --keep-latest: int       # Keep N latest versions (pack clean command)
  --activate                # Activate workspace as default (workspace commands)
  --interactive             # Interactive workspace creation wizard
  --org: string            # Organization name (for detect/complete commands)
  --apply                  # Apply changes (for complete command)
  --verbose                # Verbose output (for detect/complete/workflow commands)
  --pretty                 # Pretty-print JSON/YAML output (for detect/complete commands)
  -v               # Show version
  --version (-V)   # Show version with title
  --info                   # Show Info with title
  --about                  # Show About
  --helpinfo (-h)  # For more details use options "help" (no dashes)
  --out: string    # Print Output format: json, yaml, text (default)
  --view           # Print with highlight
  --inputfile: string  # Input format: json, yaml, text (default)
  --include_notuse # Include servers not use
]: nothing -> nothing {
  # Reorder arguments: move flags to the beginning
  # This allows: provisioning workspace update --yes
  let reordered_args = (reorder_args $args)

  # Extract flags from reordered args (for flags that came after positional 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_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" or $x == "-w"})

  # Combine with already-parsed flags (take OR - if either parsed or in args, then true)
  let final_yes = ($yes or $has_yes_in_args)
  let final_check = ($check or $has_check_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)

  # Initialize provisioning system
  provisioning_init $helpinfo "" $reordered_args

  # Parse all flags into normalized structure
  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, 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
  })

  # Handle version, info, about flags
  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 (get_about_info) ; exit }

  # Bootstrap platform services (only if running actual commands, not help/info)
  # Skip bootstrap for help-like, guide, setup, discovery/info, and utility commands
  let is_help_command = (
    ($reordered_args | length) == 0 or
    ($reordered_args | get 0) in [
      # Help and guides
      "help", "-h", "--help",
      "sc", "shortcuts", "quickstart", "quick",
      "from-scratch", "scratch",
      "customize", "custom",
      "guide", "guides", "howto",
      # Setup
      "setup", "st",
      # Discovery and module commands
      "mod", "module", "discover", "disc",
      "dt", "dp", "dc",
      "discover-taskservs", "disc-t",
      "discover-providers", "disc-p",
      "discover-clusters", "disc-c",
      # Development info
      "lyr", "layer", "version", "pack",
      # Utilities and info
      "nuinfo", "env", "allenv",
      "validate", "val", "show", "config-template",
      "cache",
      "list", "l", "ls",
      "plugin", "plugins",
      "qr", "ssh", "sops",
      "providers",
      "status", "health"
    ]
  )

  if not $is_help_command {
    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
    }
  }

  # For info/discovery/utility commands, dispatch directly without going through workspace enforcement
  # These commands don't need workspace context
  if (($reordered_args | length) > 0) and (($reordered_args | get 0) in [
    # Guide commands
    "guide", "guides", "sc", "howto", "shortcuts", "quickstart", "quick",
    "from-scratch", "scratch", "customize", "custom",
    # Discovery/info commands
    "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",
    # Utility commands (these are informational)
    "plugin", "plugins",
    "qr", "nuinfo",
    "status", "health"
  ]) {
    dispatch_command $reordered_args $parsed_flags
    if not $env.PROVISIONING_DEBUG { end_run "" }
    return
  }

  # Dispatch command to appropriate handler
  dispatch_command $reordered_args $parsed_flags

  # End run if not in debug mode
  if not ($env.PROVISIONING_DEBUG? | default false) { end_run "" }
}

export def get_show_info [
  ops: list
  curr_settings: record
  out: string
]: nothing -> record  {
  match ($ops | get -o 0 | default "") {
    "set" |"setting" | "settings" => $curr_settings,
    "def" | "defs" |"defsetting" | "defsettings" => {
      let src = ($curr_settings | get -o src | default "");
      let src_path = ($curr_settings | get -o src_path | default "");
      let def_settings = if ($src_path | path join $src | path exists) {
        open -r ($src_path | path join $src)
      } else { "" }
      let main_path = ($env.PROVISIONING | path join "kcl" | path join "settings.k")
      let src_main_settings = if ($main_path | path exists) {
        open -r $main_path
      } else { "" }
      {
        def: $src,
        def_path: $src_path,
        infra: ($curr_settings | get -o infra | default ""),
        infra_path: ($curr_settings | get -o infra_path | default ""),
        def_settings: $def_settings,
        main_path: $main_path,
        main_settings: $src_main_settings,
      }
    },
    "server" |"servers" | "s" => {
      let servers = ($curr_settings | get -o data | get -o servers |  default {})
      let item = ($ops | get -o 1 | default "")
      if ($item | is-empty) {
        $servers
      } else {
        let server = (find_server $item $servers ($out | default ""))
        let def_target = ($ops | get -o 2 | default "")
        match $def_target {
          "t" | "task" | "taskserv" => {
            let task = ($ops | get -o 3 | default "")
            (find_taskserv $curr_settings $server $task ($out | default ""))
          },
          _ => $server,
        }
      }
    },
    "serverdefs" |"serversdefs" | "sd" => {
      (find_serversdefs $curr_settings)
    },
    "provgendefs" |"provgendef" | "pgd" => {
      (find_provgendefs)
    },
    "taskservs" |"taskservs" | "ts" => {
      #(list_taskservs $curr_settings)
      let list_taskservs = (taskservs_list)
      if ($list_taskservs | length) == 0 {
          _print $"🛑 no items found for (_ansi cyan)taskservs list(_ansi reset)"
          return
      }
      $list_taskservs
    },
    "taskservsgendefs" |"taskservsgendef" | "tsd" => {
      let defs_path = ($env.PROVISIONING_TASKSERVS_PATH | path join $env.PROVISIONING_GENERATE_DIRPATH | path join $env.PROVISIONING_GENERATE_DEFSFILE)
      if ($defs_path | path exists) {
        open $defs_path
      }
    },
    "cost" | "costs" | "c" | "price" | "prices" | "p" => {
      (servers_walk_by_costs $curr_settings "" false false "stdout")
    },
    "alldata" => ($curr_settings | get -o data | default {}
       | merge { costs: (servers_walk_by_costs $curr_settings "" false false "stdout") }
    ),
    "data" | _ => {
      if ($out | is-not-empty) {
        ($curr_settings | get -o data | default {})
      } else {
          print ($" (_ansi cyan_bold)($curr_settings | get -o data | get -o main_name | default '')"
            + $"(_ansi reset): (_ansi yellow_bold)($curr_settings | get -o data | get -o main_title | default '') (_ansi reset)"
          )
          print ($curr_settings | get -o data | default {} | merge { servers: ''})
          ($curr_settings | get -o data | default {} | get -o servers | each {|item|
              print $"\n server:  (_ansi cyan_bold)($item.hostname | default '') (_ansi reset)"
              print $item
          })
          ""
      }
    },
  }
}
