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

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

usage() {
  cat <<'EOF'
Usage:
  bin/launcher --dependency <org:name:version> --main-class <fqcn> [options] [-- app-args...]
  bin/launcher --dependency-file <path> --main-class <fqcn> [options] [-- app-args...]

Options:
  --dependency <coord>        Root dependency coordinate. Repeatable.
  --dependency-file <path>    Newline-separated dependency coordinates.
  --main-class <fqcn>         Main class passed to java.
  --repository <repo>         Extra coursier repository. Repeatable.
  --cache <path>              Override coursier cache directory.
  --scala-version <version>   Pass through to coursier for Scala dependencies.
  --java <path>               Java executable. Default: java
  --java-opt <arg>            JVM option. Repeatable.
  --fetch-opt <arg>           Extra option forwarded to 'cs fetch'. Repeatable.
  --resolve-only              Print resolved classpath and exit.
  -h, --help                  Show this help.
EOF
}

require_value() {
  local name="$1"
  local value="${2:-}"
  if [[ -z "$value" ]]; then
    echo "Missing required value: $name" >&2
    exit 2
  fi
}

find_coursier() {
  if command -v cs >/dev/null 2>&1; then
    command -v cs
    return
  fi
  if command -v coursier >/dev/null 2>&1; then
    command -v coursier
    return
  fi
  echo "coursier command not found. Run: $REPO_ROOT/bin/setup cncf" >&2
  exit 3
}

java_bin="${JAVA_CMD:-java}"
main_class=""
cache_dir=""
scala_version=""
resolve_only="0"

declare -a dependencies=()
declare -a dependency_files=()
declare -a repositories=("ivy2Local" "central" "file://${HOME}/.m2/repository")
declare -a java_opts=()
declare -a fetch_opts=()
declare -a app_args=()

while [[ $# -gt 0 ]]; do
  case "$1" in
    --dependency)
      dependencies+=("${2:-}")
      shift 2
      ;;
    --dependency-file)
      dependency_files+=("${2:-}")
      shift 2
      ;;
    --main-class)
      main_class="${2:-}"
      shift 2
      ;;
    --repository)
      repositories+=("${2:-}")
      shift 2
      ;;
    --cache)
      cache_dir="${2:-}"
      shift 2
      ;;
    --scala-version)
      scala_version="${2:-}"
      shift 2
      ;;
    --java)
      java_bin="${2:-}"
      shift 2
      ;;
    --java-opt)
      java_opts+=("${2:-}")
      shift 2
      ;;
    --java-opt=*)
      java_opts+=("${1#--java-opt=}")
      shift
      ;;
    --fetch-opt)
      fetch_opts+=("${2:-}")
      shift 2
      ;;
    --fetch-opt=*)
      fetch_opts+=("${1#--fetch-opt=}")
      shift
      ;;
    --resolve-only)
      resolve_only="1"
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    --)
      shift
      app_args=("$@")
      break
      ;;
    *)
      echo "Unknown argument: $1" >&2
      usage >&2
      exit 1
      ;;
  esac
done

require_value "--main-class" "$main_class"
if [[ ${#dependencies[@]} -eq 0 && ${#dependency_files[@]} -eq 0 ]]; then
  echo "Specify at least one --dependency or --dependency-file." >&2
  exit 2
fi

if [[ ${#dependencies[@]} -gt 0 ]]; then
  for dep in "${dependencies[@]}"; do
    require_value "--dependency" "$dep"
  done
fi
if [[ ${#dependency_files[@]} -gt 0 ]]; then
  for dep_file in "${dependency_files[@]}"; do
    require_value "--dependency-file" "$dep_file"
    if [[ ! -f "$dep_file" ]]; then
      echo "Dependency file not found: $dep_file" >&2
      exit 2
    fi
  done
fi

if ! command -v "$java_bin" >/dev/null 2>&1; then
  echo "Java command not found: $java_bin" >&2
  exit 3
fi

coursier_bin="$(find_coursier)"

declare -a fetch_cmd=("$coursier_bin" "fetch" "--classpath")
for repo in "${repositories[@]}"; do
  fetch_cmd+=("--repository" "$repo")
done
if [[ -n "$cache_dir" ]]; then
  fetch_cmd+=("--cache" "$cache_dir")
fi
if [[ -n "$scala_version" ]]; then
  fetch_cmd+=("--scala-version" "$scala_version")
fi
if [[ ${#fetch_opts[@]} -gt 0 ]]; then
  for opt in "${fetch_opts[@]}"; do
    fetch_cmd+=("$opt")
  done
fi
if [[ ${#dependency_files[@]} -gt 0 ]]; then
  for dep_file in "${dependency_files[@]}"; do
    fetch_cmd+=("--dependency-file" "$dep_file")
  done
fi
if [[ ${#dependencies[@]} -gt 0 ]]; then
  for dep in "${dependencies[@]}"; do
    fetch_cmd+=("$dep")
  done
fi

classpath="$("${fetch_cmd[@]}")"

if [[ "$resolve_only" == "1" ]]; then
  printf '%s\n' "$classpath"
  exit 0
fi

if [[ ${#java_opts[@]} -gt 0 ]]; then
  if [[ ${#app_args[@]} -gt 0 ]]; then
    exec "$java_bin" "${java_opts[@]}" -cp "$classpath" "$main_class" "${app_args[@]}"
  else
    exec "$java_bin" "${java_opts[@]}" -cp "$classpath" "$main_class"
  fi
else
  if [[ ${#app_args[@]} -gt 0 ]]; then
    exec "$java_bin" -cp "$classpath" "$main_class" "${app_args[@]}"
  else
    exec "$java_bin" -cp "$classpath" "$main_class"
  fi
fi
