# shellcheck shell=sh
# shellcheck disable=3043,2154

___x_cmd_gh_secret(){
    param:scope  ___x_cmd_github
    param:subcmd ___x_cmd_gh_secret                     \
        ls               "List secrets"                 \
        set              "Create or Update secrets"     \
        rm               "Remove secrets"
    param:subcmd:try
    param:run

    ___x_cmd_gh_secret _param_help_doc
    return 1
}

# Section: List
# https://docs.github.com/en/rest/actions/secrets#list-organization-secrets
# https://docs.github.com/en/rest/actions/secrets#list-selected-repositories-for-an-organization-secret
# https://docs.github.com/en/rest/actions/secrets#list-repository-secrets
___x_cmd_gh_secret_ls(){
    param:scope ___x_cmd_github
    param:dsl <<A
options:
    --app           "List secrets for a specific application"                               <>="actions" = actions codespaces dependabot
    --repo|-r       "<owner_path>/<repo_path>"                                              <>:RepoName=""
    --org           "The organization name. The name is not case sensitive"                 <>=""
    --secret_name   "Filter by organization secret name to list selected repos"             <>=""
    --page          "Page number of the results to fetch."                                  <>:Numbers="1"
    --per_page      "Results per page (max 100)"                                            <>:Per_page="30"

    --json|-j       "output origin json data"
    --csv           "output csv data"
    --yml           "output yml data"
A
     param:run
    [ -n "$repo" ] || [ -n "$org" ] || M='Please provide at least option --repo <owner/repo> or --org <org_name>' arg:ret:64
    [ "$app" != "codespace"  ] || [ -z "$org" ] || {
        gh:error "Organization NOT provide codespace secrets"
        return 1
    }

    if [ -n "$secret_name" ]; then
        [ -n "$org" ] || M='accepts option --org <org_path>, received empty' arg:ret:64
        ___x_cmd_gh_secret_ls___filter_secret_name_for_org
    elif [ -n "$repo" ]; then
        ___x_cmd_gh_param_init_owner_repo
        ___x_cmd_gh_secret_ls___for_repo
    else
        ___x_cmd_gh_secret_ls___for_org
    fi
}

___x_cmd_gh_secret_ls___for_repo(){
    if ___x_cmd_gx_output_is_format; then
        ___x_cmd_gh_get_multi "/repos/${owner_repo}/${app}/secrets" | ___x_cmd_gx_output_format
    else
        ___x_cmd_gh_get_multi "/repos/${owner_repo}/${app}/secrets" | x jo .secrets | \
            x jo 2c            .name   .created_at .updated_at | \
            x csv header --add  Name    Created_At  Updated_At | \
            if [ -n "$csv" ]; then  ___x_cmd_cmds cat
            else                    x csv static_tab
            fi
    fi
}
___x_cmd_gh_secret_ls___for_org(){
    if ___x_cmd_gx_output_is_format; then
        ___x_cmd_gh_get_multi "/orgs/${org}/${app}/secrets" | ___x_cmd_gx_output_format
    else
        ___x_cmd_gh_get_multi "/orgs/${org}/${app}/secrets" | x jo .secrets | \
            x jo 2c            .name   .visibility .created_at .updated_at | \
            x csv header --add  Name    Visibility  Created_At  Updated_At | \
            if [ -n "$csv" ]; then  ___x_cmd_cmds cat
            else                    x csv static_tab
            fi
    fi
}
___x_cmd_gh_secret_ls___filter_secret_name_for_org(){
    if ___x_cmd_gx_output_is_format; then
        ___x_cmd_gh_get_multi "/repos/${owner_repo}/${app}/secrets" | ___x_cmd_gx_output_format
    else
        ___x_cmd_gh_get_multi "/repos/${owner_repo}/${app}/secrets" | x jo .repositories | \
            x jo 2c            .id      .full_name  | \
            x csv header --add Repo_ID  Repo        | \
            if [ -n "$csv" ]; then  ___x_cmd_cmds cat
            else                    x csv static_tab
            fi
    fi
}
# EndSection

# Section: Set
___x_cmd_gh_secret_set(){
    param:scope ___x_cmd_github
    param:dsl <<A
options:
    #1              "Target secrets. <secret_name>=<secret_value>"                                      <>=""
    --file|-f       "Load secret names and values from a dotenv-formatted file"                         <>=""
    --app           "Set the application for a secret"                                                  <>="actions" = actions codespaces dependabot
    --repo|-r       "<owner_path>/<repo_path>"                                                          <>:RepoName=""
    --org           "The organization name"                                                             <>=""
    --visibility    "Set visibility for an organization secret"                                         <>="private" = all private selected
A
    param:run
    [ "$#" -ne 0 ] || [ -n "$file" ] || {
        gh:error "Please provide at least one secret arg(s) '<secret_name>=<secret_value>' or '--file <dotenv_formatted_file_path>'"
        return 64
    }
    [ -n "$repo" ] || [ -n "$org" ] || {
        gh:error "Please provide at least one option --repo <owner/repo> or --org <org_name>"
        return 64
    }
    if [ -n "$repo" ]; then
        [ -z "$org" ] || {
            gh:error "only one of --repo <owner/repo> or --org <org_name> can be passed"
            return 64
        }
        ___x_cmd_gh_param_init_owner_repo
    fi

    [ "$app" != "codespace"  ] || [ -z "$org" ] || {
        gh:error "Organization NOT provide codespace secrets";
        return 1;
    }

    local __item=""
    if [ -n "$file" ]; then
        [ -f "$file" ] || { gh:error "Not fonund file $file"; return 1; }
        local IFS="$___X_CMD_UNSEENCHAR_NEWLINE"
        ( ___x_cmd_cmds_cat "$file" && printf "\n" ) | while read -r __item; do
            [ -z "$__item" ] || ___x_cmd_gh_secret_set____main "${__item%%=*}" "${__item#*=}" || return
        done
    fi
    if [ "$#" -ne 0 ]; then
        for __item in "$@"; do
            ___x_cmd_gh_secret_set____main "${__item%%=*}" "${__item#*=}" || return
        done
    fi
}

# https://docs.github.com/en/rest/actions/secrets#create-or-update-an-organization-secret
# https://docs.github.com/en/rest/actions/secrets#create-or-update-a-repository-secret
___x_cmd_gh_secret_set____main(){
    [ -n "$1" ] || M='accepts the frist arg (secret name), received empty' arg:ret:64
    [ -n "$2" ] || M='accepts the second arg (secret value), received empty' arg:ret:64
    local name="$1"
    local encrypted_value="$2"
    local key_id=
    local pub_key=
    x jo env . key_id=.key_id  pub_key=.key <<A
        $(___x_cmd_gh_secret_set____get_public_key)
A
    [ -n "$key_id" ] || { gh:error "Fetch ${owner_repo:-$org} secret public key failure"; return 1; }
    encrypted_value="$(___x_cmd_gh_secret_set____encrypt_by_public_key "$encrypted_value" "$pub_key")" || { gh:error "Encrypt $name secret value failure"; return 1; }

    local _url="/repos/${owner_repo}/${app}/secrets/${name}"
    [ -z "$org" ] || { _url="/orgs/${org}/${app}/secrets/${name}"; }

    # key_id must String
    x jo dict key_id:"$key_id" encrypted_value ${org:+"visibility"} | ___x_cmd_gh_curl put "$_url" "@-" | (
            [ -z "$___X_CMD_GH_IN_TEST" ] || { ___x_cmd_cmds_cat; return; }
            x jo env . gh_resp_msg=.message gh_resp_err=.errors
            if ___x_cmd_gh_http_error; then
                ___x_cmd_ui_tf  true "[Success]: Setting ${name} secret to ${owner_repo:-$org} ${app}"
            else
                ___x_cmd_ui_tf false "Setting ${name} secret to ${owner_repo:-$org} ${app} failure:" >&2
                ___x_cmd_gh____handle_resp
                return 1
            fi
        )
}

# https://docs.github.com/en/rest/actions/secrets#get-an-organization-public-key
# https://docs.github.com/en/rest/actions/secrets#get-a-repository-public-key
___x_cmd_gh_secret_set____get_public_key(){
    if [ -n "$org" ]; then
        ___x_cmd_gh_curl get "/orgs/${org}/${app}/secrets/public-key"
    else
        ___x_cmd_gh_curl get "/repos/${owner_repo}/${app}/secrets/public-key"
    fi
}

# TODO: sodium encrypt have prefix 'cipher: '
___x_cmd_gh_secret_set____encrypt_by_public_key(){
    [ -n "$1" ] || M='accepts the frist arg(encrypt value), received empty' arg:ret:64
    [ -n "$2" ] || M='accepts the second arg(public key), received empty' arg:ret:64
    local __encrypt=""
    __encrypt="$(x sodium sealedbox_encrypt "$1" "$2")" || return 1
    printf "%s" "${__encrypt#*'cipher: '}"
    # x sodium sealedbox_encrypt "$1" "$2"
}
# EndSection

# Section: Remove
# https://docs.github.com/en/rest/actions/secrets#delete-a-repository-secret
# https://docs.github.com/en/rest/actions/secrets#delete-an-organization-secret
___x_cmd_gh_secret_rm(){
    param:scope     ___x_cmd_github
    param:dsl       '
options:
    #1          "The secret names"                              <>
    --app       "Target the application for a secret"           <>="actions" = actions codespaces dependabot
    --repo|-r   "<owner_path>/<repo_path>"                      <>:RepoName=""
    --org       "The organization name"                         <>=""
    --yes|-y    "Ignore remove prompt interception"
'
    param:run
    [ -n "$repo" ] || [ -n "$org" ] || {
        gh:error "Please provide at least one option --repo <owner/repo> or --org <org_name>"
        return 64
    }

    [ "$app" != "codespace"  ] || [ -z "$org" ] || {
        gh:error "Organization NOT provide codespace secrets";
        return 1;
    }

    local _item=""; for _item in "$@"; do
        local _url="/orgs/${org}/${app}/secrets/${_item}"
        [ -z "$repo" ] || {
            ___x_cmd_gh_param_init_owner_repo
            _url="/repos/${owner_repo}/${app}/secrets/${_item}"
        }

        [ "$yes" = "true" ] || ___x_cmd_ui_yesno "Are you sure you want to remove secret $(___x_cmd_ui bold red "$_item") on ${owner_repo:-$org} ${app}?" || continue
        ___x_cmd_gh_curl del "${_url}" | (
                [ -z "$___X_CMD_GH_IN_TEST" ] || { ___x_cmd_cmds_cat; return; }
                x jo env . gh_resp_msg=.message gh_resp_err=.errors
                if ___x_cmd_gh_http_error; then
                    ___x_cmd_ui_tf  true "[Success]: Remove ${_item} secret on ${owner_repo:-$org} ${app}"
                else
                    ___x_cmd_ui_tf false "Remove ${_item} secret on ${owner_repo:-$org} ${app} failure:" >&2
                    ___x_cmd_gh____handle_resp
                    return 1
                fi
            )
    done
}
# EndSection
