use lib_provisioning *
use ../lib_provisioning/user/config.nu [get-active-workspace get-workspace-path]
# Removed broken imports - these modules don't exist
# use create.nu *
# use servers/delete.nu *
# use handlers.nu *
#use ../lib_provisioning/utils ssh_cmd

# Main CLI handler for infra commands
export def "main list" [
    --infra (-i): string = ""  # Infrastructure (ignored for list, kept for compatibility)
    --notitles            # Suppress title output
] {
    # Get active workspace name
    let active_workspace = (get-active-workspace)

    if ($active_workspace | is-empty) {
        _print "🛑 No active workspace"
        _print "  Run: provisioning workspace list"
        _print "  Then: provisioning workspace activate <name>"
        return
    }

    # Get workspace path from the active workspace
    let ws_path = (get-workspace-path $active_workspace)

    if ($ws_path | is-empty) {
        _print $"🛑 Cannot find workspace path for '$active_workspace'"
        return
    }

    let infra_dir = ($ws_path | path join "infra")
    let current_infra = (config-get "infra.current" "")

    # List all infrastructures in the workspace infra directory
    if ($infra_dir | path exists) {
        # List directory contents, filter for directories that:
        # 1. Do not start with underscore (not hidden/system)
        # 2. Are directories
        # 3. Contain a settings.ncl file (marks it as a real infra)
        let infras = (ls -s $infra_dir | where {|it|
            ((($it.name | str starts-with "_") == false) and ($it.type == "dir") and (($infra_dir | path join $it.name "settings.ncl") | path exists))
        } | each {|it| $it.name} | sort)

        if ($infras | length) > 0 {
            _print $"(_ansi cyan_bold)Infrastructures in workspace:(_ansi reset)\n"
            for infra_name in $infras {
                let is_current = if ($infra_name == $current_infra) {
                    $"(_ansi green_bold)●(_ansi reset) "
                } else {
                    "  "
                }
                _print $"($is_current)(_ansi blue)($infra_name)(_ansi reset)"
            }
        } else {
            _print "No infrastructures found in workspace '$active_workspace'"
        }
    } else {
        _print $"🛑 Infra directory not found: ($infra_dir)"
    }
}

# Validate and display detailed infrastructure configuration
export def "main validate" [
    infra_name?: string  # Infrastructure name (optional, uses current or detects from args)
    --infra (-i): string = ""    # Infrastructure name (alternate flag format)
    --check (-c)                 # Check mode (accepted but not used for validate)
    --onsel: string = ""         # On selection (accepted but not used for validate)
    --yes (-y)                   # Auto-confirm (accepted but not used for validate)
    --notitles                   # Suppress title output
] {
    # Get active workspace name
    let active_workspace = (get-active-workspace)

    if ($active_workspace | is-empty) {
        _print "🛑 No active workspace"
        _print "  Run: provisioning workspace list"
        _print "  Then: provisioning workspace activate <name>"
        return
    }

    # Get workspace path from the active workspace
    let ws_path = (get-workspace-path $active_workspace)

    if ($ws_path | is-empty) {
        _print $"🛑 Cannot find workspace path for '$active_workspace'"
        return
    }

    let infra_dir = ($ws_path | path join "infra")

    # Determine which infrastructure to validate
    let target_infra = if ($infra_name | is-not-empty) {
        $infra_name
    } else if ($infra | is-not-empty) {
        $infra
    } else {
        # Try to detect from config
        (config-get "infra.current" "")
    }

    if ($target_infra | is-empty) {
        _print "❌ No infrastructure specified"
        _print ""
        _print "Usage: provisioning infra validate [<infrastructure_name>]"
        _print ""
        _print "Available infrastructures:"

        # List available infras
        if ($infra_dir | path exists) {
            let infras = (ls -s $infra_dir | where {|it|
                ((($it.name | str starts-with "_") == false) and ($it.type == "dir") and (($infra_dir | path join $it.name "settings.ncl") | path exists))
            } | each {|it| $it.name} | sort)

            for infra in $infras {
                _print $"  • (_ansi blue)($infra)(_ansi reset)"
            }
        }
        return
    }

    let target_path = ($infra_dir | path join $target_infra)

    if not ($target_path | path exists) {
        _print $"❌ Infrastructure not found: (_ansi red)($target_infra)(_ansi reset)"
        return
    }

    # Load infrastructure configuration files
    let settings_file = ($target_path | path join "settings.ncl")
    let servers_file = ($target_path | path join "defs" "servers.ncl")

    if not ($settings_file | path exists) {
        _print $"❌ Settings file not found: ($settings_file)"
        return
    }

    # Display infrastructure header
    _print ""
    _print $"(ansi cyan_bold)════════════════════════════════════════════════════════════════════════════(ansi reset)"
    _print $"(ansi cyan_bold)  ($target_infra | str upcase) INFRASTRUCTURE CONFIGURATION (ansi reset)"
    _print $"(ansi cyan_bold)════════════════════════════════════════════════════════════════════════════(ansi reset)"
    _print ""

    # Parse and display servers if the file exists
    if ($servers_file | path exists) {
        let servers_content = (open -r $servers_file)

        # Extract servers from the _servers array
        # Split by "upcloud_prov.Server_upcloud {" to find server blocks
        let server_blocks = ($servers_content | split row "upcloud_prov.Server_upcloud {" | skip 1)

        if ($server_blocks | length) > 0 {
            _print $"(ansi green_bold)Servers:(ansi reset)"
            _print ""

            for srv_idx in (0..($server_blocks | length)) {
                if $srv_idx >= ($server_blocks | length) { break }
                let block = ($server_blocks | get $srv_idx)
                let server_count = ($srv_idx + 1)

                # Extract hostname - look for: hostname = "..."
                let hostname = if ($block | str contains "hostname =") {
                    let lines = ($block | lines | where { |l| (($l | str contains "hostname =") and not ($l | str starts-with "#")) })
                    if ($lines | length) > 0 {
                        let line = ($lines | first)
                        let match = ($line | split row "\"" | get 1? | default "")
                        $match
                    } else {
                        "N/A"
                    }
                } else {
                    "N/A"
                }

                # Check if server is disabled - look for: not_use = True
                let is_disabled = ($block | str contains "not_use = True")
                let status = if $is_disabled { $"(ansi yellow)DISABLED(ansi reset)" } else { $"(ansi green)ACTIVE(ansi reset)" }

                # Extract plan - look for: plan = "..." (not commented, prefer last one)
                let plan = if ($block | str contains "plan =") {
                    let lines = ($block | lines | where { |l| (($l | str contains "plan =") and ($l | str contains "\"") and not ($l | str starts-with "#")) })
                    if ($lines | length) > 0 {
                        let line = ($lines | last)
                        ($line | split row "\"" | get 1? | default "")
                    } else {
                        "N/A"
                    }
                } else {
                    "N/A"
                }

                # Extract total storage - look for: total = ...
                let storage = if ($block | str contains "total =") {
                    let lines = ($block | lines | where { |l| (($l | str contains "total =") and not ($l | str starts-with "#")) })
                    if ($lines | length) > 0 {
                        let line = ($lines | first)
                        let value = ($line | str trim | split row "=" | get 1? | str trim)
                        ($value | str replace "," "" | str trim)
                    } else {
                        "N/A"
                    }
                } else {
                    "N/A"
                }

                # Extract IP - look for: network_private_ip = "..."
                let ip = if ($block | str contains "network_private_ip =") {
                    let lines = ($block | lines | where { |l| (($l | str contains "network_private_ip =") and not ($l | str starts-with "#")) })
                    if ($lines | length) > 0 {
                        let line = ($lines | first)
                        ($line | split row "\"" | get 1? | default "")
                    } else {
                        "N/A"
                    }
                } else {
                    "N/A"
                }

                # Extract taskservs - look for all lines with {name = "..."} within taskservs array
                let taskservs_list = if ($block | str contains "taskservs = [") {
                    let taskservs_section = ($block | split row "taskservs = [" | get 1? | split row "]" | first | default "")
                    let lines = ($taskservs_section | lines | where { |l| (($l | str contains "name =") and not ($l | str starts-with "#")) })
                    let taskservs = ($lines | each { |l|
                        let parts = ($l | split row "name =")
                        let value_part = if ($parts | length) > 1 { ($parts | get 1) } else { "" }
                        let name = ($value_part | split row "\"" | get 1? | default "")
                        if ($name | is-not-empty) { $name } else { null }
                    } | where { |n| ($n != null) })
                    $taskservs
                } else {
                    []
                }

                _print $"  ($server_count). (ansi cyan_bold)($hostname)(ansi reset) - ($status)"
                _print $"      Plan: (ansi blue)($plan)(ansi reset)"
                _print $"      Storage: (ansi blue)($storage)GB(ansi reset)"
                _print $"      IP: (ansi blue)($ip)(ansi reset)"

                if ($taskservs_list | length) > 0 {
                    _print $"      Taskservs: (ansi yellow)($taskservs_list | length)(ansi reset) installed"
                    for svc in $taskservs_list {
                        _print $"        • ($svc)"
                    }
                }
                _print ""
            }
        }
    }

    # Display summary
    _print $"(ansi cyan_bold)Summary:(ansi reset)"
    _print $"  Workspace: (ansi green)($active_workspace)(ansi reset)"
    _print $"  Infrastructure: (ansi green)($target_infra)(ansi reset)"
    _print $"  Path: (ansi yellow)($target_path)(ansi reset)"
    _print ""
    _print $"(ansi green)✓ Infrastructure configuration validated(ansi reset)"
    _print ""
}

export def on_create_infras [
    infras_list: list      # infras list
    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
] {
    let create_infra = {|infra|
        if not ($env.PROVISIONING_INFRA_PATH | path join $infra.item | path exists) {
            print $"\n🛑 Path not found for (_ansi red)($infra.item)(_ansi reset) in (_ansi cyan)($env.PROVISIONING_KLOUD_PATH)(_ansi reset)"
        } else {
            let settings = (find_get_settings --infra $infra.item)
            on_infra $infra $settings $check $wait $outfile $hostname $serverpos
        }
    }
    if $check {
        $infras_list | enumerate | each { |infra|  do $create_infra $infra }
    } else {
        $infras_list | enumerate | par-each { |infra|  do $create_infra $infra }
    }
}
export def on_infra [
    infra: record
    settings: record
    check: bool
    wait: bool
    outfile?: string   # Out file for creation
    hostname?: string  # Server hostname in settings
    serverpos?: int    # Server position in settings
] {
    print "TODO on_infra"
    print $infra
}
export def on_taskserv_infras [
    infras_list: list      # infras list
    check: bool        # Only check mode no servers will be created
    name?: string
    server?: string
    --iptype: string = "public" # Ip type to connect
] {
    let run_create = { |infra|
        let curr_settings = (find_get_settings --infra $infra)
        $env.WK_CNPROV = $curr_settings.wk_path
        let match_task = if $name == null or $name == "" { "" } else { $name }
        let match_server = if $server == null or $server == "" { "" } else { $server}
        on_taskservs $curr_settings $match_task $match_server $iptype $check
    }
    $infras_list | enumerate | par-each { |infra|
        let task = { do $run_create $infra.item }
        let result = desktop_run_notify $"($env.PROVISIONING_NAME) ($infra.item) taskservs create" "-> " $task --timeout 11sec
    }
}
export def on_delete_infras [
    infras_list: list      # infras list
    keep_storage:  bool    # keepstorage
    wait: bool         # Wait for creation
    name?: string  # Server hostname in settings
    serverpos?: int    # Server position in settings
] {
    let run_delete = { |infra, keepstorage|
        let curr_settings = (find_get_settings --infra $infra)
        on_delete_servers $curr_settings $keepstorage $wait $name $serverpos
    }
    $infras_list | enumerate | par-each { |infra|
        let task = { do $run_delete $infra.item $keep_storage }
        let result = desktop_run_notify $"($env.PROVISIONING_NAME) ($infra.item) servers delete" "-> " $task --timeout 11sec
    }
}
export def on_generate_infras [
    infras_list: list      # infras list
    check: bool            # Only check mode
    wait: bool             # Wait for creation
    outfile?: string       # Out file for generation
    name?: string          # Server hostname in settings
    serverpos?: int        # Server position in settings
] {
    print "TODO on_generate_infras"
    #  let curr_settings = (find_get_settings --infra $infra)
}
export def infras_walk_by [
    infras_list: list
    match_hostname: string
    check: bool        # Only check mode no servers will be created
    return_no_exists: bool
] {
    mut infra_servers = {}
    mut total_month = 0
    mut total_hour = 0
    mut total_day = 0
    mut table_items = []
    let sum_color  = { fg: '#0000ff' bg: '#dadada' attr: b }
    let total_color  = { fg: '#ffff00' bg: '#0000ff' attr: b }
    print $"(_ansi purple_reverse) Cost ($infras_list | str join ' ')(_ansi reset) "
    for infra in $infras_list {
        if not ($env.PROVISIONING_INFRA_PATH | path join $infra | path exists) {
            print $"\n🛑 Path not found for (_ansi red)($infra)(_ansi reset) in (_ansi cyan)($env.PROVISIONING_KLOUD_PATH)(_ansi reset)"
            continue
        }
        let settings = (find_get_settings --infra $infra)
        mut c_infra_servers = {}
        mut c_total_month = 0
        mut c_total_hour = 0
        mut c_total_day = 0
        for server in $settings.data.servers {
            if $match_hostname != null and $match_hostname != "" and $server.hostname != $match_hostname  {
                continue
            }
            # Check if provider key exists in infra_servers
            if not (($infra_servers | columns) | any { |col| $col == $server.provider }) {
                $infra_servers = ($infra_servers | merge { $server.provider: (mw_load_infra_servers_info $settings $server false)} )
            }
            let item_raw = (mw_get_infra_item $server $settings $infra_servers false)
            let item = { item: $item_raw, target: "server" }
            if  $env.PROVISIONING_DEBUG_CHECK { print ($item | table -e)}
            let price_month = (mw_get_infra_price $server $item "month" false | default 0)
            let price_hour = (mw_get_infra_price $server $item "hour" false | default 0)
            let price_day = ($price_hour * 24)
            $total_month += $price_month
            $total_hour += $price_hour
            $total_day += ($price_day)
            $c_total_month += $price_month
            $c_total_hour += $price_hour
            $c_total_day += ($price_day)
            let already_created = (mw_server_exists $server false)
            let host_color = if $already_created { "green_bold" } else { "red" }
            $table_items = ($table_items | append {
                host: $"(_ansi $host_color)($server.hostname)(_ansi reset) (_ansi blue_bold)($server.plan)(_ansi reset)",
                prov: $"(_ansi default_bold) ($server.provider) (_ansi reset)",
                hour: $"(_ansi default_bold)  ($price_hour)€  (_ansi reset)",
                day: $"(_ansi default_bold)  ($price_day | math round -p 4)€  (_ansi reset)",
                month: $"(_ansi default_bold)  ($price_month)€ (_ansi reset)"
            })
            if not $check {
                if not ($already_created) {
                    if $return_no_exists {
                        return { status: false, error: $"($server.hostname) not created" }
                        #} else {
                        #print $"(_ansi red_bold)($server.hostname)(_ansi reset) not created"
                    }
                }
            }
        }
        rm -rf $settings.wk_path
        $table_items = ($table_items | append {
            host: $"(_ansi --escape $sum_color)   ($settings.infra)  (_ansi reset)",
            prov: $"(_ansi default_bold) (_ansi reset)",
            hour: $"(_ansi --escape $sum_color)  ($c_total_hour | math round -p 4)€  (_ansi reset)",
            day:  $"(_ansi --escape $sum_color)  ($c_total_day | math round -p 4)€  (_ansi reset)",
            month:$"(_ansi --escape $sum_color)  ($c_total_month)€  (_ansi reset)"
        })
    }
    $table_items = ($table_items | append { host: "", prov: "", month: "", day: "", hour: ""})
    $table_items = ($table_items | append {
        host: $"(_ansi --escape $total_color)   TOTAL  (_ansi reset)",
        prov: $"(_ansi default_bold) (_ansi reset)",
        hour: $"(_ansi --escape $total_color)  ($total_hour | math round -p 4)€  (_ansi reset)",
        day:  $"(_ansi --escape $total_color)  ($total_day | math round -p 4)€  (_ansi reset)",
        month:$"(_ansi --escape $total_color)  ($total_month)€  (_ansi reset)"
    })
    _print ($table_items | table -i false)
}
