
#use utils * 
#use defs * 
use lib_provisioning *

# - > Query  infrastructure and services
export def "main generate" [
  #hostname?: string      # Server hostname in settings
  ...args                 # Args for create command 
  --infra (-i): string    # Infra path 
  --settings (-s): string # Settings path 
  --serverpos (-p): int   # Server position in settings 
  --check (-c)            # Only check mode no servers will be created 
  --wait (-w)             # Wait servers to be created 
  --outfile: string      # Optional output format: json | yaml | csv | text | md | nuon 
  --find (-f): string     # Optional generate find a value (empty if no value found)
  --cols (-l): string     # Optional generate columns list separated with comma 
  --template(-t): string  # Template path or name in PROVISION_KLOUDS_PATH
  --ips                   # Optional generate get IPS only for target "servers-info" 
  --prov: string          # Optional provider name to filter generate
  --debug (-x)            # Use Debug mode
  --xm             # Debug with PROVISIONING_METADATA 
  --xc             # Debuc 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 
  --metadata       # Error with metadata (-xm)
  --notitles       # not tittles
  --helpinfo (-h)  # For more details use options "help" (no dashes) 
  --out: string    # Print Output format: json, yaml, text (default)
]: nothing -> nothing {
  if ($out | is-not-empty) {
    $env.PROVISIONING_OUT = $out 
    $env.PROVISIONING_NO_TERMINAL = true
  }
  if $helpinfo {
    _print (provisioning_generate_options)
    if not $env.PROVISIONING_DEBUG { end_run "" } 
    exit
  }
  parse_help_command "generate" --end
  if $debug { $env.PROVISIONING_DEBUG = true } 
  #use defs [ load_settings ]
  let curr_settings = if $infra != null { 
    if $settings != null { 
      (load_settings --infra $infra --settings $settings)
    } else {  
      (load_settings --infra $infra)
    }
  } else {
    if $settings != null { 
      (load_settings --settings $settings)
    } else {  
      (load_settings false true)
    }
  }
  #let cmd_template = if ($template | is-empty )  { 
  #  ($args | get -o 0 | default "")
  #} else { $template }
  #let str_out = if $outfile == null { "none" } else { $outfile }
  let str_out = if $out == null { "" } else { $out }
  let str_cols = if $cols == null { "" } else { $cols }
  let str_find = if $find == null { "" } else { $find }
  let str_template = if $template == null { "" } else { $template }
  let cmd_target = if ($args | length) > 0 { ($args| get 0) } else { "" } 
  $env.PROVISIONING_MODULE = "generate"
  let ops = $"($env.PROVISIONING_ARGS? | default "") " | str replace $env.PROVISIONING_MODULE "" | str replace $" ($cmd_target) " "" | str trim
  #generate_provision $args $curr_settings $str_template
  match $cmd_target {
    "new" | "n"  => {
      let args_list = if ($args | length) > 0 { 
        ($args| skip 1) 
      } else { [] }
      generate_provision $args_list $curr_settings $str_template
    },
    "server" | "servers" => { 
      #use utils/format.nu datalist_to_format
      _print (datalist_to_format $str_out 
        (mw_generate_servers $curr_settings $str_find $cols --prov $prov --serverpos $serverpos)
      )
    },
    "server-status" | "servers-status" |  "server-info" | "servers-info" => { 
      let list_cols = if ($cmd_target | str contains "status") {
        if ($str_cols | str contains "state") { $str_cols } else { $str_cols + ",state" }
      } else {
        $str_cols
      }
      # not use $str_cols to filter previous $ips selection  
      (out_data_generate_info
        $curr_settings
        (mw_servers_info $curr_settings $str_find --prov $prov --serverpos $serverpos)
        #(mw_servers_info $curr_settings $find $cols --prov $prov --serverpos $serverpos)
        $list_cols
        $str_out
        $ips 
      )
    },
    "servers-def" | "server-def" => {
      let data = if $str_find != "" { ($curr_settings.data.servers | find $find) } else { $curr_settings.data.servers}
      (out_data_generate_info
        $curr_settings
        $data
        $str_cols
        $str_out
        false
      )
    },
    "def" | "defs" => {
      let data = if $str_find != "" { ($curr_settings.data | find $find) } else { $curr_settings.data}
      (out_data_generate_info
        $curr_settings
        [ $data ]
        $str_cols
        $str_out
        false
      )
    }
    _ => {
      (throw-error $"🛑 ($env.PROVISIONING_NAME) generate " $"Invalid option (_ansi red)($cmd_target)(_ansi reset)"
        $"($env.PROVISIONING_NAME) generate --target ($cmd_target)" --span (metadata $cmd_target).span
      )
    }
  }
  cleanup ($curr_settings | get -o wk_path | default "")
  if $outfile == null { end_run "generate" }
}

export def generate_new_infra [
  args: list
  template: string
]: nothing -> record {
  let infra_path = ($args | get -o 0 | default "")
  let infra_name = ($infra_path | path basename)
  let target_path =  if ($infra_path | str contains "/") {
    $infra_path 
  } else if ($env.PROVISIONING_INFRA_PATH | path exists) and not ($env.PROVISIONING_INFRA_PATH | path join $infra_path | path exists) {
      ($env.PROVISIONING_INFRA_PATH | path join $infra_path)
  } else {
      $infra_path
  }
  if ($target_path | path exists) { 
    _print $"🛑 Path (_ansi yellow_bold)($target_path)(_ansi reset) already exits"
    return
  }  
  ^mkdir -p $target_path
  _print $"(_ansi green)($infra_name)(_ansi reset) created in (_ansi green)($target_path | path dirname)(_ansi reset)"
  _print $"(_ansi green)($infra_name)(_ansi reset) ... "
  let template_path = if ($template | is-empty) {
    ($env.PROVISIONING | path join $env.PROVISIONING_GENERATE_DIRPATH | path join "default")
  } else if ($template | str contains "/") and ($template | path exists) {
    $template
  } else if ($env.PROVISIONING_INFRA_PATH | path join $template | path exists) {
    ($env.PROVISIONING_INFRA_PATH | path join $template) 
  }
  let new_created = if not ($target_path | path join "settings.k" | path exists) {
    ^cp -pr ...(glob ($template_path | path join "*")) ($target_path)
    _print $"copy (_ansi green)($template)(_ansi reset) to (_ansi green)($infra_name)(_ansi reset)"
    true
  } else {
    false
  }
  { path: $target_path, name: $infra_name, created: $new_created }
}
export def generate_provision [
  args: list
  settings: record
  template: string
]: nothing -> nothing {
  let generated_infra = if ($settings | is-empty) { 
    if ($args | is-empty) {
      (throw-error $"🛑 ($env.PROVISIONING_NAME) generate " $"Invalid option (_ansi red)no settings and path found(_ansi reset)"
        $"($env.PROVISIONING_NAME) generate " --span (metadata $settings).span
      )
    } else {
      generate_new_infra $args $template
    }
  }
  if ($generated_infra | is-empty) { 
    (throw-error $"🛑 ($env.PROVISIONING_NAME) generate " $"Invalid option (_ansi red)no settings and path found(_ansi reset)"
      $"($env.PROVISIONING_NAME) generate " --span (metadata $settings).span
    )
  }
  generate_data_def $env.PROVISIONING $generated_infra.name $generated_infra.path $generated_infra.created
}
def out_data_generate_info [
  settings: record
  data: list
  cols: string
  outfile:  string
  ips: bool
]: nothing -> nothing {
  if ($data | get -o 0 | is-empty) {
    if $env.PROVISIONING_DEBUG { print $"🛑 ($env.PROVISIONING_NAME) generate (_ansi red)no data found(_ansi reset)" } 
    _print ""
    return 
  }
  let sel_data = if ($cols | is-not-empty) { 
    $data | select -o ($cols | split row ",")
  } else {
    $data
  }
  #use ../../../providers/prov_lib/middleware.nu mw_servers_ips
  #use utils/format.nu datalist_to_format
  print (datalist_to_format $outfile $sel_data)
  # let data_ips = (($data).ip_addresses? | flatten | find "public")
  if $ips { 
    let ips_result = (mw_servers_ips $settings $data) 
    print $ips_result
  }
}
