#!/usr/bin/env bash
set -euo pipefail

print_usage() {
  cat <<'USAGE'
Usage:
  download-maven-version.sh [--dry-run] <version> [output-dir]

Description:
  Downloads all artifacts for a given version from the IMG.LY Maven repository
  https://maven.img.ly/maven and keeps the same Maven path structure locally.

Options:
  --dry-run  List matching files without downloading them.
  -h, --help Show this help.

Examples:
  ./download-maven-version.sh 1.75.1
  ./download-maven-version.sh 1.75.1 ./my-offline-maven-dir
  ./download-maven-version.sh --dry-run 1.75.1

After downloading, register the local Maven repository in Gradle inside
dependencyResolutionManagement { repositories { ... } }. Point the URL at the
"maven" sub-folder of the output directory (for example maven-offline/maven):

  Kotlin DSL (settings.gradle.kts):
    maven {
        name = "imgly-offline"
        url = uri(rootDir.resolve("maven-offline/maven"))
        mavenContent {
            includeGroup("ly.img")
        }
    }

  Groovy DSL (settings.gradle):
    maven {
        name = 'imgly-offline'
        url = new File(rootDir, 'maven-offline/maven').toURI()
        mavenContent {
            includeGroup 'ly.img'
        }
    }

This script mirrors artifacts published by IMG.LY. A fully offline Gradle build
also needs local access to Gradle, plugins, and transitive dependencies from
repositories such as Google Maven and Maven Central.

Environment:
  IMGLY_MAVEN_BASE_URL    Server URL, optionally including the repository name
                          (default: https://maven.img.ly).
  IMGLY_MAVEN_REPOSITORY Repository name (default: maven).
  IMGLY_MAVEN_USERNAME   Optional HTTP Basic Auth username.
  IMGLY_MAVEN_PASSWORD   Optional HTTP Basic Auth password.
USAGE
}

DRY_RUN=false
if [[ ${1-} == "--dry-run" ]]; then
  DRY_RUN=true
  shift
fi

if [[ ${1-} == "-h" || ${1-} == "--help" ]]; then
  print_usage
  exit 0
fi

if (( $# < 1 || $# > 2 )); then
  print_usage >&2
  exit 1
fi

VERSION="$1"
OUTPUT_DIR="${2:-./maven-offline}"
RAW_BASE_URL="${IMGLY_MAVEN_BASE_URL:-https://maven.img.ly}"
RAW_BASE_URL="${RAW_BASE_URL%/}"
REPOSITORY_NAME="${IMGLY_MAVEN_REPOSITORY:-maven}"

if [[ ! "$VERSION" =~ ^[A-Za-z0-9][A-Za-z0-9._+-]*$ ]]; then
  echo "Invalid Maven version: ${VERSION}" >&2
  exit 1
fi

if [[ ! "$REPOSITORY_NAME" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then
  echo "Invalid Maven repository name: ${REPOSITORY_NAME}" >&2
  exit 1
fi

if [[ "$RAW_BASE_URL" == */"$REPOSITORY_NAME" ]]; then
  SERVER_URL="${RAW_BASE_URL%/"$REPOSITORY_NAME"}"
else
  SERVER_URL="$RAW_BASE_URL"
fi
SERVER_URL="${SERVER_URL%/}"
REPOSITORY_URL="${SERVER_URL}/${REPOSITORY_NAME}"
DETAILS_URL="${SERVER_URL}/api/maven/details/${REPOSITORY_NAME}"

for command_name in curl jq; do
  if ! command -v "$command_name" >/dev/null; then
    echo "${command_name} is required." >&2
    exit 2
  fi
done

USE_AUTH=false
if [[ -n "${IMGLY_MAVEN_USERNAME:-}" || -n "${IMGLY_MAVEN_PASSWORD:-}" ]]; then
  if [[ -z "${IMGLY_MAVEN_USERNAME:-}" || -z "${IMGLY_MAVEN_PASSWORD:-}" ]]; then
    echo "Set both IMGLY_MAVEN_USERNAME and IMGLY_MAVEN_PASSWORD for authentication." >&2
    exit 1
  fi
  USE_AUTH=true
fi

match_count=0
download_count=0
failure_count=0
DETAILS_RESPONSE=""

is_safe_segment() {
  local segment="$1"
  [[ -n "$segment" && "$segment" != "." && "$segment" != ".." &&
    "$segment" != *"/"* && "$segment" != *"\\"* &&
    "$segment" != *$'\n'* && "$segment" != *$'\r'* ]]
}

curl_maven() {
  if [[ "$USE_AUTH" == "true" ]]; then
    curl -u "${IMGLY_MAVEN_USERNAME}:${IMGLY_MAVEN_PASSWORD}" "$@"
  else
    curl "$@"
  fi
}

encode_path() {
  local path="$1"
  local encoded_path=""
  local segment
  local encoded_segment
  local segments=()

  IFS='/' read -r -a segments <<< "$path"
  for segment in "${segments[@]}"; do
    encoded_segment="$(jq -rn --arg value "$segment" '$value | @uri')"
    if [[ -n "$encoded_path" ]]; then
      encoded_path+="/"
    fi
    encoded_path+="$encoded_segment"
  done
  printf '%s' "$encoded_path"
}

request_details() {
  local path="$1"
  local request_url="$DETAILS_URL"
  local response
  local http_code

  if [[ -n "$path" ]]; then
    request_url+="/$(encode_path "$path")"
  fi

  if ! response="$(curl_maven -sS -L \
    -w $'\n%{http_code}' "$request_url")"; then
    echo "Network error while requesting ${request_url}." >&2
    return 1
  fi

  http_code="${response##*$'\n'}"
  DETAILS_RESPONSE="${response%$'\n'*}"

  if [[ "$http_code" != 2* ]]; then
    echo "Maven browse request failed for ${path:-/} (HTTP ${http_code})." >&2
    if [[ -n "$DETAILS_RESPONSE" ]]; then
      sed -n '1,20p' <<< "$DETAILS_RESPONSE" >&2
    fi
    return 1
  fi

  if ! jq -e '.files | type == "array"' <<< "$DETAILS_RESPONSE" >/dev/null; then
    echo "Unexpected Maven browse response for ${path:-/}." >&2
    return 1
  fi
}

download_version_directory() {
  local version_path="$1"
  local details_response
  local file_name
  local encoded_file_name
  local remote_path
  local local_dir="${OUTPUT_DIR}/${REPOSITORY_NAME}/${version_path}"
  local local_file

  request_details "$version_path"
  details_response="$DETAILS_RESPONSE"

  while IFS= read -r file_name; do
    if ! is_safe_segment "$file_name"; then
      echo "Ignoring unsafe Maven file name: ${file_name}" >&2
      failure_count=$((failure_count + 1))
      continue
    fi

    match_count=$((match_count + 1))
    remote_path="${REPOSITORY_NAME}/${version_path}/${file_name}"

    if [[ "$DRY_RUN" == "true" ]]; then
      echo "Would download: ${remote_path}"
      continue
    fi

    mkdir -p "$local_dir"
    local_file="${local_dir}/${file_name}"
    encoded_file_name="$(jq -rn --arg value "$file_name" '$value | @uri')"

    if curl_maven -sS --fail -L \
      -o "$local_file" \
      "${REPOSITORY_URL}/$(encode_path "$version_path")/${encoded_file_name}"; then
      download_count=$((download_count + 1))
      echo "Downloaded: ${remote_path}"
    else
      failure_count=$((failure_count + 1))
      echo "warning: could not download ${remote_path}" >&2
    fi
  done < <(jq -r '.files[] | select(.type == "FILE") | .name' <<< "$details_response")
}

browse_directory() {
  local path="$1"
  local details_response
  local child_name
  local child_path
  local children

  request_details "$path"
  details_response="$DETAILS_RESPONSE"

  # Artifact directories contain Maven metadata. Do not recurse through every
  # published version; inspect only the exact version requested by the user.
  if jq -e '.files[] | select(.type == "FILE" and .name == "maven-metadata.xml")' \
    <<< "$details_response" >/dev/null; then
    if jq -e --arg version "$VERSION" \
      '.files[] | select(.type == "DIRECTORY" and .name == $version)' \
      <<< "$details_response" >/dev/null; then
      if [[ -n "$path" ]]; then
        download_version_directory "${path}/${VERSION}"
      else
        download_version_directory "$VERSION"
      fi
    fi
    return
  fi

  children="$(jq -r '.files[] | select(.type == "DIRECTORY") | .name' <<< "$details_response")"
  while IFS= read -r child_name; do
    [[ -z "$child_name" ]] && continue
    if ! is_safe_segment "$child_name"; then
      echo "Ignoring unsafe Maven directory name: ${child_name}" >&2
      failure_count=$((failure_count + 1))
      continue
    fi

    if [[ -n "$path" ]]; then
      child_path="${path}/${child_name}"
    else
      child_path="$child_name"
    fi
    browse_directory "$child_path"
  done <<< "$children"
}

browse_directory ""

if (( match_count == 0 )); then
  echo "ERROR: No artifact found for version ${VERSION}." >&2
  exit 4
fi

if (( failure_count > 0 )); then
  echo "ERROR: ${failure_count} file or directory operations failed." >&2
  exit 5
fi

if [[ "$DRY_RUN" == "true" ]]; then
  echo "Dry run complete. ${match_count} files found for version ${VERSION}."
  exit 0
fi

echo "Done. ${download_count} files saved to ${OUTPUT_DIR}."

MAVEN_REPO_PATH="${OUTPUT_DIR#./}/${REPOSITORY_NAME}"
cat <<EOF

Next step: register this directory as a local Maven repository in Gradle.
Add the following inside dependencyResolutionManagement { repositories { ... } }:

  Kotlin DSL (settings.gradle.kts):
    maven {
        name = "imgly-offline"
        url = uri(rootDir.resolve("${MAVEN_REPO_PATH}"))
        mavenContent {
            includeGroup("ly.img")
        }
    }

  Groovy DSL (settings.gradle):
    maven {
        name = 'imgly-offline'
        url = new File(rootDir, '${MAVEN_REPO_PATH}').toURI()
        mavenContent {
            includeGroup 'ly.img'
        }
    }
EOF
