# Provider middleware now available through lib_provisioning
use lib_provisioning *
use ssh.nu *
use ../lib_provisioning/utils/ssh.nu ssh_cmd
use ../lib_provisioning/utils/settings.nu get_file_format
use ../lib_provisioning/secrets/lib.nu encrypt_secret

export def on_server [
    settings: record   # Settings record 
    check: bool        # Only check mode no servers will be created 
    wait: bool         # Wait for creation
    outfile?: string   # Out file for creation
    hostname?: string  # Server hostname in settings
    serverpos?: int    # Server position in settings 
]: nothing -> list {
    # _check_settings 
    let match_hostname = if $hostname != null {  
        $hostname  
    } else if $serverpos != null  { 
        let total = $settings.data.servers | length
        let pos = if $serverpos == 0 { 
            _print $"Use number form 1 to ($total)"
            $serverpos
        } else if $serverpos <= $total { 
            $serverpos - 1
        } else { 
            (throw-error $"🛑 server pos" $"($serverpos) from ($total) servers" 
                "on_create" --span (metadata $serverpos).span)
            exit 1
        }
        ($settings.data.servers | get $pos).hostname
    }
    if $check { 
        $settings.data.servers | enumerate | each { |it| 
            if $match_hostname == null or $it.item.hostname == $match_hostname  { 
                on_create_server $it.item $it.index true $outfile
            } 
        }
    } else { 
        $settings.data.servers | enumerate | par-each { |it| 
            if $match_hostname == null or $it.item.hostname == $match_hostname  { 
                on_create_server $it.item $it.index false $outfile
            } 
        }
    }
}

export def wait_for_server [
    server_pos: int
    server: record
    settings: record
    ip: string
]: nothing -> bool {
    if $ip == "" { return false } 
    mut num = 0
    let liveness_port = (if $server.liveness_port? != null { $server.liveness_port }  else { 22 } | into int)
    let val_timeout = if $server.running_timeout? != null { $server.running_timeout } else { 0 }  
    let wait = if $server.running_wait? != null { $server.running_wait } else { 10 }
    let wait_duration = ($"($wait)sec"| into duration)
    _print ( 
        $"wait for server (_ansi blue_bold)($server.hostname)(_ansi reset) state " + 
        $"(_ansi yellow_bold)started(_ansi reset) (_ansi default_dimmed)until ($val_timeout)secs check every ($wait)sec(_ansi reset)"
    )
    while true {
        let status = (mw_server_is_running $server false)
        #let res = (run-external --redirect-combine "nc" "-zv" "-w" 1 $ip $liveness_port | complete)
        #if $res.exit_code == 0 {
        if $status and (port_scan $ip $server.liveness_port 1) and (ssh_cmd $settings $server false "ls" $ip) {
            _print $"done in ($num)secs "
            break
        } else if $val_timeout > 0 and $num > $val_timeout {  
        _print ($"\n🛑 (_ansi red)Timeout(_ansi reset) ($val_timeout) (_ansi blue)($server.hostname)(_ansi reset)" + 
            $"(_ansi blue_bold)($ip)(_ansi reset) at ($liveness_port) (_ansi red_bold)failed(_ansi reset) "
        )
        #print $"\n($res.stdout)"
        return false
        } else {
        $num = $num + $wait
        #print -n $"($nupm) "
        print -n $"(_ansi blue_bold) 🌥 (_ansi reset)"
        sleep $wait_duration
        }
    }
    _print (
        $"(_ansi blue)($server.hostname)(_ansi reset) at (_ansi blue_bold)($ip)(_ansi reset) " + 
        $"port ($liveness_port)  (_ansi green_bold)ready(_ansi reset) "
    )
    true
}
export def on_server_template [
    server_template: string
    server: record
    index: int
    check: bool
    only_make: bool
    wait: bool
    settings: record
    outfile?: string
]: nothing -> bool {
    if $server.provider == local { return true }
    if not ( $server_template | path exists )  { 
        _print $"($server_template) not found for ($server.hostname) [($index)]"
        return false
    }
    let suffix = if ($server_template | str contains "storage") {
        "storage"
    } else { 
        "server"
    }
    #use utils/templates.nu run_from_template
    #mut create_result = false
    let duration = timeit { 
        let wk_file = $"($settings.wk_path)/($server.hostname)_($suffix)_cmd" 
        let wk_vars = $"($settings.wk_path)/($server.hostname)_($suffix)_vars.($env.PROVISIONING_WK_FORMAT)" 
        let run_file = $"($settings.wk_path)/on_($server.hostname)_($suffix)_run.sh" 
        rm --force $wk_file $wk_vars $run_file
        let data_settings = if $suffix == "storage" { 
            ($settings.data | merge { wk_file: $wk_file, now: $env.NOW, server_pos: $index, storage_pos: 0, provisioning_vers: ($env.PROVISIONING_VERS? | str replace "null" ""),
            wait: $wait, server: $server })
        } else { 
            ($settings.data | merge { wk_file: $wk_file, now: $env.NOW, serverpos: $index,  provisioning_vers: ($env.PROVISIONING_VERS? | str replace "null" ""),
            wait: $wait, provider: ($settings.providers | where {|it| $it.provider == $server.provider} | get -o 0  | get -o settings | default {}),
            server: $server }) 
        }
        if $env.PROVISIONING_WK_FORMAT == "json" {
            $data_settings |  to json  | save --force $wk_vars 
        } else {
            $data_settings | to yaml  | save --force $wk_vars 
        }
        let res = if $only_make and $check { 
            (run_from_template $server_template $wk_vars $run_file $outfile --only_make --check_mode)
        } else if $only_make { 
            (run_from_template $server_template $wk_vars $run_file $outfile --only_make)
        } else if $check { 
            (run_from_template $server_template $wk_vars $run_file $outfile --check_mode)
        } else { 
            (run_from_template $server_template $wk_vars $run_file $outfile)
        }
        if $res { 
            if  $env.PROVISIONING_DEBUG == false { rm --force $wk_file $wk_vars $run_file } 
            _print $"(_ansi green_bold)($server.hostname)(_ansi reset) (_ansi green)successfully(_ansi reset)" 
        } else {
            _print $"(_ansi red)Failed(_ansi reset) (_ansi green_bold)($server.hostname)(_ansi reset)" 
        }
    } 
    let text_duration = if not $check { $"in (_ansi blue_bold)($duration)(_ansi reset)" } else { "" }
    _print $"Done run template (_ansi blue_italic)($server_template | path basename | str replace ".j2" "" )(_ansi reset) for (_ansi green_bold)($server.hostname)(_ansi reset) ($text_duration)"
    true # $create_result
}
export def servers_selector [
    settings: record
    ip_type: string
    is_for_task: bool
]: nothing -> string {
    if ($env | get -o PROVISIONING_OUT | default "" | is-not-empty) or $env.PROVISIONING_NO_TERMINAL { return ""}
    mut servers_pick_lists = []
    if not $env.PROVISIONING_DEBUG_CHECK { 
        #use ssh.nu *
        for server in $settings.data.servers  {
            let ip = (mw_get_ip $settings $server $ip_type false | default "")
            if $ip == "" { 
                _print $"🛑 No IP ($ip_type) found for (_ansi green_bold)($server.hostname)(_ansi reset) " 
                continue
            }
            let ssh_id = (server_ssh_id $server)
            let ssh_addr = (server_ssh_addr $settings $server)
            $servers_pick_lists = ($servers_pick_lists | append { name: $server.hostname, 
                id: $ssh_id, addr: $ssh_addr
            })
        }
    }
    let msg_sel = if $is_for_task { 
        "Select one server" 
    } else { 
        "To connect to a server select one"
    }
    if ($servers_pick_lists | length) == 0 { return "" }
    let selection = if ($servers_pick_lists | length) > 1 { 
        _print $"(_ansi default_dimmed)($msg_sel) \(use arrows and press [enter] or [esc] to cancel\):(_ansi reset)"
        ($servers_pick_lists | each {|it| _print $"($it.name) -> ($it.addr)"})
        let pos_select = ($servers_pick_lists | each {|it| $"($it.name) -> ($it.addr)"} |input list --index)
        if $pos_select == null { return null }
        let selection = ($servers_pick_lists | get -o $pos_select)
        if not $is_for_task  {
            _print $"\nFor (_ansi green_bold)($selection.name)(_ansi reset) server use:"
        }
        $selection
    } else { 
        let selection = ($servers_pick_lists | get -o 0)
        if not $is_for_task  {
            _print $"\n(_ansi default_dimmed)To connect to server (_ansi reset)(_ansi green_bold)($selection.name)(_ansi reset) use:"
        }
        $selection
    } 
    if not $is_for_task  {
        let id = ($selection | get -o id | default "")
        if ($id | is-not-empty) {
            show_clip_to $"ssh -i($id) ($selection.addr)" true
        }
    }
    $selection
}
def add_item_price [
    server: record
    already_created: bool
    resource: string 
    item: string
    price: record
    host_color: string
]: nothing -> record {
    let str_price_monthly = if $price.month < 10 { $" ($price.month)" } else { $"($price.month)" }
    let price_monthly = if ($str_price_monthly | str contains ".") { $str_price_monthly } else { $"($str_price_monthly).0"}
    if ($env.PROVISIONING_OUT | is-empty)  {
        {
            host: $"(_ansi $host_color)($server.hostname)(_ansi reset)",
            item: $"(_ansi default_bold)($item)(_ansi reset)",
            resource: $"(_ansi blue_bold)($resource)(_ansi reset)", 
            prov: $"(_ansi default_bold)($server.provider)(_ansi reset)",
            zone: $"(_ansi default_bold)($server.zone)(_ansi reset)",
            unit: ($price.unit_info | default "")
            hour: $"(_ansi default_bold)  ($price.hour | fill -a left -c '0' -w 7 | str replace '.' ',') €  (_ansi reset)",
            day: $"(_ansi default_bold)  ($price.day | math round -p 4 | fill -a left -c '0' -w 7 | str replace '.' ',') €  (_ansi reset)",
            month: $"(_ansi default_bold)  ($price_monthly | fill -a left -c '0' -w 7 | str replace '.' ',' | str replace  ',0000' '') € (_ansi reset)",
            created: $already_created,
        }
    } else { 
        {
            host: $server.hostname,
            item: $item,
            resource: $resource, 
            prov: $server.provider,
            zone: $server.zone,
            unit: ($price.unit_info | default ""),
            hour: $"($price.hour | fill -a left -c '0' -w 7 | str replace '.' ',') €",
            day: $"($price.day | math round -p 4 | fill -a left -c '0' -w 7 | str replace '.' ',') €",
            month: $"($price_monthly | fill -a left -c '0' -w 7 | str replace '.' ',' | str replace  ',0000' '') €",
            created: $already_created,
        }
    }
}
export def servers_walk_by_costs [
    settings: record   # Settings record 
    match_hostname: string 
    check: bool        # Only check mode no servers will be created 
    return_no_exists: bool
    outfile?: string
]: nothing -> nothing {
    if $outfile != null  { $env.PROVISIONING_NO_TERMINAL = true } 
    if $outfile == null {
        _print $"\n (_ansi cyan)($settings.data | get -o main_title | default "")(_ansi reset) prices ($outfile)"
    }
    mut infra_servers = {}
    mut total_month = 0
    mut total_hour = 0
    mut total_day = 0
    mut table_items = []
    let total_color  = { fg: '#ffff00' bg: '#0000ff' attr: b }

    for server in $settings.data.servers {  
        if $match_hostname != null and $match_hostname != "" and $server.hostname != $match_hostname  { continue }
        if ($infra_servers | is-empty) or ($infra_servers | get -o $server.provider 
            | if ($in | describe | str starts-with "record") { [$in] } else { $in } | default [] 
            | where {|it| $it.zone? != null and $it.zone == $server.zone and $it.plan? == null and $it.plan == $server.plan} | length) == 0 { 
            $infra_servers = ($infra_servers | merge { $server.provider: (mw_load_infra_servers_info $settings $server false)} ) 
        }
        if ($infra_servers | get -o $server.provider
            | if ($in | describe | str starts-with "record") { [$in] } else { $in } | default [] 
            | where {|it| $it.zone? != null and $it.zone == $server.zone and $it.store? != null and ($it.store | is-not-empty) } | length) == 0 { 
            let store_data = (mw_load_infra_storages_info $settings $server false)
            if ($store_data |is-not-empty ) { 
                $infra_servers = ($infra_servers | merge { $server.provider: (mw_load_infra_storages_info $settings $server false)} ) 
            }
        }
        if ($infra_servers | is-empty) or ($infra_servers | get -o $server.provider | is-empty) { continue }
        let item = { item: (mw_get_infra_item $server $settings $infra_servers false), target: "server" } 
        if ($item | get -o item | is-empty) { continue }
        #if  $env.PROVISIONING_DEBUG_CHECK { _print ($item | table -e)}

        let already_created = (mw_server_exists $server false) 
        let host_color = if $already_created { "green_bold" } else { "red" }

        let price_hour = (mw_get_infra_price $server $item "hour" false)
        let price = {
            hour: $price_hour,
            month: ((mw_get_infra_price $server $item  "month" false) | math round -p 4)
            day : (($price_hour * 24) | math round -p 4)
            unit_info: (mw_get_infra_price $server $item  "unit" false)
        }
        let str_server_plan = if ($server.reqplan? != null ) {
            $"($server.reqplan.cores | default 1)xCPU-(($server.reqplan.memory | default 1024) / 1024)GB ($server.plan)"
        } else { $server.plan }
        if ($price.hour > 0 or $price.month > 0) { 
            $total_month += $price.month 
            $total_hour += $price.hour
            $total_day += ($price.day)
            $table_items = ($table_items | append (add_item_price $server $already_created $str_server_plan "server" $price $host_color))
        }
        for it in ($server | get -o storages | default [] | enumerate) {
            let storage = $it.item
            let storage_item = { item: (mw_get_infra_storage $server $settings $infra_servers false), target: "storage", src: $it } 
            if ($storage_item | get -o item | is-empty) { continue }
            #if  $env.PROVISIONING_DEBUG_CHECK { _print ($storage_item | table -e)}
            let storage_size = if ($storage | get -o parts | length) > 0 { 
                ($storage | get -o parts | each {|part| $part | get -o size | default 0} | math sum)
            } else { 
                ($storage | get -o size | default 0) 
            }
            if $storage_size > 0 { 
                let storage_targets = if ($storage | get -o parts | length) > 0 { 
                    ($storage | get -o parts | each {|part| $part | get -o mount_path | default ""} | str join " - ")
                } else { 
                    ($storage | get -o mount_path | default "") 
                }
                let store_price_month = ((mw_get_infra_price $server $storage_item  "month" false) * $storage_size | math round -p 4 )
                let store_price_day = ((mw_get_infra_price $server $storage_item  "day" false) * $storage_size | math round -p 4 )
                let store_price_hour = ((mw_get_infra_price $server $storage_item  "hour" false) * $storage_size | math round -p 4 )
                let store_price = {
                    month : $store_price_month,
                    day : $store_price_day,
                    hour : $store_price_hour
                    unit_info: (mw_get_infra_price $server $storage_item  "unit" false)
                }
                if ($store_price.hour > 0 or $store_price.month > 0) { 
                    $total_month += $store_price.month 
                    $total_hour += $store_price.hour
                    $total_day += ($store_price.day)
                    $table_items = ($table_items | append (add_item_price $server $already_created $"($storage_size) Gb ($storage_targets)" "store" $store_price $host_color))
                }
            }
        }
        if not $check {
            let already_created = (mw_server_exists $server false)
            if not ($already_created) {
                if $return_no_exists {
                    return { status: false, error: $"($server.hostname) not created" }
                #   _print $"(_ansi red_bold)($server.hostname)(_ansi reset) not created"
                }
            }
        }
        #{ status: true, error: "" }
    }
    if ($env.PROVISIONING_OUT | is-empty)  {
        $table_items = ($table_items | append { host: "", item: "",  resource: "", prov: "", zone: "", unit: "", month: "", day: "", hour: "", created: ""})
    }
    let str_total_month = if ($"($total_month)" | str contains ".") { 
        ($total_month | math round -p 4 | fill -a left -c '0' -w 7 | str replace  ',0000' '')
    } else { 
        ($"($total_month).0" | fill -a left -c '0' -w 7 | str replace  ',0000' '')
    }
    if ($env.PROVISIONING_OUT | is-empty)  {
        $table_items = ($table_items | append {
            host: $"(_ansi --escape $total_color)   TOTAL  (_ansi reset)", 
            item: $"(_ansi default_bold) (_ansi reset)",
            resource: $"(_ansi default_bold) (_ansi reset)",
            prov: $"(_ansi default_bold) (_ansi reset)",
            zone: $"(_ansi default_bold) (_ansi reset)",
            unit: $"(_ansi default_bold) (_ansi reset)",
            hour: $"(_ansi --escape $total_color)  ($total_hour | math round -p 4 | fill -a left -c '0' -w 7 | str replace '.' ',') €  (_ansi reset)",
            day:  $"(_ansi --escape $total_color)  ($total_day | math round -p 4 | fill -a left -c '0' -w 7 | str replace '.' ',') €  (_ansi reset)",
            month:$"(_ansi --escape $total_color)  ($str_total_month | str replace '.' ',' | str replace  ',0000' '') €  (_ansi reset)"
            created: $"(_ansi default_bold) (_ansi reset)",
        })
    } else { 
        $table_items = ($table_items | append {
            host: "TOTAL", 
            item: "",
            resource: "",
            prov: "",
            zone: "",
            unit: "",
            hour: $"(_ansi --escape $total_color)($total_hour | math round -p 4 | fill -a left -c '0' -w 7 | str replace '.' ',') €(_ansi reset)",
            day:  $"(_ansi --escape $total_color)($total_day | math round -p 4 | fill -a left -c '0' -w 7 | str replace '.' ',') €(_ansi reset)",
            month:$"(_ansi --escape $total_color)($str_total_month | str replace '.' ',' | str replace  ',0000' '') €(_ansi reset)"
            created: false,
        })
    }
    if $outfile != null  { 
        if ($outfile == "stdout") {
            return  $table_items 
        } else if ($outfile | str ends-with ".json") {
            $table_items | to json | save --force $outfile
        } else if ($outfile | str ends-with ".yaml") {
            $table_items | to yaml | save --force $outfile
        } else if ($outfile | str ends-with ".csv") {
            $table_items | to csv | save --force $outfile
        } else if ($outfile | str ends-with ".table") {
            $table_items | table -e | save --force $outfile
        } else { 
            $table_items | to text | save --force $outfile
        }
        $env.PROVISIONING_NO_TERMINAL = false
        _print  $"Prices saved in (_ansi cyan_bold)($outfile)(_ansi reset) "
    } else { 
        $env.PROVISIONING_NO_TERMINAL = false
        match $env.PROVISIONING_OUT  {
            "json" => { _print ($table_items | to json) "json" "result" "table" },
            "yaml" => { _print ($table_items | to yaml) "yaml" "result" "table" },
            _ => { _print ($table_items | table -i false) },
        }
    }
}
export def wait_for_servers [
    settings: record 
    check: bool
    ip_type: string = "public"
]: nothing -> bool {
    mut server_pos = 0
    mut has_errors = false
    for srvr in $settings.data.servers  {
        $server_pos += 1
        let ip = if $env.PROVISIONING_DEBUG_CHECK or $check { 
            "127.0.0.1"
        } else { 
            let curr_ip = (mw_get_ip $settings $srvr $ip_type false | default "")
            if $curr_ip == "" { 
                _print $"🛑 No IP ($ip_type) found for (_ansi green_bold)($srvr.hostname)(_ansi reset) ($server_pos) " 
                $has_errors = true
            continue
            }
            #use utils.nu wait_for_server 
            if not (wait_for_server $server_pos $srvr $settings $curr_ip) { 
                _print $"🛑 server ($srvr.hostname) ($curr_ip)  (_ansi red_bold)not in running state(_ansi reset)" 
                $has_errors = true
                continue  
            }
        }
        _print $"on (_ansi green_bold)($srvr.hostname)(_ansi reset) ($ip)"
    }
    $has_errors
}
export def provider_data_cache [
    settings: record
    --outfile (-o): string  # Output file
]: nothing -> nothing {
    mut cache_already_loaded = []
    for server in ($settings.data.servers? | default []) { 
        _print $"server (_ansi green)($server.hostname)(_ansi reset) on (_ansi blue)($server.provider)(_ansi reset)"  
        if ($cache_already_loaded | where {|it| $it == $server.provider} |length) > 0 { continue } else { $cache_already_loaded = ($cache_already_loaded | append $server.provider)}
        let provider_path = (get_provider_data_path $settings $server) 
        #use ../lib_provisioning/utils/settings.nu load_provider_env
        let data = (load_provider_env $settings $server $provider_path)
        if ($data | is-empty) {   
            _print $"❗server ($server.hostname) no data in cache path found ($provider_path | path basename)"  
            exit
        } 
        let outfile_path = if ($outfile | is-not-empty) { ($outfile | path dirname | path join $"($server.provider)_($outfile | path basename)") } else { "" }
        if ($outfile_path | is-not-empty ) { 
            let out_extension = (get_file_format $outfile_path)
            if $out_extension == "json" {
                ($data | to json | save --force $outfile_path)
            } else {
                ($data | to yaml | save --force $outfile_path)
            }
            if ($outfile_path | path exists)  { 
            _print $"✅ (_ansi green_bold)($server.provider)(_ansi reset) (_ansi cyan_bold)cache settings(_ansi reset) saved in (_ansi yellow_bold)($outfile_path)(_ansi reset)"
            _print $"To create a (_ansi purple)kcl(_ansi reset) for (_ansi cyan)defs(_ansi reset) file use:"
            let k_file_path = $"($outfile_path | str replace $'.($out_extension)' '').k"
            ^kcl import ($outfile_path) -o ($k_file_path) --force 
            ^sed -i '1,4d;s/^{/_data = {/' $k_file_path 
            '{ main = _data.main, priv = _data.priv }' | tee {save -a $k_file_path} | ignore 
            let res = ( ^kcl $k_file_path | complete)
            if $res.exit_code == 0 { 
                $res.stdout | save $"($k_file_path).yaml" --force
                ^kcl import $"($k_file_path).yaml" -o ($k_file_path) --force 
                ^sed -i '1,4d;s/^{/_data = {/' $k_file_path 
                let content = (open $k_file_path --raw)
                let comment = $"# ($server.provider)" + " environment settings, if not set will be autogenerated in 'provider_path' (data/" + $server.provider + "_cache.yaml)"
                let from_scratch = (mw_start_cache_info $settings  $server) 
                ($"# Info: KCL Settings created by ($env.PROVISIONING_NAME)\n# Date: (date now | format date '%Y-%m-%d %H:%M:%S')\n\n" + 
                $"($comment)\n($from_scratch)" +
                $"# Use a command like: '($env.PROVISIONING_NAME) server cache -o /tmp/data.yaml' to genereate '/tmp/($server.provider)_data.k' for 'defs' settings\n" +
                $"# then you can move genereated '/tmp/($server.provider)_data.k' to '/defs/($server.provider)_data.k'  \n\n" +
                $"import ($server.provider)_prov\n" +
                ($content | str replace '_data = {' $"($server.provider)_prov.Provision_($server.provider) {") | save $k_file_path --force
                )
                let result = (encrypt_secret $k_file_path --quiet)
                if ($result | is-not-empty) { ($result | save --force $k_file_path) } 
                _print $"(_ansi purple)kcl(_ansi reset) for (_ansi cyan)defs(_ansi reset) file has been created at (_ansi green)($k_file_path)(_ansi reset)"
            } 
            #show_clip_to $"kcl import ($outfile_path) -o ($k_file_path) --force ; sed -i '1,4d;s/^{/_data = {/' ($k_file_path) ; echo '{ main = _data.main, priv = _data.priv }' >> ($k_file_path)" true
            } 
        } else { 
            let cmd = ($env| get -o PROVISIONING_FILEVIEWER | default (if (^bash -c "type -P bat" | is-not-empty) { "bat" } else { "cat" }))
            if $cmd != "bat" { _print $"(_ansi magenta_bold)----------------------------------------------------------------------------------------------------------------(_ansi reset)"}
            if $env.PROVISIONING_WK_FORMAT == "json" { 
                ($data | to json | run-external $cmd -)
            } else { 
                ($data | to yaml | run-external $cmd -)
            }
            if $cmd != "bat" { _print $"(_ansi magenta_bold)----------------------------------------------------------------------------------------------------------------(_ansi reset)"}
        }
    }
}

export def find_server [
    item: string
    servers: list,
    out: string,
]: nothing -> record {
    if ($item | parse --regex '^[0-9]' | length) > 0 {
        let pos = ($item | into int)
        if ($pos >= ($servers | length)) {
            if ($out | is-empty)  { _print $"No server index ($pos) found "}
            {}
        } else {
            ($servers | get -o ($item | into int) | default {}) 
        }
    } else { 
        ($servers | where {|s| ( $s | get -o hostname | default "") == $item} | get -o 0 | default {}) 
    }
}
export def find_serversdefs [
    settings: record
]: nothing -> record {
    let src_path = ($settings | get -o src_path | default "")
    mut defs = []
    for it in ($settings | get -o data | get -o servers_paths | default []) {
        let name = ($it| str replace "/" "_")
        let it_path = if ($it | str ends-with ".k") { $it } else { $"($it).k" }
        let path_def = ($src_path | path join $it_path )
        let defs_srvs = if ($path_def | path exists ) {
            (open -r $path_def)
        } else { "" }
            $defs = ($defs | append { 
                name: $name, path_def: $it_path, def: $defs_srvs, defaults: ""
            }
        )
    }
    let defaults_path = ($env.PROVISIONING  | path join "kcl" | path join "defaults.k")
    let defaults = if ($defaults_path   | path exists) {
        (open -r $defaults_path | default "")
    } else { "" }
    let path_main = ($env.PROVISIONING  | path join "kcl" | path join "server.k")
    let main = if ($path_main | path exists) {
        (open -r $path_main | default "")
    } else { "" }
    let prov_defs = if ($env.PROVISIONING_PROVIDERS_PATH? == null) { 
        { 
            defs_providers: [],
            providers: [],
        }
    } else { 
        let providers_list = (ls -s  $env.PROVISIONING_PROVIDERS_PATH | where {|it| (
            ($it.name | str starts-with "_") == false 
                and ($env.PROVISIONING_PROVIDERS_PATH | path join $it.name | path type) == "dir"
                and ($env.PROVISIONING_PROVIDERS_PATH | path join $it.name | path join "templates" | path exists)
            )
        })  
        let defs_providers = ($providers_list | each {|it| 
            let it_path = ($src_path| path join "defs") 
            let defaults = if ($it_path | path join $"($it.name)_defaults.k" | path exists) { 
                (open -r ($it_path | path join $"($it.name)_defaults.k"))
            } else { "" }
            let def = if ($it_path | path join "servers.k" | path exists) { 
                (open -r ($it_path | path join "servers.k")) 
            } else { "" }
            { 
                name: $it.name, path_def: $it_path, def: $def, defaults: $defaults
            } 
        } | default [])
        let providers = ($providers_list | each {|it|
            let it_path = ($env.PROVISIONING_PROVIDERS_PATH | path join $it.name | path join "kcl") 
            let defaults = if ($it_path | path join $"defaults_($it.name).k" | path exists) { 
                (open -r ($it_path | path join $"defaults_($it.name).k"))
            } else { "" }
            let def = if ($it_path | path join $"server_($it.name).k" | path exists) { 
                (open -r ($it_path | path join $"server_($it.name).k")) 
            } else { "" }
            { 
                name: $it.name, path_def: $it_path, def: $def, defaults: $defaults
            } 
        } | default [])
        {
            defs_providers: $defs_providers,
            providers: $providers,
        }
    }
    { 
        defaults: $defaults,
        path_main: $path_main,
        main: $main,
        providers: $prov_defs.providers,
        defs_providers: $prov_defs.defs_providers,
        defs: $defs
    }
}
export def find_provgendefs [
]: nothing -> record {
    let prov_defs = if ($env.PROVISIONING_PROVIDERS_PATH? == null) { 
        { 
            defs_providers: [],
        }
    } else { 
        let providers_list = (ls -s  $env.PROVISIONING_PROVIDERS_PATH | where {|it| (
            ($it.name | str starts-with "_") == false 
                and ($env.PROVISIONING_PROVIDERS_PATH | path join $it.name | path type) == "dir"
                and ($env.PROVISIONING_PROVIDERS_PATH | path join $it.name | path join "templates" | path exists)
            )
        })  
        mut provdefs = []
        for it in $providers_list {
            let it_defs_path = ($env.PROVISIONING_PROVIDERS_PATH | path join $it.name  
                | path join $env.PROVISIONING_GENERATE_DIRPATH
                | path join $env.PROVISIONING_GENERATE_DEFSFILE)
            if ($it_defs_path | path exists) {
                $provdefs = ($provdefs | append { name: $it.name, defs: (open $it_defs_path) })
            } 
        }
        $provdefs
    }
    $prov_defs
}
