
use ../config/accessor.nu *
use ../utils/on_select.nu run_on_selection
export def get_provisioning_info [
    dir_path: string
    target: string
] {
    # task root path target will be empty
    let item =  if $target != "" { $target } else { ($dir_path | path basename) }
    let full_path = if $target != "" { $"($dir_path)/($item)" } else { $dir_path }
    if not ($full_path | path exists) {
        _print $"🛑 NO path found for (_ansi cyan)($full_path)(_ansi reset)"
        return []
    }
    ls -s  $full_path | where {|el|(
            $el.type == "dir"
            # discard paths with "_" prefix
            and ($el.name != "generate" )
            and ($el.name | str starts-with "_") == false
            and (
                # When target is empty (listing all tasks), accept all directories
                # When target is specified (looking for modes), require default or install script
                if $target == "" {
                    true
                } else {
                    # for main task directory at least has default
                    ($full_path | path join $el.name | path join "default" | path exists)
                    # for modes in  task directory at least has install-task.sh file
                    or ($"($full_path)/($el.name)/install-($item).sh" | path exists)
                }
            )
        )} |
        each {|it|
            if ($"($full_path)/($it.name)" | path exists) and ($"($full_path)/($it.name)/provisioning.toml" | path exists) {
                # load provisioning.toml for info and vers
                let provisioning_data = open $"($full_path)/($it.name)/provisioning.toml"
                { task: $item, mode: ($it.name), info: $provisioning_data.info, vers: $provisioning_data.release}
            } else {
                { task: $item, mode: ($it.name), info: "", vers: ""}
            }
        }
}
export def providers_list [
    mode?: string
] {
    let configured_path = (get-providers-path)
    let providers_path = if ($configured_path | is-empty) {
        # Fallback to system providers directory
        ($env.PROVISIONING | path join "extensions/providers")
    } else {
        $configured_path
    }
    if ($providers_path | is-empty) or (not ($providers_path | path exists)) { return [] }
    ls -s  $providers_path | where {|it| (
        ($it.name | str starts-with "_") == false
            and ($providers_path | path join $it.name | path type) == "dir"
            and ($providers_path | path join $it.name | path join "templates" | path exists)
        )
    } |
    each {|it|
        let it_path = ($providers_path | path join $it.name | path join "provisioning.yaml")
        if ($it_path | path exists) {
            # load provisioning.yaml for info and vers
            let provisioning_data = (open $it_path | default {})
            let tools = match $mode {
                "list" | "selection" => ($provisioning_data | get tools? | default {} | transpose key value | get key? | default [] | str join ',')
                _ =>  ($provisioning_data | get tools? | default [])
            }
            { name: ($it.name), info: ($provisioning_data | get info? | default ""), tools: $tools }
        } else {
            { name: ($it.name), info: "", vers: "", source: "", site: ""}
        }
    }
}
def detect_infra_context [] {
    # Detect if we're inside an infrastructure directory OR using --infra flag
    # Priority: 1) PROVISIONING_INFRA env var (from --infra flag), 2) pwd path detection

    # Check if PROVISIONING_INFRA is set (from --infra flag)
    let env_infra = ($env.PROVISIONING_INFRA? | default "")
    if ($env_infra | is-not-empty) {
        return $env_infra
    }

    # Fallback: detect from current path
    let current_path = pwd

    # Check if path contains /infra/ pattern
    if not ($current_path | str contains "/infra/") {
        return ""
    }

    # Simple string manipulation to extract infra name
    # Find everything after "/infra/" and take the first directory name
    let after_infra = (
        $current_path
        | str index-of "/infra/"
    )

    if ($after_infra == null) {
        return ""
    }

    # Get substring after /infra/
    let start_pos = $after_infra + ("/infra/" | str length)
    let remainder = ($current_path | str substring $start_pos..)

    # Get first path component
    let first_component = (
        $remainder
        | split row "/"
        | get 0
    )

    if ($first_component | is-empty) {
        return ""
    }

    $first_component
}

def get_infra_taskservs [infra_name: string] {
    # Get taskservs from specific infrastructure directory
    let current_path = pwd

    # Try two approaches:
    # 1) If we're inside an infra directory, navigate from there
    # 2) Otherwise, look for workspace_librecloud relative to current path

    let infra_taskservs_path = if ($current_path | str contains "/infra/") {
        # Approach 1: Navigate from inside infra directory
        # Current path: .../workspace_librecloud/infra/sgoyol or deeper
        # We need: .../workspace_librecloud/infra/$infra_name/taskservs
        (
            $current_path
            | path dirname     # .../workspace_librecloud/infra
            | path dirname     # .../workspace_librecloud
            | path join "infra"
            | path join $infra_name
            | path join "taskservs"
        )
    } else {
        # Approach 2: Look for workspace_librecloud in current path or parent
        let workspace_base = if ($current_path | path basename) == "workspace_librecloud" {
            $current_path
        } else if ($current_path | path join "workspace_librecloud" | path exists) {
            $current_path | path join "workspace_librecloud"
        } else {
            ""
        }

        if ($workspace_base | is-empty) {
            return []
        }

        $workspace_base
        | path join "infra"
        | path join $infra_name
        | path join "taskservs"
    }

    if not ($infra_taskservs_path | path exists) {
        return []
    }

    # List all .ncl files and directories in this infra's taskservs folder
    ls -s $infra_taskservs_path | where {|el|
        ($el.name | str ends-with ".ncl") or ($el.type == "dir" and ($el.name | str starts-with "_") == false)
    } | each {|it|
        # Parse task name from filename (remove .ncl extension if present)
        let task_name = if ($it.name | str ends-with ".ncl") {
            $it.name | str replace ".ncl" ""
        } else {
            $it.name
        }

        # Try to find this taskserv in global list to get full info
        let provisioning_taskservs = ($env.PROVISIONING_TASKSERVS_PATH? | default "")
        if ($provisioning_taskservs | is-empty) or (not ($provisioning_taskservs | path exists)) {
            { task: $task_name, mode: "configured", info: "", vers: "" }
        } else {
            # Search for this taskserv in global list
            let global_tasks = get_provisioning_info $provisioning_taskservs "" | each { |it2|
                get_provisioning_info ($provisioning_taskservs | path join $it2.mode) ""
            } | flatten

            let matching = $global_tasks | where {|t| $t.task == $task_name}
            if ($matching | length) > 0 {
                $matching | get 0
            } else {
                { task: $task_name, mode: "local", info: "", vers: "" }
            }
        }
    }
}

export def taskservs_list [
] {
    # Detect if we're inside an infrastructure directory
    let infra_context = detect_infra_context

    if ($infra_context | is-not-empty) {
        # If inside infra, list only taskservs in that infra
        return (get_infra_taskservs $infra_context)
    }

    # Otherwise list all available taskservs from provisioning extensions
    # This allows discovering all taskservs regardless of workspace location
    let provisioning_taskservs = ($env.PROVISIONING_TASKSERVS_PATH? | default "")

    if ($provisioning_taskservs | is-empty) {
        return []
    }

    if not ($provisioning_taskservs | path exists) {
        return []
    }

    get_provisioning_info $provisioning_taskservs "" |
    each { |it|
        get_provisioning_info ($provisioning_taskservs | path join $it.mode) ""
    } | flatten
}
export def cluster_list [
] {
    # Determine workspace base path
    # Try: 1) check if we're already in workspace, 2) look for workspace_librecloud relative to pwd
    let current_path = pwd
    let workspace_base = if ($current_path | path basename) == "workspace_librecloud" {
        $current_path
    } else if ($current_path | path join "workspace_librecloud" | path exists) {
        $current_path | path join "workspace_librecloud"
    } else {
        ""
    }

    # Return empty if workspace not found
    if ($workspace_base | is-empty) {
        return []
    }

    let clusters_path = $workspace_base | path join ".clusters"

    # Return empty if clusters path doesn't exist
    if not ($clusters_path | path exists) {
        return []
    }

    get_provisioning_info $clusters_path "" |
    each { |it|
        get_provisioning_info ($clusters_path | path join $it.mode) ""
    } | flatten | default []
}
export def infras_list [
] {
    # Determine workspace base path
    # Try: 1) check if we're already in workspace, 2) look for workspace_librecloud relative to pwd
    let current_path = pwd
    let workspace_base = if ($current_path | path basename) == "workspace_librecloud" {
        $current_path
    } else if ($current_path | path join "workspace_librecloud" | path exists) {
        $current_path | path join "workspace_librecloud"
    } else {
        ""
    }

    # Return empty if workspace not found
    if ($workspace_base | is-empty) {
        return []
    }

    let infra_path = $workspace_base | path join "infra"

    # Return empty if infra path doesn't exist
    if not ($infra_path | path exists) {
        return []
    }

    # List infra directories that have defs subdirectory
    ls -s $infra_path | where {|el|
        $el.type == "dir" and ($infra_path | path join $el.name | path join "defs" | path exists)
    } | each { |it|
        { name: $it.name, modified: $it.modified, size: $it.size}
    } | flatten | default []
}
export def on_list [
    target_list: string
    cmd: string
    ops: string
] {
    #use utils/on_select.nu run_on_selection
    match  $target_list {
        "providers" | "p" => {
            _print $"\n(_ansi green)PROVIDERS(_ansi reset) list: \n"
            let list_items = (providers_list "selection")
            if ($list_items | length) == 0 {
                _print $"🛑 no items found for (_ansi cyan)providers list(_ansi reset)"
                return []
            }
            if $cmd == "-" { return $list_items }
            if ($cmd | is-empty) {
                _print ($list_items | to json) "json" "result" "table"
            } else {
                if (get-provisioning-out | is-not-empty) or (get-provisioning-no-terminal) { return ""}
                let selection_pos = ($list_items | each {|it|
                    match ($it.name | str length) {
                        2..5  =>   $"($it.name)\t\t ($it.info) \tversion: ($it.vers)",
                        _  =>   $"($it.name)\t ($it.info) \tversion: ($it.vers)",
                    }
                } | input list --index (
                        $"(_ansi default_dimmed)Select one item for (_ansi cyan_bold)($cmd)(_ansi reset)" +
                        $" \(use arrow keys and press [enter] or [escape] to exit\)( _ansi reset)"
                    )
                )
                if $selection_pos != null {
                    let item_selec = if ($list_items | length) > $selection_pos { $list_items | get $selection_pos } else { null }
                    let item_path = ((get-providers-path) | path join $item_selec.name)
                    if not ($item_path | path exists) { _print $"Path ($item_path) not found" }
                    (run_on_selection $cmd $item_selec.name $item_path
                        ($item_path | path join "nulib" | path join $item_selec.name | path join "servers.nu") (get-providers-path))
                }
            }
            return []
        },
        "taskservs" | "t" => {
            _print $"\n(_ansi blue)TASKSERVICESS(_ansi reset) list: \n"
            let list_items = (taskservs_list)
            if ($list_items | length) == 0 {
                _print $"🛑 no items found for (_ansi cyan)taskservs list(_ansi reset)"
                return
            }
            if $cmd == "-" { return $list_items }
            if ($cmd | is-empty) {
                _print ($list_items | to json) "json" "result" "table"
                return []
            } else {
                if (get-provisioning-out | is-not-empty) or (get-provisioning-no-terminal) { return ""}
                let selection_pos = ($list_items | each {|it|
                    match ($it.task | str length) {
                        2..4 => $"($it.task)\t\t  ($it.mode)\t\t($it.info)\t($it.vers)",
                        5 => $"($it.task)\t\t  ($it.mode)\t\t($it.info)\t($it.vers)",
                        12 => $"($it.task)\t  ($it.mode)\t\t($it.info)\t($it.vers)",
                        15..20 => $"($it.task) ($it.mode)\t\t($it.info)\t($it.vers)",
                        _ => $"($it.task)\t  ($it.mode)\t\t($it.info)\t($it.vers)",
                    }
                } | input list --index (
                        $"(_ansi default_dimmed)Select one item for (_ansi cyan_bold)($cmd)(_ansi reset)" +
                        $" \(use arrow keys and press [enter] or [escape] to exit\)( _ansi reset)"
                    )
                )
                if $selection_pos != null {
                    let item_selec = if ($list_items | length) > $selection_pos { $list_items | get $selection_pos } else { null }
                    let item_path = $"((get-taskservs-path))/($item_selec.task)/($item_selec.mode)"
                    if not ($item_path | path exists) { _print $"Path ($item_path) not found" }
                    run_on_selection $cmd $item_selec.task $item_path ($item_path | path join $"install-($item_selec.task).sh") (get-taskservs-path)
                }
            }
            return []
        },
        "clusters" | "c" => {
            _print $"\n(_ansi purple)Cluster(_ansi reset) list: \n"
            let list_items = (cluster_list)
            if ($list_items | length) == 0 {
                _print $"🛑 no items found for (_ansi cyan)cluster list(_ansi reset)"
                return []
            }
            if $cmd == "-" { return $list_items }
            if ($cmd | is-empty) {
                _print ($list_items | to json) "json" "result" "table"
            } else {
                if (get-provisioning-out | is-not-empty) or (get-provisioning-no-terminal) { return ""}
                let selection = (cluster_list | input list)
                #print ($"(_ansi default_dimmed)Select one item for (_ansi cyan_bold)($cmd)(_ansi reset) " +
                #        $" \(use arrow keys and press [enter] or [escape] to exit\)( _ansi reset)" )
                _print $"($cmd) ($selection)"
            }
            return []
        },
        "infras" | "i" => {
            _print $"\n(_ansi cyan)Infrastructures(_ansi reset) list: \n"
            let list_items = (infras_list)
            if ($list_items | length) == 0 {
                _print $"🛑 no items found for (_ansi cyan)infras list(_ansi reset)"
                return []
            }
            if $cmd == "-" { return $list_items }
            if ($cmd | is-empty) {
                _print ($list_items | to json) "json" "result" "table"
            } else {
                if (get-provisioning-out | is-not-empty) or (get-provisioning-no-terminal) { return ""}
                let selection_pos = ($list_items | each {|it|
                    match ($it.name | str length) {
                        2..5 => $"($it.name)\t\t  ($it.modified) -- ($it.size)",
                        12 => $"($it.name)\t  ($it.modified) -- ($it.size)",
                        15..20 => $"($it.name) ($it.modified) -- ($it.size)",
                        _ => $"($it.name)\t  ($it.modified) -- ($it.size)",
                    }
                } | input list --index (
                        $"(_ansi default_dimmed)Select one item for (_ansi cyan_bold)($cmd)(_ansi reset)" +
                        $" \(use arrow keys and [enter] or [escape] to exit\)( _ansi reset)"
                    )
                )
                if $selection_pos != null {
                    let item_selec = if ($list_items | length) > $selection_pos { $list_items | get $selection_pos } else { null }
                    let item_path = $"((get-workspace-path))/($item_selec.name)"
                    if not ($item_path | path exists) { _print $"Path ($item_path) not found" }
                    run_on_selection $cmd $item_selec.name $item_path ($item_path | path join (get-default-settings)) (get-provisioning-infra-path)
                }
            }
            return []
        },
        "help" | "h" | _ => {
            if $target_list != "help" or $target_list != "h" {
                _print $"🛑 Not found ((get-provisioning-name)) target list option (_ansi red)($target_list)(_ansi reset)"
            }
            _print (
                $"Use (_ansi blue_bold)((get-provisioning-name))(_ansi reset) (_ansi green)list(_ansi reset)" +
                $" [ providers (_ansi green)p(_ansi reset) | tasks (_ansi green)t(_ansi reset) | " +
                $"infras (_ansi cyan)i(_ansi reset) ] to list items" +
                $"\n(_ansi default_dimmed)add(_ansi reset) --onsel (_ansi yellow_bold)e(_ansi reset)dit | " +
                $"(_ansi yellow_bold)v(_ansi reset)iew | (_ansi yellow_bold)l(_ansi reset)ist | (_ansi yellow_bold)t(_ansi reset)ree | " +
                $"(_ansi yellow_bold)c(_ansi reset)ode | (_ansi yellow_bold)s(_ansi reset)hell | (_ansi yellow_bold)n(_ansi reset)u"
            )
            return []
        },
        _ => {
            _print $"🛑 invalid_option $list ($ops)"
            return []
        }
    }
}
