#!/bin/bash
# Consolidated script for next-gen image validation, target URL retrieval, and syncing
# Usage: ./nextgen-image-sync.sh <action> <config-file> <images>
#   action: validate|get-targets|sync
#   config-file: Path to the image-sync-nextgen-{env}.yaml file
#   images: Newline-separated list of images in format "repository:tag"

set -e

# ==================== Helper Functions ====================

check_dependencies() {
  if ! command -v yq &> /dev/null; then
    echo "Error: yq is required but not installed" >&2
    exit 1
  fi

  if [ "$ACTION" = "sync" ] && ! command -v regctl &> /dev/null; then
    echo "Error: regctl is required for sync action but not installed" >&2
    exit 1
  fi
}

parse_image() {
  local image="$1"

  if [[ ! "$image" =~ ^([^:]+):(.+)$ ]]; then
    echo ""
    return 1
  fi

  echo "${BASH_REMATCH[1]}|${BASH_REMATCH[2]}"
  return 0
}

find_repo_config() {
  local config_file="$1"
  local repository="$2"
  local tag="$3"

  local num_sets=$(yq '.repositorySets | length' "$config_file")

  for ((i=0; i<num_sets; i++)); do
    local set_name=$(yq ".repositorySets | keys | .[$i]" "$config_file")
    local num_repos=$(yq ".repositorySets.$set_name | length" "$config_file")

    for ((j=0; j<num_repos; j++)); do
      local source=$(yq ".repositorySets.$set_name[$j].source" "$config_file")

      if [ "$source" = "$repository" ]; then
        local target_name=$(yq ".repositorySets.$set_name[$j].name" "$config_file")
        local matched_pattern=$(validate_tag "$config_file" "$set_name" "$j" "$tag")
        if [ -n "$matched_pattern" ]; then
          echo "$set_name|$target_name"
          return 0
        fi
      fi
    done
  done

  return 1
}

validate_tag() {
  local config_file="$1"
  local set_name="$2"
  local repo_index="$3"
  local tag="$4"

  local num_tags=$(yq ".repositorySets.$set_name[$repo_index].tags.allow | length" "$config_file")

  if [ "$num_tags" = "0" ] || [ "$num_tags" = "null" ]; then
    return 1
  fi

  for ((k=0; k<num_tags; k++)); do
    local pattern=$(yq ".repositorySets.$set_name[$repo_index].tags.allow[$k]" "$config_file")
    local grep_pattern=$(echo "$pattern" | sed 's/\\d/[0-9]/g')

    if echo "$tag" | grep -qE "^${grep_pattern}$"; then
      echo "$pattern"
      return 0
    fi
  done

  return 1
}

find_repo_index() {
  local config_file="$1"
  local set_name="$2"
  local repository="$3"

  local num_repos=$(yq ".repositorySets.$set_name | length" "$config_file")

  for ((j=0; j<num_repos; j++)); do
    local source=$(yq ".repositorySets.$set_name[$j].source" "$config_file")
    if [ "$source" = "$repository" ]; then
      echo "$j"
      return 0
    fi
  done

  return 1
}

get_target_registries() {
  local config_file="$1"
  local set_name="$2"
  local target_name="$3"
  local tag="$4"

  local num_targets=$(yq '.targetRegistries | length' "$config_file")
  local targets=""

  for ((t=0; t<num_targets; t++)); do
    local target_registry=$(yq ".targetRegistries[$t].name" "$config_file")
    local includes=$(yq ".targetRegistries[$t].includeRepositorySets[]" "$config_file" | grep -w "$set_name" || true)

    if [ -n "$includes" ]; then
      local target_image="${target_registry}/${target_name}:${tag}"
      targets="${targets}${target_image}\n"
    fi
  done

  echo -e "$targets" | grep -v '^$' || true
}

# ==================== Action Functions ====================

action_validate() {
  echo "Validating images against config: $CONFIG_FILE"
  echo ""

  local all_valid=true
  local valid_images=""
  local invalid_images=""

  while IFS= read -r image; do
    [ -z "$image" ] && continue

    echo "Checking image: $image"

    local parsed=$(parse_image "$image")
    if [ -z "$parsed" ]; then
      echo "  ERROR: Invalid image format. Expected 'repository:tag', got '$image'"
      invalid_images="${invalid_images}${image}\n"
      all_valid=false
      continue
    fi

    IFS='|' read -r repository tag <<< "$parsed"

    local config=$(find_repo_config "$CONFIG_FILE" "$repository" "$tag")
    if [ -z "$config" ]; then
      echo "  ERROR: Repository '$repository' not found in config file"
      invalid_images="${invalid_images}${image}\n"
      all_valid=false
      echo ""
      continue
    fi

    IFS='|' read -r set_name target_name <<< "$config"
    echo "  Found matching source: $repository"

    local repo_index=$(find_repo_index "$CONFIG_FILE" "$set_name" "$repository")
    local matched_pattern=$(validate_tag "$CONFIG_FILE" "$set_name" "$repo_index" "$tag")

    if [ -z "$matched_pattern" ]; then
      echo "  ERROR: Tag '$tag' does not match any allowed patterns"
      invalid_images="${invalid_images}${image}\n"
      all_valid=false
    else
      echo "  ✓ Tag '$tag' matches allowed pattern: $matched_pattern"
      valid_images="${valid_images}${image}\n"
    fi

    echo ""
  done <<< "$IMAGES"

  echo "========================================="
  # Print valid/invalid lists and never fail the script.
  if [ -n "$valid_images" ]; then
    echo "Valid images:"
    echo "===VALID_IMAGES_START==="
    echo -e "$valid_images" | grep -v '^$' || true
    echo "===VALID_IMAGES_END==="
  else
    echo "Valid images:"
    echo "(none)"
    echo "===VALID_IMAGES_START==="
    echo "===VALID_IMAGES_END==="
  fi

  if [ -n "$invalid_images" ]; then
    echo ""
    echo "Invalid images:"
    echo "===INVALID_IMAGES_START==="
    echo -e "$invalid_images" | grep -v '^$' || true
    echo "===INVALID_IMAGES_END==="
    echo ""
    echo "⚠ Some images failed validation, proceeding with valid ones only"
  else
    echo ""
    echo "Invalid images:"
    echo "===INVALID_IMAGES_START==="
    echo "===INVALID_IMAGES_END==="
    echo "✓ All images passed validation"
  fi

  # Always exit 0 to allow downstream steps to continue.
  exit 0
}

action_get_targets() {
  echo "Getting target image URLs from config: $CONFIG_FILE" >&2
  echo "" >&2

  local target_images=""
  local invalid_images=""

  while IFS= read -r image; do
    [ -z "$image" ] && continue

    local parsed=$(parse_image "$image")
    if [ -z "$parsed" ]; then
      echo "Warn: skipping invalid image format: '$image' (expected 'repository:tag')" >&2
      invalid_images="${invalid_images}${image}\n"
      continue
    fi

    IFS='|' read -r repository tag <<< "$parsed"

    local config=$(find_repo_config "$CONFIG_FILE" "$repository" "$tag")
    if [ -z "$config" ]; then
      echo "Warn: skipping image - repository '$repository' not found in config" >&2
      invalid_images="${invalid_images}${image}\n"
      continue
    fi

    IFS='|' read -r set_name target_name <<< "$config"

    # Validate the tag against allowed patterns before producing targets
    local repo_index=$(find_repo_index "$CONFIG_FILE" "$set_name" "$repository")
    local matched_pattern=$(validate_tag "$CONFIG_FILE" "$set_name" "$repo_index" "$tag")
    if [ -z "$matched_pattern" ]; then
      echo "Warn: skipping image - tag '$tag' not allowed by policy for '$repository'" >&2
      invalid_images="${invalid_images}${image}\n"
      continue
    fi

    local targets=$(get_target_registries "$CONFIG_FILE" "$set_name" "$target_name" "$tag")
    target_images="${target_images}${targets}\n"
  done <<< "$IMAGES"

  # Only print target URLs to stdout for downstream consumption
  echo -e "$target_images" | grep -v '^$' || true

  # Print a brief summary of skipped inputs to stderr
  if [ -n "$invalid_images" ]; then
    echo "" >&2
    echo "Note: some input images were invalid and were skipped:" >&2
    echo -e "$invalid_images" | grep -v '^$' >&2 || true
  fi
}

action_sync() {
  echo "========================================="
  echo "Syncing specific images"
  echo "========================================="
  echo ""

  local synced_images=""
  local failed_images=""
  local skipped_images=""

  while IFS= read -r image; do
    [ -z "$image" ] && continue

    echo "Processing image: $image"

    local parsed=$(parse_image "$image")
    if [ -z "$parsed" ]; then
      echo "  SKIP: Invalid image format. Expected 'repository:tag', got '$image'" >&2
      skipped_images="${skipped_images}${image}\n"
      continue
    fi

    IFS='|' read -r repository tag <<< "$parsed"

    local config=$(find_repo_config "$CONFIG_FILE" "$repository" "$tag")
    if [ -z "$config" ]; then
      echo "  SKIP: Repository '$repository' not found in config file" >&2
      skipped_images="${skipped_images}${image}\n"
      echo ""
      continue
    fi

    IFS='|' read -r set_name target_name <<< "$config"
    echo "  Found config: source=$repository -> name=$target_name"

    # Validate the tag against allowed patterns before syncing
    local repo_index=$(find_repo_index "$CONFIG_FILE" "$set_name" "$repository")
    local matched_pattern=$(validate_tag "$CONFIG_FILE" "$set_name" "$repo_index" "$tag")
    if [ -z "$matched_pattern" ]; then
      echo "  SKIP: Tag '$tag' does not match any allowed patterns" >&2
      skipped_images="${skipped_images}${image}\n"
      echo ""
      continue
    else
      echo "  ✓ Tag '$tag' matches allowed pattern: $matched_pattern"
    fi

    local targets=$(get_target_registries "$CONFIG_FILE" "$set_name" "$target_name" "$tag")

    while IFS= read -r target_image; do
      [ -z "$target_image" ] && continue

      echo "  Copying to: $target_image"

      if regctl image copy "$image" "$target_image"; then
        echo "  ✓ Successfully synced to: $target_image"
        synced_images="${synced_images}${target_image}\n"
      else
        echo "  ✗ Failed to sync to: $target_image"
        failed_images="${failed_images}${image} -> ${target_image}\n"
      fi
    done <<< "$targets"

    echo ""
  done <<< "$IMAGES"

  echo "========================================="
  echo "Sync Summary"
  echo "========================================="

  if [ -n "$synced_images" ]; then
    echo ""
    echo "Successfully synced images:"
    echo -e "$synced_images" | grep -v '^$' || true
  fi

  if [ -n "$skipped_images" ]; then
    echo ""
    echo "Skipped (invalid or not allowed):"
    echo -e "$skipped_images" | grep -v '^$' || true
  fi

  if [ -n "$failed_images" ]; then
    echo ""
    echo "Failed to sync:"
    echo -e "$failed_images" | grep -v '^$' || true
    exit 1
  fi

  echo ""
  echo "Completed sync. Valid images were processed; invalid ones were skipped."
}

# ==================== Main Script ====================

ACTION="$1"
CONFIG_FILE="$2"
IMAGES="$3"

if [ -z "$ACTION" ] || [ -z "$CONFIG_FILE" ] || [ -z "$IMAGES" ]; then
  echo "Usage: $0 <action> <config-file> <images>"
  echo "  action: validate|get-targets|sync"
  echo "  config-file: Path to the image-sync-nextgen-{env}.yaml file"
  echo "  images: Newline-separated list of images in format 'repository:tag'"
  exit 1
fi

if [ ! -f "$CONFIG_FILE" ]; then
  echo "Error: Config file '$CONFIG_FILE' not found"
  exit 1
fi

check_dependencies

case "$ACTION" in
  validate)
    action_validate
    ;;
  get-targets)
    action_get_targets
    ;;
  sync)
    action_sync
    ;;
  *)
    echo "Error: Invalid action '$ACTION'. Must be one of: validate, get-targets, sync"
    exit 1
    ;;
esac
