# Infrastructure Command Handlers
# Handles: server, taskserv, cluster, infra commands

use ../flags.nu *
# REMOVED: use ../../lib_provisioning * - causes circular import
use ../../lib_provisioning/plugins/auth.nu *

# Pre-load server module to preserve plugin context (tera, auth, kms, etc.)
# This is needed so template rendering and other plugin operations work
# in the same Nushell process
use ../../servers/create.nu *

# Helper to run module commands
# Modules are pre-loaded above to preserve plugin context
def run_module [
  args: string
  module: string
  subcommand?: string  # Optional explicit subcommand (for create operations)
  --exec
] {
  # Convert args string to list by splitting on spaces
  let args_list = if ($args | is-not-empty) {
    $args | split row " " | where {|x| ($x | str trim | is-not-empty) }
  } else {
    []
  }

  # Call the appropriate module's main function
  # Server module is pre-loaded above, so plugins (tera, auth, kms, etc.) are in scope
  match $module {
    "server" => {
      # For server: call the "main create" function directly from the already-loaded servers/create.nu
      # This preserves the tera plugin context in the same process
      # If subcommand is explicitly provided (from handle_server), use it
      # Otherwise, extract from args
      let actual_subcommand = if ($subcommand | is-not-empty) {
        $subcommand
      } else {
        let op_list = ($args | split row " " | where { |x| ($x | is-not-empty) })
        if ($op_list | length) > 0 { $op_list | first } else { "help" }
      }

      # For now, only handle "create" directly. For others, use -mod
      match $actual_subcommand {
        "create" | "c" | "list" | "l" => {
          # The servers/create.nu and servers/list.nu are loaded modules
          # Call "main create" or "main list" function directly with the arguments
          # This preserves context (env vars, plugins, etc.) in the same process
          let use_debug = if ($env.PROVISIONING_DEBUG? | default false) { "-x" } else { "" }
          let cmd_args = [-mod, "server", $actual_subcommand, ...$args_list]
          exec $"($env.PROVISIONING_NAME)" $use_debug ...$cmd_args
        }
        _ => {
          # For other operations (delete, ssh, price, status, etc.), use -mod with explicit subcommand
          let use_debug = if ($env.PROVISIONING_DEBUG? | default false) { "-x" } else { "" }
          let cmd_args = [-mod, "server", $actual_subcommand, ...$args_list]
          exec $"($env.PROVISIONING_NAME)" $use_debug ...$cmd_args
        }
      }
    }
    "taskserv" | "task" => {
      # Taskserv uses exec mode
      let use_debug = if ($env.PROVISIONING_DEBUG? | default false) { "-x" } else { "" }
      let cmd_args = [-mod, $module, ...$args_list, --notitles]
      exec $"($env.PROVISIONING_NAME)" $use_debug ...$cmd_args
    }
    "cluster" => {
      # Cluster uses exec mode
      let use_debug = if ($env.PROVISIONING_DEBUG? | default false) { "-x" } else { "" }
      let cmd_args = [-mod, $module, ...$args_list, --notitles]
      exec $"($env.PROVISIONING_NAME)" $use_debug ...$cmd_args
    }
    "infra" => {
      # Infra uses exec mode since it's a legacy module
      let use_debug = if ($env.PROVISIONING_DEBUG? | default false) { "-x" } else { "" }
      let cmd_args = [-mod, $module, ...$args_list, --notitles]
      exec $"($env.PROVISIONING_NAME)" $use_debug ...$cmd_args
    }
    _ => {
      print $"❌ Unknown module: ($module)"
      exit 1
    }
  }
}

# Show infrastructure commands help
def show_infrastructure_help [] {
  print ""
  print "INFRASTRUCTURE"
  print ""
  print "  s   server              Server lifecycle — create, delete, list, ssh, price"
  print "  t   taskserv            L2 provisioning — create, update, reset, delete, status"
  print "                            list  → components filtered to mode=taskserv"
  print "                            show  → component show"
  print "  c   component           Unified component catalog and workspace instances"
  print "  e   component (ext)       list [--mode taskserv|cluster|container] [--workspace <ws>]"
  print "                            show <name> [--workspace <ws>] [--ext]"
  print "                            status <name> --workspace <ws>"
  print "      vm                  Virtual machine management"
  print ""
  print "ORCHESTRATION"
  print ""
  print "  w   workflow            WorkflowDef lifecycle — list, show, run, validate, status"
  print "  j   job                 Orchestrator job management — list, status, monitor, submit"
  print "  b   batch               Batch operations"
  print "  o   orchestrator        Orchestrator daemon lifecycle"
  print ""
  print "Examples:"
  print "  prvng c list                                        # all components"
  print "  prvng c list --mode cluster                         # cluster-mode only"
  print "  prvng c show postgresql --workspace libre-daoshi    # full component view"
  print "  prvng c status k0s --workspace libre-daoshi         # FSM state only"
  print "  prvng w list --workspace libre-daoshi               # workspace workflows"
  print "  prvng w run deploy-services-libre-daoshi --workspace libre-daoshi"
  print "  prvng t create --infra libre-daoshi                 # L2 provisioning"
  print "  prvng s list                                        # server list"
  print "  prvng j list                                        # orchestrator jobs"
  print ""
}

# Main infrastructure command dispatcher
export def handle_infrastructure_command [
  command: string
  ops: string
  flags: record
] {
  set_debug_env $flags

  match $command {
    "create" | "c" => {
      # Handle: provisioning create server/taskserv/cluster <name> ...
      let create_ops_list = if ($ops | is-not-empty) {
        $ops | split row " " | where {|x| ($x | is-not-empty) }
      } else { [] }

      let resource_type = if (($create_ops_list | length) > 0) {
        $create_ops_list | first
      } else { "" }

      let resource_name_and_args = if (($create_ops_list | length) > 1) {
        $create_ops_list | skip 1 | str join " "
      } else { "" }


      match $resource_type {
        "server" | "s" => {
          let server_args = $"create ($resource_name_and_args)"
          handle_server $server_args $flags
        }
        "taskserv" | "task" | "t" => {
          let taskserv_args = $"create ($resource_name_and_args)"
          handle_taskserv $taskserv_args $flags
        }
        "cluster" | "cl" => {
          let cluster_args = $"create ($resource_name_and_args)"
          handle_cluster $cluster_args $flags
        }
        _ => {
          if ($resource_type | is-empty) {
            print "❌ Resource type required for create command"
          } else {
            print $"❌ Unknown resource type for create: ($resource_type)"
          }
          print ""
          print "Usage: provisioning create <resource> <name>"
          print ""
          print "Resources:"
          print "  server   (s)  - Create a server"
          print "  taskserv (t)  - Create a task service"
          print "  cluster (cl)  - Create a cluster"
          exit 1
        }
      }
    }
    "delete" | "d" => {
      # Handle: provisioning delete server/taskserv/cluster <name> ...
      let delete_ops_list = if ($ops | is-not-empty) {
        $ops | split row " " | where {|x| ($x | is-not-empty) }
      } else { [] }

      let resource_type = if (($delete_ops_list | length) > 0) {
        $delete_ops_list | first
      } else { "" }

      let resource_name_and_args = if (($delete_ops_list | length) > 1) {
        $delete_ops_list | skip 1 | str join " "
      } else { "" }

      match $resource_type {
        "server" | "s" => {
          let server_args = $"delete ($resource_name_and_args)"
          handle_server $server_args $flags
        }
        "taskserv" | "task" | "t" => {
          let taskserv_args = $"delete ($resource_name_and_args)"
          handle_taskserv $taskserv_args $flags
        }
        "cluster" | "cl" => {
          let cluster_args = $"delete ($resource_name_and_args)"
          handle_cluster $cluster_args $flags
        }
        _ => {
          print $"❌ Unknown resource type for delete: ($resource_type)"
          exit 1
        }
      }
    }
    "bootstrap" | "bstrap" => { handle_bootstrap $ops $flags }
    "fip" | "floating-ip" => { handle_fip $ops $flags }
    "server" => { handle_server $ops $flags }
    "taskserv" | "task" => { handle_taskserv $ops $flags }
    "component" | "comp" => { handle_component $ops $flags }
    "extension" | "ext"  => { handle_extension $ops $flags }
    "cluster"            => { handle_component $ops $flags }   # cluster → component (deprecated alias)
    "vm" => {
      # Import VM domain handler
      use vm_domain.nu handle_vm_command

      # Parse VM subcommand
      let vm_ops_list = if ($ops | is-not-empty) {
        $ops | split row " " | where {|x| ($x | is-not-empty) }
      } else { [] }

      let vm_command = if (($vm_ops_list | length) > 0) {
        $vm_ops_list | first
      } else { "vm" }

      let vm_remaining_ops = if (($vm_ops_list | length) > 1) {
        $vm_ops_list | skip 1 | str join " "
      } else { "" }

      handle_vm_command $vm_command $vm_remaining_ops $flags
    }
    "infra" | "infras" => {
      # Show help if no ops provided
      if ($ops | is-empty) {
        show_infrastructure_help
      } else {
        handle_infra $ops $flags
      }
    }
    "infrastructure" | "help" | "" => { show_infrastructure_help }
    _ => {
      print $"❌ Unknown command: ($command)"
      show_infrastructure_help
      exit 1
    }
  }
}

# Floating IP command handler
def handle_fip [ops: string, flags: record] {
  use ../../main_provisioning/fip.nu *

  let ops_list = if ($ops | is-not-empty) {
    $ops | split row " " | where {|x| ($x | is-not-empty) }
  } else { [] }

  let subcommand = if ($ops_list | length) > 0 { $ops_list | first } else { "" }
  let remaining  = if ($ops_list | length) > 1 { $ops_list | skip 1 } else { [] }
  let out_flag   = ($flags | get --optional output_format | default "")

  match $subcommand {
    "list" | "l" => {
      if ($out_flag | is-not-empty) { main list --out $out_flag } else { main list }
    }
    "show" | "s" => {
      let name = if ($remaining | length) > 0 { $remaining | first } else {
        error make { msg: "Usage: provisioning fip show <name>" }
      }
      if ($out_flag | is-not-empty) { main show $name --out $out_flag } else { main show $name }
    }
    "assign" => {
      let name   = if ($remaining | length) > 0 { $remaining | get 0 } else {
        error make { msg: "Usage: provisioning fip assign <name> <server>" }
      }
      let server = if ($remaining | length) > 1 { $remaining | get 1 } else {
        error make { msg: "Usage: provisioning fip assign <name> <server>" }
      }
      let yes = $flags.auto_confirm
      main assign $name $server --yes=$yes
    }
    "unassign" => {
      let name = if ($remaining | length) > 0 { $remaining | first } else {
        error make { msg: "Usage: provisioning fip unassign <name>" }
      }
      let yes = $flags.auto_confirm
      main unassign $name --yes=$yes
    }
    "protection" => {
      let name   = if ($remaining | length) > 0 { $remaining | get 0 } else {
        error make { msg: "Usage: provisioning fip protection <name> <enable|disable>" }
      }
      let action = if ($remaining | length) > 1 { $remaining | get 1 } else {
        error make { msg: "Usage: provisioning fip protection <name> <enable|disable>" }
      }
      main protection $name $action
    }
    _ => {
      print "Floating IP Management"
      print "====================="
      print ""
      print "Usage: provisioning fip <command> [args]"
      print ""
      print "Commands:"
      print "  list                          List all Floating IPs with role and protection"
      print "  show    <name>                Show detail for a specific FIP"
      print "  assign  <name> <server>       Assign FIP to a server"
      print "  unassign <name>               Release FIP from its current server"
      print "  protection <name> enable|disable   Toggle delete protection"
      print ""
      print "Examples:"
      print "  provisioning fip list"
      print "  provisioning fip show librecloud-fip-smtp"
      print "  provisioning fip assign librecloud-fip-smtp sgoyol-1"
      print "  provisioning fip unassign librecloud-fip-smtp"
      print "  provisioning fip protection librecloud-fip-smtp enable"
    }
  }
}

# Bootstrap command handler — L1 Hetzner resource provisioning
def handle_bootstrap [ops: string, flags: record] {
  use ../../main_provisioning/bootstrap.nu *
  let ws    = ($flags | get --optional workspace | default "")
  let dry   = $flags.dry_run
  if ($ws | is-not-empty) {
    main bootstrap --workspace $ws --dry-run=$dry
  } else {
    main bootstrap --dry-run=$dry
  }
}

# Server command handler
def handle_server [ops: string, flags: record] {
  # Show help if no subcommand provided
  if ($ops | is-empty) {
    print "Server Management"
    print "================="
    print ""
    print "Usage: provisioning server <command> [options]"
    print ""
    print "Commands:"
    print "  create <name>         Create a new server"
    print "  delete <name>         Delete a server"
    print "  list                  List all servers"
    print "  ssh <name>            SSH into server"
    print "  price                 Show server pricing"
    print ""
    print "Examples:"
    print "  provisioning server create web-01"
    print "  provisioning server list"
    print "  provisioning server ssh web-01"
    print ""
    return
  }

  # Authentication check for server operations (metadata-driven)
  let operation_parts = ($ops | split row " " | where {|x| ($x | is-not-empty)})
  let action = if ($operation_parts | is-empty) { "" } else { $operation_parts | first }

  # Determine operation type
  let operation_type = match $action {
    "create" | "c" => "create"
    "delete" | "d" | "remove" => "delete"
    "modify" | "update" => "modify"
    _ => "read"
  }

  # Check authentication using metadata-driven approach
  if not (is-check-mode $flags) and $operation_type != "read" {
    let operation_name = $"server ($action)"
    check-operation-auth $operation_name $operation_type $flags
  }

  # Extract the remaining arguments after the action verb (create/delete/list/etc)
  let action_and_args = if ($operation_parts | length) > 1 {
    $operation_parts | skip 1 | str join " "
  } else {
    ""
  }

  let args = build_module_args $flags $action_and_args
  # Pass the action as explicit subcommand so run_module knows which operation is being performed
  # For create operations, this preserves plugin context by calling "main create" directly
  run_module $args "server" $action --exec
}

# Task service command handler
def handle_taskserv [ops: string, flags: record] {
  # Show help if no subcommand provided
  if ($ops | is-empty) {
    print "Task Service Management"
    print "======================"
    print ""
    print "Usage: provisioning taskserv <command> [options]"
    print ""
    print "Commands:"
    print "  create <service>      Create a task service"
    print "  delete <service>      Delete a task service"
    print "  list                  List all task services"
    print "  generate <service>    Generate task service config"
    print ""
    print "Service Mesh Options:"
    print "  istio              - Full-featured service mesh with built-in ingress gateway"
    print "  linkerd            - Lightweight service mesh (requires external ingress)"
    print "  cilium             - CNI with service mesh capabilities"
    print ""
    print "Ingress Controller Options:"
    print "  nginx-ingress      - Most popular, battle-tested ingress controller"
    print "  traefik            - Modern cloud-native ingress with middleware"
    print "  contour            - Envoy-based ingress with simple configuration"
    print "  haproxy-ingress    - High-performance HAProxy-based ingress"
    print ""
    print "Examples:"
    print "  provisioning taskserv create kubernetes"
    print "  provisioning taskserv create istio"
    print "  provisioning taskserv create linkerd"
    print "  provisioning taskserv create nginx-ingress"
    print "  provisioning taskserv create traefik"
    print "  provisioning taskserv list"
    print ""
    print "Recommended Combinations:"
    print "  1. Linkerd + Nginx Ingress     - Lightweight mesh + proven ingress"
    print "  2. Istio (standalone)          - Full-featured with built-in gateway"
    print "  3. Linkerd + Traefik           - Lightweight mesh + modern ingress"
    print "  4. No mesh + Nginx Ingress     - Simple deployments"
    print ""
    return
  }

  # Authentication check for taskserv operations (metadata-driven)
  let operation_parts = ($ops | split row " ")
  let action = if ($operation_parts | is-empty) { "" } else { $operation_parts | first }

  # Determine operation type
  let operation_type = match $action {
    "create" | "c" => "create"
    "delete" | "d" | "remove" => "delete"
    "modify" | "update" => "modify"
    _ => "read"
  }

  # Check authentication using metadata-driven approach
  if not (is-check-mode $flags) and $operation_type != "read" {
    let operation_name = $"taskserv ($action)"
    check-operation-auth $operation_name $operation_type $flags
  }

  # Show ontoref FSM state from both ontology instances:
  #   1. provisioning project domain  ($PROVISIONING/.ontology/)
  #   2. active workspace domain      ($PROVISIONING_KLOUD_PATH/.ontology/)
  let ontoref_bin = (do { ^which ontoref } | complete | get stdout | str trim)
  if ($ontoref_bin | is-not-empty) {
    let prov_path   = ($env.PROVISIONING? | default "")
    let kloud_path  = ($env.PROVISIONING_KLOUD_PATH? | default "")
    let onto_roots  = (
      [$prov_path, $kloud_path]
      | where { |p| ($p | is-not-empty) and ($p | path join ".ontology" "state.ncl" | path exists) }
      | uniq
    )
    if ($onto_roots | is-not-empty) {
      print ""
      for root in $onto_roots {
        do { cd $root; ^ontoref describe state } | complete | get stdout | print
      }
    }
  }

  let args = build_module_args $flags $ops
  run_module $args "taskserv" --exec
}

# Cluster command handler
def handle_cluster [ops: string, flags: record] {
  # Show help if no subcommand provided
  if ($ops | is-empty) {
    print "Cluster Management"
    print "=================="
    print ""
    print "Usage: provisioning cluster <command> [options]"
    print ""
    print "Commands:"
    print "  deploy <layer> <cluster>  Deploy L3 platform or L4 app extensions"
    print "  create <name>             Create a new cluster"
    print "  delete <name>             Delete a cluster"
    print "  list                      List all clusters"
    print ""
    print "Examples:"
    print "  provisioning cluster deploy platform sgoyol --ws librecloud_renew"
    print "  provisioning cluster deploy apps    sgoyol --ws librecloud_renew"
    print "  provisioning cluster create k8s-prod"
    print "  provisioning cluster list"
    print ""
    return
  }

  let operation_parts = ($ops | split row " " | where { $in | is-not-empty })
  let action = if ($operation_parts | is-empty) { "" } else { $operation_parts | first }

  # Intercept deploy — routes to cluster-deploy.nu, not the old -mod cluster module
  if $action in ["deploy"] {
    use ../../main_provisioning/cluster-deploy.nu *
    let rest   = ($operation_parts | skip 1)
    let layer  = ($rest | get -o 0 | default "")
    let cluster = ($rest | get -o 1 | default "")
    if ($layer | is-empty) or ($cluster | is-empty) {
      print "❌ Usage: provisioning cluster deploy <layer> <cluster> [--ws <workspace>]"
      print "   layer: platform | apps"
      exit 1
    }
    let ws       = ($flags | get --optional workspace | default "")
    let dry      = $flags.dry_run
    let kube_cfg = ""
    let sec_file = ""
    if ($ws | is-not-empty) {
      main cluster deploy $layer $cluster --workspace $ws --dry-run=$dry --kubeconfig $kube_cfg --secrets-file $sec_file
    } else {
      main cluster deploy $layer $cluster --dry-run=$dry --kubeconfig $kube_cfg --secrets-file $sec_file
    }
    return
  }

  # Determine operation type for auth check
  let operation_type = match $action {
    "create" | "c" => "create"
    "delete" | "d" | "remove" | "destroy" => "delete"
    "modify" | "update" => "modify"
    _ => "read"
  }

  if not (is-check-mode $flags) and $operation_type != "read" {
    let operation_name = $"cluster ($action)"
    check-operation-auth $operation_name $operation_type $flags
  }

  let args = build_module_args $flags $ops
  run_module $args "cluster" --exec
}

# Infrastructure command handler
def handle_infra [ops: string, flags: record] {
  # Handle infra-specific argument building
  let infra_arg = if ($flags.infra | is-not-empty) {
    $"-i ($flags.infra)"
  } else if ($flags.infras | is-not-empty) {
    $"--infras ($flags.infras)"
  } else {
    $"-i (get_infra | path basename)"
  }

  let use_yes = if $flags.auto_confirm { "--yes" } else { "" }
  let use_check = if $flags.check_mode { "--check" } else { "" }
  let use_onsel = if ($flags.onsel | is-not-empty) {
    $"--onsel ($flags.onsel)"
  } else { "" }

  let args = $"($ops) ($infra_arg) ($use_check) ($use_onsel) ($use_yes)" | str trim
  run_module $args "infra"
}

# Price/cost command handler
export def handle_price_command [ops: string, flags: record] {
  let use_check = if $flags.check_mode { "--check " } else { "" }
  let str_infra = if ($flags.infra | is-not-empty) {
    $"--infra ($flags.infra) "
  } else { "" }
  let str_out = if ($flags.outfile | is-not-empty) {
    $"--outfile ($flags.outfile) "
  } else { "" }

  run_module $"($ops) ($str_infra) ($use_check) ($str_out)" "server" "price" --exec
}

# Create-server-task combined command handler
export def handle_create_server_task [ops: string, flags: record] {
  # Create servers first
  let server_args = build_module_args $flags $ops
  run_module $server_args "server" "create"

  # Check if server creation succeeded
  if $env.LAST_EXIT_CODE != 0 {
    _print $"🛑 Errors found in (_ansi yellow_bold)create-server(_ansi reset)"
    exit 1
  }

  # Create taskservs
  let taskserv_args = build_module_args $flags $"- ($ops)"
  run_module $taskserv_args "taskserv" "create"
}

# Component command handler — unified view for extensions/components
def handle_component [ops: string, flags: record] {
  let parts     = ($ops | split row " ")
  let action    = if ($parts | is-empty) { "" } else { $parts | first }
  let workspace = ($flags.workspace? | default ($flags.ws? | default ""))
  let mode      = ($flags.mode? | default "")

  use ../../components/mod.nu *

  match $action {
    "list" | "ls" | "l" | "" => {
      component-list $mode $workspace
    }
    "show" | "s" => {
      if ($parts | length) < 2 {
        print "❌ Error: component show requires a name"
        return
      }
      let name     = ($parts | get 1)
      let ext_only = ($flags.ext? | default false)
      component-show $name $workspace $ext_only
    }
    "status" | "st" => {
      if ($parts | length) < 2 {
        print "❌ Error: component status requires a name"
        return
      }
      let name = ($parts | get 1)
      component-status $name $workspace
    }
    "" => {
      print "Component Management"
      print "===================="
      print ""
      print "Usage: provisioning component <command> [options]"
      print ""
      print "Commands:"
      print "  list [--mode taskserv|cluster|container] [--workspace <ws>]"
      print "  show <name> [--workspace <ws>] [--ext]"
      print "  status <name> [--workspace <ws>]"
      print ""
      print "Examples:"
      print "  provisioning component list"
      print "  provisioning component list --mode cluster"
      print "  provisioning component show postgresql --workspace libre-daoshi"
      print "  provisioning component status k0s --workspace libre-daoshi"
    }
    _ => {
      print $"❌ Unknown component subcommand: ($action)"
      print "Use 'provisioning component' for help"
    }
  }
}

# Extension command handler — browses extension catalog (extensions/components/ definitions)
# e / ext  →  extension  →  shows metadata, modes, requires/provides without workspace context
def handle_extension [ops: string, flags: record] {
  let parts  = ($ops | split row " ")
  let action = if ($parts | is-empty) { "" } else { $parts | first }
  let mode   = ($flags.mode? | default "")

  use ../../components/mod.nu *

  match $action {
    "list" | "ls" | "l" | "" => {
      # Extension catalog: no workspace filter (ext_only view)
      component-list $mode ""
    }
    "show" | "s" => {
      if ($parts | length) < 2 {
        print "❌ Error: extension show requires a name"
        return
      }
      component-show ($parts | get 1) "" true   # ext_only = true
    }
    _ => {
      print $"❌ Unknown extension subcommand: ($action)"
      print "Use: prvng e list | prvng e show <name>"
    }
  }
}
