#!/bin/bash
# Derived from https://github.com/docker-library/postgres/blob/master/update.sh
set -Eeuo pipefail

cd "$(dirname "$(readlink -f "$BASH_SOURCE")")"

versions=( "$@" )
if [ ${#versions[@]} -eq 0 ]; then
	versions=( */Dockerfile )
fi
versions=( "${versions[@]%/Dockerfile}" )

for optimized in debian alpine test; do
    rm -f      _dockerlists_${optimized}.md
    echo "<!-- This part is automatically generated by ./update.sh. Do not modify this section manually. -->" > _dockerlists_${optimized}.md
    echo "| DockerHub image | Dockerfile | OS | Postgres | PostGIS |" >> _dockerlists_${optimized}.md
    echo "| --------------- | ---------- | -- | -------- | ------- |" >> _dockerlists_${optimized}.md
done

autogenerated='NOTE: THIS DOCKERFILE IS GENERATED VIA "make update"! PLEASE DO NOT EDIT IT DIRECTLY.'

dockerhublink="https://registry.hub.docker.com/r/postgis/postgis/tags?page=1&name="
githubrepolink="https://github.com/postgis/docker-postgis/blob/master"

# sort version numbers with highest last (so it goes first in .travis.yml)
IFS=$'\n'; versions=( $(echo "${versions[*]}" | sort -V) ); unset IFS

defaultAlpinenSuite='3.22'
defaultDebianSuite='bullseye-slim'
declare -A debianSuite=(
    # https://github.com/docker-library/postgres/issues/582
    [11]='bullseye-slim'
    [12]='bullseye-slim'
    [13]='bullseye-slim'
    [14]='bullseye-slim'
    [15]='bullseye-slim'
    [16]='bullseye-slim'
    [17]='bullseye-slim'
    [18]='trixie-slim'
)

defaultPostgisDebPkgNameVersionSuffix='3'
declare -A postgisDebPkgNameVersionSuffixes=(
    [3.0]='3'
    [3.1]='3'
    [3.2]='3'
    [3.3]='3'
    [3.4]='3'
    [3.5]='3'
    [3.6]='3'
)

packagesBase='http://apt.postgresql.org/pub/repos/apt/dists/'

cgalGitHash="$(git ls-remote https://github.com/CGAL/cgal.git heads/master | awk '{ print $1}')"
sfcgalGitHash="$(git ls-remote https://gitlab.com/SFCGAL/SFCGAL.git heads/master | awk '{ print $1}')"
projGitHash="$(git ls-remote https://github.com/OSGeo/PROJ.git heads/master | awk '{ print $1}')"
gdalGitHash="$(git ls-remote https://github.com/OSGeo/gdal.git refs/heads/master | grep '\srefs/heads/master' | awk '{ print $1}')"
geosGitHash="$(git ls-remote https://github.com/libgeos/geos.git heads/main | awk '{ print $1}')"
postgisGitHash="$(git ls-remote https://github.com/postgis/postgis.git heads/master | awk '{ print $1}')"

#-------------------------------------------
# Correct version sorting
function version_reverse_sort() {
    # This function sorts version numbers in reverse order,
    # ensuring that versions without pre-release tags (e.g., "3.4.0")
    # are ranked higher than those with pre-release tags (e.g., "3.4.0rc4").
    # It adds a ".9991" suffix to versions without pre-release tags,
    # sorts them with `sort -Vr`, and then removes the ".9991" suffix.
    sed -r "s/([0-9]+\.[0-9]+\.[0-9]+$)/\1\.9991/" | sort -Vr | sed s/\.9991$//
}

api_preference="github"
function fetch_postgis_versions() {
    # get all postgis versions from github
    local REPO="postgis/postgis"
    local PER_PAGE=100 # You can ask for up to 100 results per page
    local page=1
    postgis_all_v3_versions=""

    while true; do
        local response
        if [ "$api_preference" == "github" ]; then
            response=$(curl --silent "https://api.github.com/repos/$REPO/tags?per_page=$PER_PAGE&page=$page") || {
                echo "Failed to fetch postgis_versions from api.github.com/repos/$REPO/tags"
                return 1
            }
        elif [ "$api_preference" == "osgeo" ]; then
            response=$(curl --silent "https://git.osgeo.org/gitea/api/v1/repos/${REPO}/tags?page=$page&limit=$PER_PAGE") || {
                echo "Failed to fetch postgis_versions from git.osgeo.org/gitea/api/v1/repos/${REPO}/tags"
                return 1
            }
        fi

        # Check for rate limit exceeded error - related to api.github.com
        if echo "$response" | grep -q "API rate limit exceeded"; then
            echo "Error: API rate limit exceeded!"
            echo "$response"
            exit 1
        fi

        # Extract tag names from the JSON response
        local tags
        tags=$(echo "$response" | grep -Po '"name":\s*"\K[^"]+' || true)
        local count
        count=$(echo "$tags" | sed '/^$/d' | wc -l)

        if ((count == 0)); then
            break
        fi

        if ((page > 12)); then
            echo "Too many pages: ${page} - exiting; unexpected and something is wrong!"
            exit 1
        fi

        postgis_all_v3_versions+=" $tags"

        ((page++))
    done
}

fetch_postgis_versions || {
    echo "Error fetching postgis versions! Maybe network or server error!"
    exit 1
}

# Keep 3.* versions only
postgis_all_v3_versions=$(echo "$postgis_all_v3_versions" | sed '/^$/d' | grep '^3\.' | version_reverse_sort)
postgis_all_v3_versions_array_string=$(echo "$postgis_all_v3_versions" | tr '\n' ' ')
echo "postgis_all_v3_versions_array_string = ${postgis_all_v3_versions_array_string}"
echo " "

#-------------------------------------------

declare -A suitePackageList=() suiteArches=()
for version in "${versions[@]}"; do
    IFS=- read postgresVersion postgisVersion <<< "$version"

    echo " "
    echo "---- generate Dockerfile for $version ----"
    echo "postgresVersion=$postgresVersion"
    echo "postgisVersion=$postgisVersion"

    if [ "2.5" == "$postgisVersion" ]; then
        # posgis 2.5 only in the stretch ; no bullseye version
        tag='stretch-slim'
    else
        tag="${debianSuite[$postgresVersion]:-$defaultDebianSuite}"
    fi
    suite="${tag%%-slim}"

    if [ -z "${suitePackageList["$suite"]:+isset}" ]; then
        suitePackageList["$suite"]="$(curl -fsSL "${packagesBase}/${suite}-pgdg/main/binary-amd64/Packages.bz2" | bunzip2)"
    fi
    if [ -z "${suiteArches["$suite"]:+isset}" ]; then
        suiteArches["$suite"]="$(curl -fsSL "${packagesBase}/${suite}-pgdg/Release" | awk -F ':[[:space:]]+' '$1 == "Architectures" { gsub(/[[:space:]]+/, "|", $2); print $2 }')"
    fi

    postgresVersionMain="$(echo "$postgresVersion" | awk -F 'alpha|beta|rc' '{print $1}')"
    versionList="$(echo "${suitePackageList["$suite"]}"; curl -fsSL "${packagesBase}/${suite}-pgdg/${postgresVersionMain}/binary-amd64/Packages.bz2" | bunzip2)"
    fullVersion="$(echo "$versionList" | awk -F ': ' '$1 == "Package" { pkg = $2 } $1 == "Version" && pkg == "postgresql-'"$postgresVersionMain"'" { print $2; exit }' || true)"

    if [ "$suite" = "bullseye" ]; then
        boostVersion="1.74.0"
    elif [ "$suite" = "trixie" ]; then
        boostVersion="1.88.0"
    else
        echo "Unknown debian version; stop"
        exit 1
    fi


    optimized=""
    if [[ "$version" =~ "alpha" || "$version" =~ "beta" || "$version" =~ "rc" || "$version" =~ "master" ]];
    then
        optimized="test"
    else
        optimized="debian"
    fi
    echo "optimized=$optimized"

    debianPostgisMajMin=""
    if [ "master" == "$postgisVersion" ]; then
        postgisPackageName=""
        postgisFullVersion="$postgisVersion"
        postgisMajor=""
        postgisDocSrc="development: postgis, geos, proj, gdal"
    else
        postgisMajMin="$( echo "${postgisVersion}" | cut -d. -f1 ).$( echo "${postgisVersion}" | cut -d. -f2 )"
        echo "postgisMajMin=${postgisMajMin}"

        postgisPackageName="postgresql-${postgresVersionMain}-postgis-${postgisDebPkgNameVersionSuffixes[${postgisMajMin}]}"
        postgisFullVersion="$(echo "$versionList" | awk -F ': ' '$1 == "Package" { pkg = $2 } $1 == "Version" && pkg == "'"$postgisPackageName"'" { print $2; exit }' || true)"
        echo "postgisPackageName=${postgisPackageName}"
        echo "postgisFullVersion=${postgisFullVersion}"

        debianPostgisMajMin="$( echo "${postgisFullVersion}" | cut -d. -f1 ).$( echo "${postgisFullVersion}" | cut -d. -f2 )"

        if [ "$debianPostgisMajMin" == "$postgisMajMin" ]; then
            echo "debian postgis version is OK "
            postgisMajor="${postgisDebPkgNameVersionSuffixes[${postgisMajMin}]}"
            postgisDocSrc="${postgisFullVersion%%+*}"
        else
            echo "debian postgis is not updated, different .. "
            postgisFullVersion=""
            postgisMajor=""
            postgisDocSrc=""
        fi
    fi

    if [ -z "$postgisFullVersion" ]
    then
        echo "SKIP debian version";
        # debain version not found;
        echo " # placeholder Dockerfile"                                         > "$version/Dockerfile"
        echo " # Debian version of postgis $postgisFullVersion is not detected!">> "$version/Dockerfile"
        echo " # This is an autogenerated message of ./update.sh "              >> "$version/Dockerfile"
        rm -f "$version/*.sh"
        rm -f "$version/*.md"
        # detect the exact Postgis version - for the alpine version
        # check the latest released 3.x version (so not alpha/beta/rc)
        _postgisMinor=$(echo "$postgisMajMin" | cut -d. -f2)
        postgisFullVersion=$(echo "$postgis_all_v3_versions" | grep "^3\.${_postgisMinor}\." | grep -v '[a-zA-Z]' | version_reverse_sort | head -n 1 || true)
        # Check if the result is empty
        if [[ -z "${postgisFullVersion}" ]]; then
            # If empty, run the command again without excluding pre-releases (alpha/beta/rc)
            postgisFullVersion=$(echo "$postgis_all_v3_versions" | grep "^3\.${_postgisMinor}\." | version_reverse_sort | head -n 1 || true)
        fi
        postgisDocSrc=$postgisFullVersion
        echo "!!!!! postgisFullVersion = ${postgisFullVersion}";
    else
        (
            set -x
            cp -p initdb-postgis.sh update-postgis.sh "$version/"
            if [ "master" == "$postgisVersion" ]; then
              cat Dockerfile.master.template > "$version/Dockerfile"
            else
              cat Dockerfile.template        > "$version/Dockerfile"
            fi
            sed -i 's/%%TXT_AUTOGENERATED%%/'"$autogenerated"'/g; s/%%PG_MAJOR%%/'$postgresVersion'/g; s/%%POSTGIS_MAJOR%%/'$postgisMajor'/g; s/%%POSTGIS_VERSION%%/'$postgisFullVersion'/g; s/%%POSTGIS_GIT_HASH%%/'$postgisGitHash'/g; s/%%CGAL_GIT_HASH%%/'$cgalGitHash'/g; s/%%SFCGAL_GIT_HASH%%/'$sfcgalGitHash'/g; s/%%PROJ_GIT_HASH%%/'$projGitHash'/g; s/%%GDAL_GIT_HASH%%/'$gdalGitHash'/g; s/%%GEOS_GIT_HASH%%/'$geosGitHash'/g; s/%%BOOST_VERSION%%/'"$boostVersion"'/g; s/%%DEBIAN_VERSION%%/'"$suite"'/g;' "$version/Dockerfile"

            echo "| [postgis/postgis:${version}](${dockerhublink}${version}) | [Dockerfile](${githubrepolink}/${version}/Dockerfile) | debian:${suite} | ${postgresVersion} | ${postgisDocSrc} |" >> _dockerlists_${optimized}.md
        )
    fi

    if [ "master" == "$postgisVersion" ]; then
        srcVersion=""
        srcSha256=""
    elif [ -d "$version/alpine" ]; then
        # For Alpine, get the latest release in the same minor version series
        _postgisMajor=$(echo "$postgisMajMin" | cut -d. -f1)
        _postgisMinor=$(echo "$postgisMajMin" | cut -d. -f2)

        # Find the latest non-preview release for this minor version
        srcVersion=$(echo "$postgis_all_v3_versions_array_string" | tr ' ' '\n' | grep "^${_postgisMajor}\.${_postgisMinor}\." | grep -v '[a-zA-Z]' | head -n 1 || true)

        # If no stable release found, fall back to the debian version
        if [ -z "$srcVersion" ]; then
            echo " No stable release found for ${_postgisMajor}.${_postgisMinor}. Falling back ..."
            # For alpha/beta/rc versions, use the version from directory name
            if [[ "$postgisVersion" =~ alpha|beta|rc ]]; then
                srcVersion="$postgisVersion"
            else
                srcVersion="${postgisFullVersion%%+*}"
            fi
        fi

        srcSha256="$(curl -sSL "https://github.com/postgis/postgis/archive/$srcVersion.tar.gz" | sha256sum | awk '{ print $1 }')"
        echo "Selected PostGIS version for Alpine: ${srcVersion}"
        postgisDocSrc=$srcVersion
    else
        srcVersion="${postgisFullVersion%%+*}"
        srcSha256="$(curl -sSL "https://github.com/postgis/postgis/archive/$srcVersion.tar.gz" | sha256sum | awk '{ print $1 }')"
        postgisDocSrc=$srcVersion
    fi

    for variant in alpine; do
        if [ ! -d "$version/$variant" ]; then
            continue
        fi
        (
            set -x
            if [ "$optimized" != "test" ]; then
              optimized="alpine"
            fi
            cp -p Dockerfile.alpine.template initdb-postgis.sh update-postgis.sh "$version/$variant/"
            mv "$version/$variant/Dockerfile.alpine.template" "$version/$variant/Dockerfile"
            sed -i 's/%%TXT_AUTOGENERATED%%/'"$autogenerated"'/g; s/%%PG_MAJOR%%/'"$postgresVersion"'/g; s/%%POSTGIS_VERSION%%/'"$srcVersion"'/g; s/%%POSTGIS_SHA256%%/'"$srcSha256"'/g' "$version/$variant/Dockerfile"

            echo "| [postgis/postgis:${version}-${variant}](${dockerhublink}${version}-${variant}) | [Dockerfile](${githubrepolink}/${version}/${variant}/Dockerfile) | alpine:${defaultAlpinenSuite} | ${postgresVersion} | ${postgisDocSrc} |" >> _dockerlists_${optimized}.md
        )
    done
done

echo "|-------------------------|"
echo "|-   Generated images    -|"
echo "|-------------------------|"

for optimized in debian alpine test; do
    echo " "
    echo "---- ${optimized} ----"
    cat _dockerlists_${optimized}.md
done

# update README.md
bash ./apply-readme.sh

echo " "
echo "=============================="
echo " - update done && git status: "

git status
