# List all servers in active workspace
# This file is sourced by bash after lib_minimal.nu is loaded
# Not meant to be run standalone
# Usage: Called from bash with optional $INFRA_FILTER environment variable

# PWD-based workspace detection: if we're inside a workspace root that has
# config/provisioning.ncl, use it — takes precedence over the active workspace.
let pwd_config_file = ($env.PWD | path join "config" "provisioning.ncl")
use ../lib_provisioning/utils/nickel_processor.nu [ncl-eval-soft]

let pwd_ws_config = if ($pwd_config_file | path exists) {
    ncl-eval-soft $pwd_config_file [] {}
} else { {} }

let pwd_ws_name     = ($pwd_ws_config | get --optional workspace    | default "")
let pwd_current_infra = ($pwd_ws_config | get --optional current_infra | default "")

# Convention fallback: if config/provisioning.ncl has no current_infra but
# infra/<pwd-basename>/settings.ncl exists, that's the default infra.
let pwd_convention_infra = if ($pwd_current_infra | is-empty) {
    let candidate = ($env.PWD | path join "infra" ($env.PWD | path basename) | path join "settings.ncl")
    if ($candidate | path exists) { $env.PWD | path basename } else { "" }
} else { "" }

let pwd_infra = if ($pwd_current_infra | is-not-empty) { $pwd_current_infra } else { $pwd_convention_infra }

# Resolve workspace: PWD-inferred takes precedence over session active workspace
let ws_path = if ($pwd_ws_name | is-not-empty) {
    # We are inside the workspace root — PWD is the workspace path
    $env.PWD
} else {
    # Fall back to active workspace from user_config.yaml
    let ws_result = (workspace-active)
    let active_ws = if (is-ok $ws_result) { $ws_result.ok } else { "" }
    if ($active_ws | is-empty) {
        print 'No active workspace. Run: provisioning workspace activate <name>'
        exit 1
    }

    let user_config_path = (
        $env.HOME | path join 'Library' | path join 'Application Support'
        | path join 'provisioning' | path join 'user_config.yaml'
    )
    if not ($user_config_path | path exists) {
        print 'Config not found'
        exit 1
    }
    let config = (open $user_config_path)
    let workspaces = ($config | get --optional workspaces | default [])
    let ws = ($workspaces | where { $in.name == $active_ws } | first)
    if ($ws | is-empty) {
        print $"Workspace '($active_ws)' not found in user config"
        exit 1
    }
    $ws.path
}

let infra_path = ($ws_path | path join 'infra')
if not ($infra_path | path exists) {
    print 'No infrastructures found'
    exit 0
}

# Resolve filter: explicit INFRA_FILTER > PWD current_infra > convention > workspace default_infra
let filter_raw = ($env.INFRA_FILTER? | default "")
let filter = if ($filter_raw | is-not-empty) {
    $filter_raw | path basename
} else if ($pwd_infra | is-not-empty) {
    $pwd_infra
} else {
    # Last resort: registered workspace default_infra from user config
    let ws_result2 = (workspace-active)
    let active_ws2 = if (is-ok $ws_result2) { $ws_result2.ok } else { "" }
    if ($active_ws2 | is-not-empty) {
        let uc = (
            $env.HOME | path join 'Library' | path join 'Application Support'
            | path join 'provisioning' | path join 'user_config.yaml'
        )
        if ($uc | path exists) {
            let wlist = (open $uc | get --optional workspaces | default [])
            let wentry = ($wlist | where { $in.name == $active_ws2 } | first)
            $wentry | get --optional default_infra | default ""
        } else { "" }
    } else { "" }
}

# List server definitions from infrastructure (filtered if --infra specified)
let servers = (
    ls $infra_path
    | where type == 'dir'
    | each {|infra|
        let infra_name = ($infra.name | path basename)

        # Skip if filter is specified and doesn't match
        if (($filter | is-not-empty) and ($infra_name != $filter)) {
            []
        } else {
            # servers.ncl can live directly in infra dir or under defs/
            let infra_dir = ($infra_path | path join $infra_name)
            let servers_file_direct = ($infra_dir | path join 'servers.ncl')
            let servers_file_defs   = ($infra_dir | path join 'defs' | path join 'servers.ncl')
            let servers_file = if ($servers_file_direct | path exists) {
                $servers_file_direct
            } else {
                $servers_file_defs
            }

            if ($servers_file | path exists) {
                # Parse servers.ncl: correlate hostname / server_type / private_ip per block.
                # Strategy: scan lines in order; a new server block begins at each `make_server {`
                # (or at the first hostname = "..." after the previous block closes).
                # We accumulate fields until the next block starts.
                let lines = (open $servers_file --raw | split row "\n")
                let extract_quoted = {|line|
                    let parts = ($line | split row '"')
                    if ($parts | length) >= 2 { $parts | get 1 } else { "" }
                }

                # Build one record per server by scanning lines top-to-bottom.
                # Reset on each `make_server {` boundary.
                let parsed = (
                    $lines | reduce --fold {blocks: [], cur: {hostname: "", server_type: "", private_ip: ""}} {|line, acc|
                        let trimmed = ($line | str trim)
                        if ($trimmed =~ 'make_server\s*\{') {
                            # flush previous if it had a hostname
                            let blocks = if ($acc.cur.hostname | is-not-empty) {
                                $acc.blocks | append $acc.cur
                            } else {
                                $acc.blocks
                            }
                            {blocks: $blocks, cur: {hostname: "", server_type: "", private_ip: ""}}
                        } else if ($trimmed =~ '^hostname\s*=\s*"') {
                            {blocks: $acc.blocks, cur: ($acc.cur | upsert hostname (do $extract_quoted $trimmed))}
                        } else if ($trimmed =~ '^server_type\s*=\s*"') {
                            {blocks: $acc.blocks, cur: ($acc.cur | upsert server_type (do $extract_quoted $trimmed))}
                        } else if ($trimmed =~ '^private_ip\s*=\s*"') {
                            {blocks: $acc.blocks, cur: ($acc.cur | upsert private_ip (do $extract_quoted $trimmed))}
                        } else {
                            $acc
                        }
                    }
                )
                # flush last block
                let all_blocks = if ($parsed.cur.hostname | is-not-empty) {
                    $parsed.blocks | append $parsed.cur
                } else {
                    $parsed.blocks
                }

                $all_blocks
                | where {|b| $b.hostname | is-not-empty }
                | each {|b|
                    {
                        name:           $b.hostname
                        infrastructure: $infra_name
                        server_type:    $b.server_type
                        private_ip:     $b.private_ip
                        path:           $servers_file
                    }
                }
            } else {
                []
            }
        }
    }
    | flatten
)

# Read persisted server state (written by server_create workflow post-sync)
# Key: server name → { provider_id, public_ip, location, status, floating_ip, floating_ip_address }
let cached_state_path = ($ws_path | path join "infra" | path join $filter | path join ".servers-state.json")
let cached_state = if ($filter | is-not-empty) and ($cached_state_path | path exists) {
    open $cached_state_path
} else { {} }

# Bootstrap state: FIP name → actual IP (fallback when server not in cached_state)
let bs_state_path = ($ws_path | path join ".provisioning-state.json")
let bs_fips = if ($bs_state_path | path exists) {
    open $bs_state_path | get -o bootstrap.floating_ips | default {}
} else { {} }

# Query live status from hcloud for real-time status updates
let hcloud_res = (do { ^hcloud server list -o json } | complete)
let live_servers_all = if $hcloud_res.exit_code == 0 and ($hcloud_res.stdout | str trim | is-not-empty) {
    let parsed = ($hcloud_res.stdout | from json)
    if (($parsed | describe) | str starts-with "list") { $parsed } else { [] }
} else { [] }
let live_servers = if ($filter | is-not-empty) {
    $live_servers_all | where {|l| ($servers | any {|s| $s.name == $l.name }) }
} else {
    $live_servers_all
}

def status_icon [s: string] {
    match $s {
        "running"      => "🟢"
        "off"          => "🔴"
        "starting"     => "🟡"
        "stopping"     => "🟡"
        "rebuilding"   => "🔵"
        "migrating"    => "🔵"
        _              => "⚪"
    }
}

if ($servers | length) == 0 {
    print '📦 Available Servers: (none configured)'
} else {
    print ''
    let rows = ($servers | each {|srv|
            let live    = ($live_servers | where {|l| $l.name == $srv.name} | first | default null)
            let cached  = ($cached_state | get -o $srv.name | default null)

            # Status: hcloud live > cached state > unknown
            let status = if $live != null { $live.status } else if $cached != null { $cached.status } else { "—" }

            # Public IP: hcloud live > cached state
            let pub_ip = if $live != null {
                $live.public_net?.ipv4?.ip? | default ""
            } else if $cached != null {
                $cached.public_ip? | default ""
            } else { "" }

            # Private IP: hcloud live (actual) > NCL (desired)
            let priv_ip = if $live != null {
                $live.private_net? | default [] | first | default null | get --optional ip | default ""
            } else {
                $srv.private_ip? | default ""
            }

            # Server type: hcloud live > NCL (type is config, not runtime state)
            let srv_type = if $live != null {
                $live.server_type?.name? | default ($srv.server_type? | default "")
            } else {
                $srv.server_type? | default ""
            }

            # Location: hcloud live > cached state
            let location = if $live != null {
                $live.datacenter?.location?.name? | default ""
            } else if $cached != null {
                $cached.location? | default ""
            } else { "" }

            # Floating IP: cached state (has name+ip) > bootstrap state lookup by FIP name
            let fip_display = if $cached != null and ($cached.floating_ip? | default "" | is-not-empty) {
                let fip_ip = ($cached.floating_ip_address? | default "")
                if ($fip_ip | is-not-empty) {
                    $"($cached.floating_ip) ($fip_ip)"
                } else {
                    $cached.floating_ip
                }
            } else {
                # Fallback: resolve FIP IP from bootstrap state using the FIP name in NCL
                let fip_name = ($srv | get -o floating_ip | default "")
                if ($fip_name | is-not-empty) {
                    let fip_key = ($fip_name | str replace --all "librecloud-fip-" "" | str replace --all "-" "_")
                    let fip_rec = ($bs_fips | get -o $fip_key | default null)
                    if $fip_rec != null {
                        $"($fip_name) ($fip_rec.ip? | default "")"
                    } else { $fip_name }
                } else { "" }
            }

            # Delete protection: hcloud live > cached state
            let protected = if $live != null {
                $live.protection?.delete? | default false
            } else if $cached != null {
                $cached.protection_delete? | default false
            } else { false }
            let lock_icon = if $protected { "🔒" } else { "" }

            {
                hostname:    $srv.name
                type:        $srv_type
                location:    $location
                status:      $status
                public_ip:   $pub_ip
                private_ip:  $priv_ip
                floating_ip: $fip_display
                protected:   $lock_icon
                provider:    "hetzner"
            }
        }
    )
    print ($rows | table -i false)
}
