#!/bin/bash

printf2() { printf "$@" >&2 ; }
echo2()   { echo   "$@" >&2 ; }
read2()   { read   "$@" >&2 ; }
Indent()  { sed 's|^|    |' ; }

FolderTests() {
    local filename_head="$1"
    local testing="$2"             # yes or no

    if [ "$(basename "$PWD")" != "PKG_ARCHIVE" ] ; then
        echo2 "You seem to be in the wrong folder, should be PKG_ARCHIVE."
        return 4
    fi
    if [ -z "$(ls -1 ${filename_head}-*.zst 2>/dev/null)" ] ; then
        [ "$testing" = "yes" ] || echo2 "You seem to be in the wrong folder, package ${filename_head}-*.zst was not found."
        return 4
    fi
}

FileToTag() {
    local filename_head="$1"
    case "$filename_head" in
        welcome)            tag=packages ;;
        eos-pkgbuild-setup) tag=repo-testing ;;
    esac
}

Tag() {
    local available_tags
    local known_tag

    echo "Making sure we can reach the tags."
    logstuff on

    readarray -t available_tags < <(hub release)
    
    for known_tag in "${available_tags[@]}" ; do
        [ "$tag" = "$known_tag" ] && break
    done

    case "$tag" in
        "$known_tag")
            case "$tag" in
                packages)
                    FolderTests welcome no || return $?
                    echo2 "Using tag: $tag"
                    return 0
                    ;;
                repo-testing)
                    FolderTests eos-pkgbuild-setup no || return $?
                    echo2 "Using tag: $tag"
                    return 0
                    ;;
                *)
                    echo2 "Detected tags: ${available_tags[*]}"
                    read2 -p "Use tag '$tag'? [Y/n] "
                    case "$REPLY" in
                        "" | [yY]*) ;;
                        *) return 1 ;;
                    esac
                    ;;
            esac
            ;;
        "")
            local xx
            for xx in eos-pkgbuild-setup welcome ; do
                if FolderTests $xx yes ; then
                    FileToTag $xx
                    printf2 "Based on existing file(s) ${xx}-*.zst using tag: $tag\n"
                    return 0
                fi
            done
            echo2 "Please give a tag. Available tags:" 
            printf2 "    %s\n" "${available_tags[@]}"
            return 2
            ;;
        *)
            echo2 "Unknown tag '$tag'. Available tags:"
            printf2 "    %s\n" "${available_tags[@]}"
            return 3
            ;;
    esac
}

Options() {
    local progname="${0##*/}"
    local opts

    opts="$(/usr/bin/getopt -o=hd --longoptions help,dryrun --name "$progname" -- "$@")" || {
        Options -h
        return 1
    }

    eval set -- "$opts"

    while true ; do
        case "$1" in
            -d | --dryrun)
                dryrun=yes
                ;;

            -h | --help)
                cat <<EOF >&2
Usage: $progname [options] tag
Options:
  -d, --dryrun       Simulate run, don't change anything permanently.
Tag:                 Release assets tag at github.
EOF
                ;;
            
            --) shift ; break ;;
        esac
        shift
    done

    tag="$1"
}

AddsAndRemoves() {
    # combine local and remote, keep 5 latest of each file version
    local locals_and_remotes=$(printf "%s\n" "$locals" "$remotes" | grep -v "\.sig$" | sort -Vu)
    local file fileprev=""
    local pkgname pkgnameprev=""
    local files=()
    local files_to_keep=()
    local files_to_remove=()
    local -r breaker="[A-DUMMY-BREAKER]"
    local count

    for file in $locals_and_remotes "$breaker" ; do
        [ "$file" = "$breaker" ]  && break       # all files checked
        # [ "$file" = "$fileprev" ] && continue    # remove duplicates (commented because of 'sort -Vu' above)

        fileprev="$file"

        pkgname=$(pkg-name-components N "$file")
        [ -z "$pkgnameprev" ] && pkgnameprev="$pkgname"

        case "$pkgname" in
            "$pkgnameprev")
                files+=("$file")
                ;;
            *) 
                # pkgname changed, files full
                files_to_keep+=($(printf "%s\n" "${files[@]}" | tail -n$keep))
                count=${#files[@]}
                if [ $count -gt $keep ] ; then
                    files_to_remove+=($(printf "%s\n" "${files[@]}" | head -n$((count-keep))))
                fi

                # prepare for the next file
                pkgnameprev="$pkgname"
                files=("$file")
        esac
    done

    # now we know which files to remove from local and remote, and keep

    local add_remote=()
    local remove_remote=()
    local add_local=()
    local remove_local=()

    printf2 "\n===> Managing archived files >>>>>>>>>>>>>\n\n"

    # Note: handle .sig files now.

    for file in "${files_to_keep[@]}" ; do
        if [ -z "$(echo "$remotes" | grep "$file")" ] ; then
            add_remote+=("$file" "$file.sig")
        fi

        if [ -z "$(echo "$locals" | grep "$file")" ] ; then
            add_local+=("$file" "$file.sig")
        fi
    done
    for file in "${files_to_remove[@]}" ; do
        if [ -n "$(echo "$remotes" | grep "$file")" ] ; then
            remove_remote+=("$file" "$file.sig")
        fi

        if [ -n "$(echo "$locals" | grep "$file")" ] ; then
            remove_local+=("$file" "$file.sig")
        fi
    done

    ShowFileInfo "additions to local"   "${add_local[@]}"
    ShowFileInfo "additions to remote"  "${add_remote[@]}"
    ShowFileInfo "removals from local"  "${remove_local[@]}"
    ShowFileInfo "removals from remote" "${remove_remote[@]}"

    if [ "$dryrun" = no ] ; then
        local arr=() remo renamed_to_remo=()
        local handle_chmods=no

        case "$(/bin/ls -ld .)" in
            "dr-x"*) handle_chmods=yes; chmod u+w . ;;
        esac

        # local adds
        if [ ${#add_local[@]} -gt 0 ] ; then
            for remo in "${add_local[@]}" ; do
                ## local file name may include : or +, so convert them for github
                case "$remo" in
                    *.sig) ;;    # download-release-assets will get this too!
                    *) 
                        download-release-assets "$tag" "$remo*" || return $?   # remove * from $remo* ??
                        sleep 1
                        ;;
                esac
            done
        fi

        # remote adds
        if [ ${#add_remote[@]} -gt 0 ] ; then
            # rename local name with :/+ to COLON/PLUS for remote
            arr=()
            for file in "${add_remote[@]}" ; do
                remo=${file/:/COLON}
                remo=${remo/+/PLUS}
                arr+=("$remo")
                if [ "$file" != "$remo" ] ; then
                    cp "$file" "$remo"
                    renamed_to_remo+=("$remo")
                fi
            done
            add-release-assets "$tag" "${arr[@]}" || return $?
            rm -f "${renamed_to_remo[@]}"
            sleep 1
        fi

        # local removes
        if [ ${#remove_local[@]} -gt 0 ] ; then
            rm -f "${remove_local[@]}"
        fi

        # remote removes
        if [ ${#remove_remote[@]} -gt 0 ] ; then
            arr=()
            # rename :/+ to COLON/PLUS
            for file in "${remove_remote[@]}" ; do
                remo=${file/:/COLON}
                remo=${remo/+/PLUS}
                arr+=("$remo")
            done
            delete-release-assets --quietly "$tag" "${arr[@]}" || return $?
            sleep 1
        fi
    fi
    [ "$handle_chmods" = yes ] && chmod u-w .
    printf2 "\n<<<<<<<<<< Done. <===============\n\n"
}

ShowFileInfo() {
    local msg="$1"
    shift
    local count="$#"

    printf2 "%-5d %s\n" "$count"   "$msg"
    [ $count -gt 0 ] && printf "%s\n" "$@" | Indent >&2
}

FilterPackages() { grep -P '\.xz$|\.zst$' ; }    # Note: exclude *.sig files for now.

Main() {
    local tag=""
    local dryrun=no
    local keep=5              # keeps max 5 latest of each package
    local locals=""
    local remotes=""

    Options "$@"

    Tag || return $?

    locals=$(/usr/bin/ls -1 | FilterPackages)
    remotes=$(release-asset-names "$tag" | FilterPackages | sed -e 's|COLON|:|g' -e 's|PLUS|+|')

    AddsAndRemoves || return $?
}

Main "$@"
