#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS json

function display_help() {
    uecho "$scriptname: piloter un serveur git (gitolite, gogs, gitea, etc.)

USAGE
    $scriptname ACTION URL [options]

ACTIONS
    u|update-origin URL
        Mettre à jour origin dans le dépôt courant avec l'url spécifié

    c|create URL [description]
        Créer un nouveau dépôt avec la description spécifiée
        OPTIONS
            -u, --update-origin
                mettre à jour origin dans le dépôt courant avec l'url du dépôt
                nouvellement créé, comme avec l'action update-origin

    l|list URL [VARs...]
        Lister les dépôts dans l'organisation spécifiée. Si aucune organisation
        n'est spécifiée dans l'url, lister les dépôts *accessibles* par
        l'utilisateur (cela inclut les dépôts des organisations auxquelles
        l'utilisateur a accès)
        VARs est une liste de variables à afficher pour chaque dépôt, séparés
        par le caractère tabulation. La valeur par défaut est full_name

    g|get URL [VARs...]
        Afficher les propriétés du dépôt spécifié. VARs est une liste de
        variables à afficher pour le dépôt, séparés par le caractère tabulation.

    e|edit URL var=value...
        Modifier les propriétés du dépôt. Consulter l'API pour la liste exacte
        des propriétés pouvant être modifiées. Avec gitea 1.9.3, il y a au moins
        celles-là:
          name
          description
          website
          private
          default_branch

    d|delete URL
        Supprimer le dépôt spécifié"
}

function repoctl_init() {
    repourl="${1%.git}"
    [ -n "$repourl" ] || return
    rname=
    rtype=gitolite
    rprefix=

    REPO_PREFIXES=()
    REPO_TYPES=()
    set_defaults repoctl

    # Traduire les aliases éventuels
    local asrcdest asrc adest
    for asrcdest in "${REPO_PREFIXES[@]}"; do
        splitfsep "$asrcdest" = asrc adest
        if [ "${repourl#$asrc}" != "$repourl" ]; then
            newurl="$adest${repourl#$asrc}"
            if [ "$newurl" != "$repourl" ]; then
                enote "$repourl --> $newurl"
                repourl="$newurl"
                break
            fi
        fi
    done

    local rnametypeprefix tmp found
    for rnametypeprefix in "${REPO_TYPES[@]}"; do
        splitfsep "$rnametypeprefix" : rname tmp
        splitfsep "$tmp" : rtype rprefix
        if [ "${repourl#$rprefix}" != "$repourl" ]; then
            found=1
            break
        fi
    done
    if [ -z "$found" ]; then
        rname=
        rtype=gitolite
        rprefix=
    fi
}

function curlto() {
    local url="$1"; shift
    local payload="$1"; shift
    local outfile="$1"; shift
    local tmpfile
    if [ -z "$outfile" ]; then
        ac_set_tmpfile tmpfile
        outfile="$tmpfile"
    fi

    local -a args
    local r http_code
    args=(-s -w '%{http_code}' -o "$outfile")
    [ -n "$payload" ] && args+=(-d "$payload")
    [ -n "$HTTP_METHOD" ] && args+=(-X "$HTTP_METHOD")
    args+=("$@" "$url")
    setx http_code=curl "${args[@]}"

    case "$http_code" in
    2*) r=0;;
    4*) r=1;;
    5*) r=3;;
    *) r=11;;
    esac
    if [ -n "$tmpfile" ]; then
        cat "$tmpfile"
        ac_clean "$tmpfile"
    fi

    upvar http_code "$http_code"
    return "$r"
}

function gogs_setvars() {
    gogs_url="${rname}_GOGS_URL"; gogs_url="${!gogs_url}"
    gogs_user="${rname}_GOGS_USER"; gogs_user="${!gogs_user}"
    gogs_key="${rname}_GOGS_KEY"; gogs_key="${!gogs_key}"
    userpath="${repourl#$rprefix}"
    splitfsep "$userpath" / user path
}

################################################################################

function _update_origin() {
    estep "Mise à jour de l'origine"
    git remote set-url origin "$repourl"
}
function _update_origin_push() {
    if ask_yesno "Faut-il faire git push?" O; then
        git push --all && git push --tags
    else
        enote "Ne pas oublier de faire 'git push --all && git push --tags'"
    fi
}
function _update_origin_pull() {
    if ask_yesno "Faut-il faire git pull?" O; then
        git pull
    else
        enote "Ne pas oublier de faire 'git pull'"
    fi
}

function update_origin_action() {
    case "$rtype" in
    #gitolite) ;;
    gogs|gitea)
        gogs_setvars
        gogs_update_origin_action "$@"
        ;;
    *) die "$rtype: type de dépôt non supporté";;
    esac
}
function gogs_update_origin_action() {
    local repourl
    if [ -n "$user" -a "$user" != "$gogs_user" ]; then
        # dépôt d'une organisation
        repourl="$gogs_url/$user/$path"
    else
        # dépôt d'un utilisateur
        repourl="$gogs_url/$gogs_user/$path"
    fi
    _update_origin
    _update_origin_pull
}

################################################################################

function create_action() {
    case "$rtype" in
    #gitolite) ;;
    gogs|gitea)
        gogs_setvars
        gogs_create_action "$@"
        ;;
    *) die "$rtype: type de dépôt non supporté";;
    esac
}
function gogs_create_action() {
    local update_origin="$1"; shift

    local url repourl desc payload result
    local -a vars
    if [ -n "$user" -a "$user" != "$gogs_user" ]; then
        # créer un dépôt dans une organisation
        url="$gogs_url/api/v1/org/$user/repos"
        repourl="$gogs_url/$user/$path"
    else
        # créer un dépôt pour un utilisateur
        url="$gogs_url/api/v1/user/repos"
        repourl="$gogs_url/$gogs_user/$path"
    fi
    vars=(name="$path" private=true)
    [ -n "$1" ] && vars+=(description="$1"); shift
    setx payload=json_build "${vars[@]}"

    [ $# -gt 0 ] && vars=("$@") || vars=("")
    setx result=curlto "$url" "$payload" "" \
         -H 'Content-Type: application/json' \
         -H "Authorization: token $gogs_key" || \
        die "Une erreur s'est produite lors de la tentative de création du dépôt
url: $url
payload: $payload
result: $result"
    isatty && estep "Création du dépôt $repourl"
    echo "$result" | json_get "${vars[@]}"

    if [ -n "$update_origin" ]; then
        _update_origin
        _update_origin_push
    fi
}

################################################################################

function list_action() {
    case "$rtype" in
    #gitolite) ;;
    gogs|gitea)
        gogs_setvars
        gogs_list_action "$@"
        ;;
    *) die "$rtype: type de dépôt non supporté";;
    esac
}
function gogs_list_action() {
    local url result
    local -a vars
    if [ -n "$user" -a "$user" != "$gogs_user" ]; then
        # lister les dépôts d'une organisation
        url="$gogs_url/api/v1/orgs/$user/repos"
    else
        # lister les dépôts accessibles par l'utilisateur
        url="$gogs_url/api/v1/user/repos"
    fi

    [ $# -gt 0 ] && vars=("$@") || vars=(full_name)
    setx result=curlto "$url" "" "" \
         -H 'Content-Type: application/json' \
         -H "Authorization: token $gogs_key" || \
        die "Une erreur s'est produite lors de la tentative de listage des dépôts
url: $url
result: $result"
    echo "$result" | json_each "${vars[@]}"
}

################################################################################

function get_action() {
    case "$rtype" in
    #gitolite) ;;
    gogs|gitea)
        gogs_setvars
        gogs_get_action "$@"
        ;;
    *) die "$rtype: type de dépôt non supporté";;
    esac
}
function gogs_get_action() {
    local url repourl payload result
    local -a vars
    url="$gogs_url/api/v1/repos/$user/$path"
    repourl="$gogs_url/$user/$path"

    [ $# -gt 0 ] && vars=("$@") || vars=("")
    local HTTP_METHOD=GET
    setx result=curlto "$url" "" "" \
         -H 'Content-Type: application/json' \
         -H "Authorization: token $gogs_key" || \
        die "Une erreur s'est produite lors de la tentative de déplacement du dépôt
url: $url
payload: $payload
result: $result"
    isatty && estep "Attributs du dépôt $repourl"
    echo "$result" | json_get "${vars[@]}"
}

################################################################################

function edit_action() {
    case "$rtype" in
    #gitolite) ;;
    gogs|gitea)
        gogs_setvars
        gogs_edit_action "$@"
        ;;
    *) die "$rtype: type de dépôt non supporté";;
    esac
}
function gogs_edit_action() {
    local url repourl payload result
    local -a vars
    url="$gogs_url/api/v1/repos/$user/$path"
    repourl="$gogs_url/$user/$path"
    vars=()
    while [[ "$1" == *=* ]]; do
        vars+=("$1")
        shift
    done
    setx payload=json_build "${vars[@]}"

    [ $# -gt 0 ] && vars=("$@") || vars=("")
    local HTTP_METHOD=PATCH
    setx result=curlto "$url" "$payload" "" \
         -H 'Content-Type: application/json' \
         -H "Authorization: token $gogs_key" || \
        die "Une erreur s'est produite lors de la tentative de déplacement du dépôt
url: $url
payload: $payload
result: $result"
    isatty && estep "Mise à jour du dépôt $repourl"
    echo "$result" | json_get "${vars[@]}"
}

################################################################################

function delete_action() {
    case "$rtype" in
    #gitolite) ;;
    gogs|gitea)
        gogs_setvars
        gogs_delete_action "$@"
        ;;
    *) die "$rtype: type de dépôt non supporté";;
    esac
}
function gogs_delete_action() {
    local url repourl payload result
    url="$gogs_url/api/v1/repos/$user/$path"
    repourl="$gogs_url/$user/$path"

    local HTTP_METHOD=DELETE
    setx result=curlto "$url" "" "" \
         -H 'Content-Type: application/json' \
         -H "Authorization: token $gogs_key" || \
        die "Une erreur s'est produite lors de la tentative de suppression du dépôt
url: $url
payload: $payload
result: $result"
    isatty && estep "Suppression du dépôt $repourl"
    [ -n "$result" ] && echo "$result"
    return 0
}

################################################################################

action=
update_origin=
args=(
    --help '$exit_with display_help'
    -c,--create action=create
    -u,--update,--update-origin update_origin=1
    -l,--list action=list
    -g,--get action=get
    -e,--edit action=edit
    -d,--delete action=delete
)
parse_args "$@"; set -- "${args[@]}"

if [ -z "$action" -a -n "$update_origin" ]; then
    case "$1" in
    c|create) action=create; shift;;
    *) action=update-origin;;
    esac
fi
if [ -z "$action" ]; then
    action="$1"; shift
fi
[ -n "$action" ] || action=list

repoctl_init "$1"; shift
[ -n "$repourl" ] || die "Vous devez spécifier l'url du dépôt"

case "$action" in
u|update|update-origin) update_origin_action "$@";;
c|create) create_action "$update_origin" "$@";;
l|list) list_action "$@";;
g|get|s|show) get_action "$@";;
e|edit) edit_action "$@";;
d|del|delete|rm|remove) delete_action "$@";;
esac