
use std
use ../config/accessor.nu *
use ../utils/interface.nu *
use ../utils/init.nu [get-provisioning-use-sops, get-workspace-path, get-provisioning-infra-path]

def find_file [
    start_path: string
    match_path: string
    only_first: bool
] {
    mut found_path = ""
    mut search_path = $start_path
    let home_root = ($env.HOME | path dirname)
    while $found_path == "" and  $search_path != "/" and $search_path != $home_root {
        if $search_path == "" { break }
        let res = if $only_first {
            (^find $search_path -type f -name $match_path -print -quit | complete)
        } else {
            (^find $search_path -type f -name $match_path err> (if $nu.os-info.name == "windows" { "NUL" } else { "/dev/null" }) | complete)
        }
    if $res.exit_code == 0 { $found_path = ($res.stdout | str trim )  }
        $search_path = ($search_path | path dirname)
    }
    $found_path
}

export def run_cmd_sops [
    task: string
    cmd: string
    source_path: string
    error_exit: bool
] {
    let str_cmd = $"-($cmd)"
    let use_sops_value = (get-provisioning-use-sops | into string)
    let res  = if ($use_sops_value | str contains "age") {
        if $env.SOPS_AGE_RECIPIENTS? != null {
            (with-env { SOPS_AGE_KEY_FILE: (get-sops-age-key-file) } {
                do { ^sops $str_cmd --config (find-sops-key) --age $env.SOPS_AGE_RECIPIENTS $source_path } | complete
            })
        } else {
            if $error_exit {
                (throw-error $"🛑 Sops with age error" $"(_ansi red)no AGE_RECIPIENTS(_ansi reset) for (_ansi green)($source_path)(_ansi reset)"
                "on_sops decrypt" --span (metadata $task).span)
            } else {
                _print $"🛑 Sops with age error (_ansi red)no AGE_RECIPIENTS(_ansi reset) for (_ansi green_bold)($source_path)(_ansi reset)"
                return ""
            }
        }
    } else {
        (^sops $str_cmd --config (find-sops-key)  $source_path | complete )
    }
    if $res.exit_code != 0 {
        if $error_exit {
            (throw-error $"🛑 Sops error" $"(_ansi red)($source_path)(_ansi reset) ($res.stdout)"
                $"on_sops ($task)" --span (metadata $res).span)
        } else {
            _print $"🛑 Sops error (_ansi red)($source_path)(_ansi reset) ($res.exit_code)"
            return ""
        }
    }
    return $res.stdout
}
export def on_sops [
    task: string            #
    source_path: string     #
    output_path?: string    #
    ...args                 # Args for create command
    --check (-c)            # Only check mode no servers will be created
    --error_exit
    --quiet
] {
    #[ -z "$PROVIISONING_SOPS" ] && echo "PROVIISONING_SOPS not defined on_sops $sops_task for $source to $target" && return
    # if [ -z "$PROVIISONING_SOPS" ] && [ -z "$($YQ -er '.sops' < "$source" 2>(if $nu.os-info.name == "windows" { "NUL" } else { "/dev/null" }) | sed 's/null//g')" ]; then
    #     [ -z "$source" ] && echo "Error not source file found" && return
    #     [ -z "$target" ] && cat "$source" && return
    #     [ "$source" != "$target" ] && cat "$source" > "$target"
    #     return
    # fi
    # [ -n "$PROVIISONING_SOPS" ] && cfg_ops="--config $PROVIISONING_SOPS"
    # [ -n "$target" ]  && output="--output $target"
    match $task {
        "sed" =>  {
            # check is a sops file or error
            if (is_sops_file $source_path) {
                ^sops $source_path
            } else {
                (throw-error $"🛑 File (_ansi green_italic)($source_path)(_ansi reset) exists"
                    $"No (_ansi yellow_bold)sops(_ansi reset) content found "
                    "on_sops sed"
                    --span (metadata $source_path).span
                )
            }
        },
        "is_sops" | "i" => {
            return (is_sops_file $source_path)
        },
        "encrypt" | "encode" | "e" => {
            if not ( $source_path | path exists ) {
                if not $quiet { _print $"🛑 No file ($source_path) found to decrypt with sops " }
                return ""
            }
            if (is_sops_file $source_path) {
                if not $quiet { _print $"🛑 File ($source_path) alredy with sops " }
                return (open -r $source_path)
            }
            let result = (run_cmd_sops "encrypt" "e" $source_path  $error_exit)
            if ($output_path | is-not-empty) {
                $result | save -f $output_path
                if not $quiet { _print $"Result saved in ($output_path) " }
            }
            return $result
        },
        "generate" | "gen" | "g" => {
            generate_sops_file $source_path $output_path $quiet
        },
        "decrypt" | "decode" | "d" => {
            if not ( $source_path | path exists ) {
                if not $quiet { _print $"🛑 No file ($source_path) found to decrypt with sops " }
                return ""
            }
            if not (is_sops_file $source_path) {
                if not $quiet { _print $"🛑 File ($source_path) does not have sops info " }
                return (open -r $source_path)
            }
            let result = (run_cmd_sops "decrypt" "d" $source_path  $error_exit)
            if ($output_path | is-not-empty) {
                $result | save -f $output_path
                if not $quiet { _print $"Result saved in ($output_path) " }
            }
            return $result
        },
        _ => {
            (throw-error $"🛑 Option " $"(_ansi red)($task)(_ansi reset) undefined")
            return ""
        }
    }
}
export def generate_sops_file [
    source_path: string
    target_path: string
    quiet: bool
] {
    let result = (on_sops "encrypt"  $source_path --error_exit)
    if result == "" {
        _print $"🛑 File ($source_path) not sops generated"
        return false
    }
    $result | save -f $target_path
    if not $quiet {
        _print $"($source_path) generated for 'sops' "
    }
    return  true
}
export def generate_sops_settings [
    mode: string
    target: string
    file: string
] {
    _print ""
    # [ -z "$ORG_MAIN_SETTINGS_FILE" ] && return
    # [ -r "$PROVIISONING_KEYS_PATH" ] && [ -n "$PROVIISONING_USE_nickel" ] && _on_sops_item "$mode" "$PROVIISONING_KEYS_PATH" "$target"
    # file=$($YQ -er < "$ORG_MAIN_SETTINGS_FILE" ".defaults_path" | sed 's/null//g')
    # [ -n "$file" ] && _on_sops_item "$mode" "$file" "$target"
    # _on_sops_item "$mode" "$ORG_MAIN_SETTINGS_FILE" "$target"
    # list=$($YQ -er < "$ORG_MAIN_SETTINGS_FILE" ".servers_paths[]" 2>(if $nu.os-info.name == "windows" { "NUL" } else { "/dev/null" }) | sed 's/null//g')
    # [ -n "$list" ]  && for item_file in $list ; do _on_sops_item "$mode" "$item_file" "$target" ;  done
    # list=$($YQ -er < "$ORG_MAIN_SETTINGS_FILE" ".services_paths[]" 2> (if $nu.os-info.name == "windows" { "NUL" } else { "/dev/null" })| sed 's/null//g')
    # [ -n "$list" ]  && for item_file in $list ; do _on_sops_item "$mode" "$item_file" "$target" ;  done
}
export def edit_sop [
  items: list<string>
] {
    _print ""
    # [ -z "$PROVIISONING_USE_SOPS" ] && echo "🛑 No PROVIISONING_USE_SOPS value foud review environment settings or provisioning installation " && return 1
    # [ ! -r "$1" ] && echo "❗Error no file $1 found " && exit 1
    # if [ -z "$($YQ e '.sops' < "$1" 2>(if $nu.os-info.name == "windows" { "NUL" } else { "/dev/null" }) | sed 's/null//g')" }
    #     echo "❗File $1 not 'sops' signed with $PROVIISONING_USE_SOPS "
    #     exit

    # }
    # _check_sops
    # [ -z "$PROVIISONING_SOPS" ] && return 1
    # for it in $items {
    #     [ -r "$it" ] && sops "$it"
    # }
}
# TODO migrate all SOPS code from bash
export def is_sops_file [
    target: string
] {
    if not ($target | path exists) {
        (throw-error $"🛑 File (_ansi green_italic)($target)(_ansi reset)"
           $"(_ansi red_bold)Not found(_ansi reset)"
           $"is_sops_file ($target)"
            --span (metadata $target).span
        )

    }
    let file_sops = (open $target --raw )
    if ($file_sops | find "sops" | length) == 0 { return false }
    if ($file_sops | find "ENC[" | length) == 0 { return false }
    #let sops = ($file_sops | from json).sops? | default "")
    #($sops.mac? != null and $sops.mac != "")
    return true
}
export def decode_sops_file [
    source: string
    target: string
    quiet: bool
] {
  if $quiet {
    on_sops "decrypt" $source --quiet
  } else {
    on_sops "decrypt" $source
  }  | save --force $target
}

export def get_def_sops [
    current_path: string
] {
    let use_sops = (get-provisioning-use-sops)
    if ($use_sops | is-empty) { return ""}
    let start_path = if ($current_path | path exists) {
        $current_path
    } else {
        $"((get-workspace-path))/($current_path)"
    }
    let sops_file = "sops.yaml"
    # use ../lib_provisioning/utils/files.nu find_file
    mut provisioning_sops = (find_file $start_path $sops_file true )
    if $provisioning_sops == "" and ($env.HOME | path join ".config"| path join "provisioning" | path join $sops_file | path exists ) {
        $provisioning_sops = ($env.HOME | path join ".config"| path join "provisioning" | path join $sops_file )
    }
    if $provisioning_sops == "" and ($env.HOME | path join ".provisioning"| path join $sops_file | path exists ) {
       $provisioning_sops = ($env.HOME | path join ".provisioning"| path join $sops_file )
    }
    if $provisioning_sops == "" {
        _print $"❗Error no (_ansi red_bold)($sops_file)(_ansi reset) file for secure operations found "
        exit 1
    }
    ($provisioning_sops | default "")
}
export def get_def_age [
    current_path: string
] {
    # Check if SOPS is configured for age encryption
    let use_sops = (get-provisioning-use-sops | into string)
    if not ($use_sops | str contains "age") {
        return ""
    }
    let kage_file = ".kage"
    let start_path = if ($current_path | path exists) {
        $current_path
    } else {
        ((get-provisioning-infra-path) | path join $current_path)
    }
    #use utils/files.nu find_file
    let provisioning_kage = (find_file $start_path $kage_file true)
    let provisioning_kage = if $provisioning_kage == "" and ($env.HOME | path join ".config" | path join "provisioning "| path join $kage_file | path exists ) {
       ($env.HOME | path join ".config" | path join "provisioning "| path join $kage_file )
    } else {
       $provisioning_kage
    }
    let provisioning_kage = if $provisioning_kage == "" and ($env.HOME | path join ".provisioning "| path join $kage_file | path exists ) {
       ($env.HOME | path join ".provisioning "| path join $kage_file )
    } else {
       $provisioning_kage
    }
    let workspace_path = (get-workspace-path)
    let provisioning_kage = if $provisioning_kage == "" and ($workspace_path | is-not-empty) and (($workspace_path | path join ".provisioning" | path join $kage_file) | path exists ) {
       ($workspace_path | path join ".provisioning" | path join $kage_file )
    } else {
       $provisioning_kage
    }
    if $provisioning_kage == "" {
        _print $"❗Error no (_ansi red_bold)($kage_file)(_ansi reset) file for secure operations found "
        exit 1
    }
    ($provisioning_kage | default "")
}

# Return the SOPS config file path — env-var fast path, then filesystem search.
export def find-sops-key [] {
    let from_env = ($env.PROVISIONING_SOPS? | default "")
    if ($from_env | is-not-empty) { return $from_env }
    let search_path = ($env.CURRENT_KLOUD_PATH? | default ($env.PROVISIONING_WORKSPACE_PATH? | default $env.PWD))
    get_def_sops $search_path
}

# Return the age private-key file path used for SOPS encryption/decryption.
export def get-sops-age-key-file [] {
    $env.SOPS_AGE_KEY_FILE? | default ($env.PROVISIONING_KAGE? | default "")
}
