#!/bin/bash
images="rancher-images.tar.gz"
list="rancher-images.txt"
windows_image_list=""
windows_versions="1809"
source_registry=""
usage () {
    echo "USAGE: $0 [--images rancher-images.tar.gz] [--source-registry index.docker.io] --registry my.registry.com:5000"
    echo "  [-l|--image-list path] text file with list of images; one image per line."
    echo "  [-i|--images path] tar.gz generated by docker save."
    echo "  [-r|--registry registry:port] target private registry in the registry:port format."
    echo "  [-s|--source-registry registry:port] source registry in the registry:port format."
    echo "  [--windows-image-list path] text file with list of images used in Windows. Windows image mirroring is skipped when this is empty."
    echo "  [--windows-versions version] Comma separated Windows versions. e.g., \"1809,ltsc2022\". (Default \"1809\")"
    echo "  [-h|--help] Usage message"
}

push_manifest () {
    export DOCKER_CLI_EXPERIMENTAL=enabled
    manifest_list=()
    for i in "${arch_list[@]}"
    do
        manifest_list+=("$1-${i}")
    done

    echo "Preparing manifest $1, list[${arch_list[@]}]"
    docker manifest create "$1" "${manifest_list[@]}" --amend
    docker manifest push "$1" --purge
}

while [[ $# -gt 0 ]]; do
    key="$1"
    case $key in
        -r|--registry)
        target_registry="$2"
        shift # past argument
        shift # past value
        ;;
        -s|--source-registry)
        source_registry="$2"
        shift # past argument
        shift # past value
        ;;
        -l|--image-list)
        list="$2"
        shift # past argument
        shift # past value
        ;;
        -i|--images)
        images="$2"
        shift # past argument
        shift # past value
        ;;
        --windows-image-list)
        windows_image_list="$2"
        shift # past argument
        shift # past value
        ;;
        --windows-versions)
        windows_versions="$2"
        shift # past argument
        shift # past value
        ;;
        -h|--help)
        help="true"
        shift
        ;;
        *)
        usage
        exit 1
        ;;
    esac
done
if [[ -z "${target_registry}" ]]; then
    usage
    exit 1
fi
if [[ $help ]]; then
    usage
    exit 0
fi

target_registry="${target_registry%/}/"
source_registry="${source_registry%/}"
if [ ! -z "${source_registry}" ]; then
    source_registry="${source_registry}/"
fi

docker_uses_containerd_snapshotter () {
    docker info 2>/dev/null | grep -qi "containerd.snapshotter"
}

helm_available () {
    command -v helm >/dev/null 2>&1
}

helm_oci_ga () {
    local v major minor
    v=$(helm version --short 2>/dev/null | sed -n 's/^v\([0-9.]*\).*/\1/p')
    [ -z "$v" ] && return 1
    major=${v%%.*}
    minor=${v#*.}; minor=${minor%%.*}
    [ "$major" -gt 3 ] || { [ "$major" -eq 3 ] && [ "$minor" -ge 8 ]; }
}

is_oci_helm_chart () {
    [[ "$1" == *"rancher/charts/"* ]]
}

# Preflight: rancher-images.txt now ships OCI Helm chart entries that
# 'docker push' cannot handle unless the daemon uses the containerd
# snapshotter. If the host has neither the snapshotter nor the helm CLI, fail
# fast with a clear message instead of producing cryptic media-type errors.
has_oci_charts=false
while IFS= read -r i; do
    [ -z "${i}" ] && continue
    if is_oci_helm_chart "${i}"; then
        has_oci_charts=true
        break
    fi
done < "${list}"

use_helm_for_charts=false
if $has_oci_charts; then
    if ! docker_uses_containerd_snapshotter; then
        if helm_available; then
            if ! helm_oci_ga; then
                echo "====================================================================="
                echo "ERROR: helm CLI is older than v3.8.0; OCI chart support requires helm 3.8.0+ (GA)."
                echo ""
                echo "Please upgrade helm to v3.8.0 or later:"
                echo "    https://helm.sh/docs/intro/install/"
                echo ""
                echo "For reference, older versions need HELM_EXPERIMENTAL_OCI=1 and are not supported here:"
                echo "    v3.7.2 - last pre-GA (OCI works only with HELM_EXPERIMENTAL_OCI=1)"
                echo "    v3.6.3 - older experimental-OCI"
                echo "    v3.4.2 - OCI very limited"
                echo "====================================================================="
                exit 1
            fi
            echo "WARNING: Docker is not using the containerd snapshotter; falling back to helm CLI for OCI Helm charts."
            use_helm_for_charts=true
        else
            echo "====================================================================="
            echo "ERROR: List contains non-image OCI artifacts, which are not supported"
            echo "by docker's legacy storage drivers. You must take action in order to continue."
            echo ""
            echo "Preferred: configure docker to use the containerd image store"
            echo "    https://docs.docker.com/engine/storage/containerd/#enable-containerd-image-store-on-docker-engine"
            echo "    NOTE: Switching image stores hides all images and containers currently present in the original docker image store."
            echo ""
            echo "Alternative: install the helm CLI:"
            echo "    https://helm.sh/docs/intro/install/"
            echo "====================================================================="
            exit 1
        fi
    fi
fi

docker load --input ${images}

linux_images=()
while IFS= read -r i; do
    [ -z "${i}" ] && continue
    linux_images+=("${i}");
done < "${list}"

arch_list=()
if [[ -n "${windows_image_list}" ]]; then
    IFS=',' read -r -a versions <<< "$windows_versions"
    for version in "${versions[@]}"
    do
        arch_list+=("windows-${version}")
    done

    windows_images=()
    while IFS= read -r i; do
        [ -z "${i}" ] && continue
        windows_images+=("${i}")
    done < "${windows_image_list}"

    # use manifest to publish images only used in Windows
    for i in "${windows_images[@]}"; do
        if [[ ! " ${linux_images[@]}" =~ " ${i}" ]]; then
            case $i in
            */*)
                image_name="${target_registry}${i}"
                ;;
            *)
                image_name="${target_registry}rancher/${i}"
                ;;
            esac
            push_manifest "${image_name}"
        fi
    done
fi

arch_list+=("linux-amd64")
for i in "${linux_images[@]}"; do
    [ -z "${i}" ] && continue
    if $use_helm_for_charts && is_oci_helm_chart "${i}"; then
        continue
    fi
    arch_suffix=""
    use_manifest=false
    if [[ (-n "${windows_image_list}") && " ${windows_images[@]}" =~ " ${i}" ]]; then
        # use manifest to publish images when it is used both in Linux and Windows
        use_manifest=true
        arch_suffix="-linux-amd64"
    fi
    case $i in
    */*)
        image_name="${target_registry}${i}"
        ;;
    *)
        image_name="${target_registry}rancher/${i}"
        ;;
    esac

    docker tag "${source_registry}${i}" "${image_name}${arch_suffix}"
    docker push "${image_name}${arch_suffix}"

    if $use_manifest; then
        push_manifest "${image_name}"
    fi
done

if $use_helm_for_charts; then
    target_oci_base="${target_registry%/}"
    helm_push_args=()
    target_host="${target_oci_base#http://}"
    target_host="${target_host#https://}"
    if [[ "${target_host}" == localhost* || "${target_host}" == 127.0.0.1* || "${target_host}" == 0.0.0.0* ]]; then
        helm_push_args+=("--plain-http")
    fi
    for i in "${linux_images[@]}"; do
        [ -z "${i}" ] && continue
        is_oci_helm_chart "${i}" || continue
        chart_repo="${i%:*}"
        chart_ver_oci="${i#*:}"
        chart_name="${chart_repo##*/}"
        chart_ns="${chart_repo%/*}"
        chart_file="./rancher-oci-charts/${chart_name}-${chart_ver_oci//_/+}.tgz"
        if [ ! -f "${chart_file}" ]; then
            echo "ERROR: Chart file missing: ${chart_file}"
            echo "Expected location: ./rancher-oci-charts/"
            echo "Ensure rancher-save-images.sh completed successfully and"
            echo "rancher-oci-charts/ directory was transferred to this host."
            exit 1
        fi
        echo "Helm chart push: ${chart_file} -> oci://${target_oci_base}/${chart_ns}"
        helm push "${chart_file}" "oci://${target_oci_base}/${chart_ns}" "${helm_push_args[@]}"
    done
fi
