#!/usr/bin/env bash

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$SCRIPT_DIR"
TMP_DIR="$PROJECT_DIR/.tmp-android-build"
SDK_DIR="$PROJECT_DIR/android-sdk"
JDK_DIR="$PROJECT_DIR/jdk"
STATE_FILE="$PROJECT_DIR/.setup-build-environment.state"
CMDLINE_TOOLS_DIR="$SDK_DIR/cmdline-tools"
CMDLINE_TOOLS_LATEST_DIR="$CMDLINE_TOOLS_DIR/latest"
WRAPPER_JAR_PATH="$PROJECT_DIR/gradle/wrapper/gradle-wrapper.jar"
LOCAL_PROPERTIES_PATH="$PROJECT_DIR/local.properties"

ANDROID_CMDLINE_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"
GRADLE_DIST_URL="https://services.gradle.org/distributions/gradle-8.7-bin.zip"
JDK_DIST_URL="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jdk/hotspot/normal/eclipse"
ANDROID_NDK_PACKAGE="ndk;27.2.12479018"
ANDROID_CMAKE_PACKAGE="cmake;3.22.1"
ANDROID_NDK_DIR="$SDK_DIR/ndk/27.2.12479018"
ANDROID_CMAKE_DIR="$SDK_DIR/cmake/3.22.1"
STATE_TTL_SECONDS=86400

log() {
  printf '\n[%s] %s\n' "setup" "$1"
}

require_tool() {
  if ! command -v "$1" >/dev/null 2>&1; then
    echo "Missing required tool: $1" >&2
    exit 1
  fi
}

ensure_dir() {
  mkdir -p "$1"
}

current_epoch() {
  date +%s
}

have_recent_success_marker() {
  if [ ! -f "$STATE_FILE" ]; then
    return 1
  fi

  local recorded_epoch
  recorded_epoch="$(cat "$STATE_FILE" 2>/dev/null || true)"
  if ! printf '%s' "$recorded_epoch" | grep -Eq '^[0-9]+$'; then
    return 1
  fi

  local now
  now="$(current_epoch)"
  [ $((now - recorded_epoch)) -lt "$STATE_TTL_SECONDS" ]
}

mark_successful_check() {
  current_epoch > "$STATE_FILE"
}

auto_commit_if_needed() {
  if ! command -v git >/dev/null 2>&1; then
    log "Git is not available; skipping automatic commit"
    return
  fi

  if ! git -C "$PROJECT_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    log "Project is not inside a git work tree; skipping automatic commit"
    return
  fi

  git -C "$PROJECT_DIR" add -A

  if git -C "$PROJECT_DIR" diff --cached --quiet; then
    log "No source changes to commit after successful build"
    return
  fi

  local changed_files
  changed_files="$(git -C "$PROJECT_DIR" diff --cached --name-only)"

  local -a summary_parts=()
  if printf '%s\n' "$changed_files" | grep -q '^app/src/main/java/'; then
    summary_parts+=("update app gameplay/UI")
  fi
  if printf '%s\n' "$changed_files" | grep -Eq '^(SetupBuildEnvironment\.sh|gradle\.properties|gradle/|gradlew|gradlew\.bat|build\.gradle\.kts|settings\.gradle\.kts|app/build\.gradle\.kts)'; then
    summary_parts+=("improve build setup")
  fi
  if printf '%s\n' "$changed_files" | grep -Eq '^(README\.md|\.gitignore)$'; then
    summary_parts+=("update docs/config")
  fi
  if [ ${#summary_parts[@]} -eq 0 ]; then
    summary_parts+=("project updates")
  fi

  local commit_message
  commit_message="Auto-commit after successful build: $(printf '%s, ' "${summary_parts[@]}" | sed 's/, $//')"
  log "Creating git commit for the latest successful build"
  git -C "$PROJECT_DIR" commit -m "$commit_message" -m "Changed files:\n$changed_files"
}

download_if_missing() {
  local url="$1"
  local output="$2"
  if [ -f "$output" ]; then
    log "Using existing download: ${output#$PROJECT_DIR/}"
  else
    log "Downloading $(basename "$output")"
    curl -L "$url" -o "$output"
  fi
}

ensure_cmdline_tools() {
  if [ -x "$CMDLINE_TOOLS_LATEST_DIR/bin/sdkmanager" ]; then
    log "Android command-line tools already present"
    return
  fi

  ensure_dir "$CMDLINE_TOOLS_DIR"
  ensure_dir "$TMP_DIR"
  local zip_path="$TMP_DIR/commandlinetools.zip"
  download_if_missing "$ANDROID_CMDLINE_TOOLS_URL" "$zip_path"

  log "Extracting Android command-line tools"
  rm -rf "$CMDLINE_TOOLS_LATEST_DIR"
  unzip -q -o "$zip_path" -d "$CMDLINE_TOOLS_DIR"
  if [ -d "$CMDLINE_TOOLS_DIR/cmdline-tools" ]; then
    mv "$CMDLINE_TOOLS_DIR/cmdline-tools" "$CMDLINE_TOOLS_LATEST_DIR"
  fi
}

ensure_wrapper_jar() {
  if [ -f "$WRAPPER_JAR_PATH" ]; then
    log "Gradle wrapper jar already present"
    return
  fi

  ensure_dir "$TMP_DIR"
  local zip_path="$TMP_DIR/gradle-8.7-bin.zip"
  local gradle_extract_dir="$TMP_DIR/gradle-8.7"
  local plugin_wrapper_jar="$gradle_extract_dir/lib/plugins/gradle-wrapper-8.7.jar"
  download_if_missing "$GRADLE_DIST_URL" "$zip_path"

  log "Extracting Gradle wrapper jar"
  rm -rf "$gradle_extract_dir"
  unzip -q -o "$zip_path" -d "$TMP_DIR"
  python - <<PY
import zipfile
src = r'''$plugin_wrapper_jar'''
dst = r'''$WRAPPER_JAR_PATH'''
with zipfile.ZipFile(src) as zf:
    data = zf.read('gradle-wrapper.jar')
with open(dst, 'wb') as f:
    f.write(data)
PY
}

ensure_jdk17() {
  if [ -x "$JDK_DIR/bin/java" ]; then
    log "Project-local JDK already present"
    return
  fi

  ensure_dir "$TMP_DIR"
  local archive_path="$TMP_DIR/jdk17.tar.gz"
  local extract_dir="$TMP_DIR/jdk17-extract"
  rm -f "$archive_path"
  download_if_missing "$JDK_DIST_URL" "$archive_path"

  log "Extracting project-local JDK"
  rm -rf "$extract_dir" "$JDK_DIR"
  mkdir -p "$extract_dir"
  if ! file "$archive_path" | grep -qiE 'gzip compressed|tar archive'; then
    echo "Downloaded JDK archive is not a valid tar.gz: $archive_path" >&2
    exit 1
  fi
  tar -xzf "$archive_path" -C "$extract_dir"
  local extracted_root
  extracted_root="$(find "$extract_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1)"
  if [ -z "$extracted_root" ]; then
    echo "Unable to locate extracted JDK directory" >&2
    exit 1
  fi
  mv "$extracted_root" "$JDK_DIR"
}

setup_java_env() {
  export JAVA_HOME="$JDK_DIR"
  export PATH="$JAVA_HOME/bin:$PATH"
}

write_local_properties() {
  log "Writing local.properties"
  cat > "$LOCAL_PROPERTIES_PATH" <<EOF_INNER
sdk.dir=$SDK_DIR
EOF_INNER
}

bump_android_version() {
  local gradle_file="$PROJECT_DIR/app/build.gradle.kts"
  if [ ! -f "$gradle_file" ]; then
    log "No app/build.gradle.kts found; skipping version bump"
    return
  fi

  log "Bumping Android app version before build"
  python - <<PY
from pathlib import Path
import re

path = Path(r'''$gradle_file''')
text = path.read_text()

version_code_match = re.search(r'versionCode\s*=\s*(\d+)', text)
version_name_match = re.search(r'versionName\s*=\s*"(\d+)\.(\d+)\.(\d+)"', text)

if not version_code_match or not version_name_match:
    raise SystemExit('Could not find versionCode/versionName in app/build.gradle.kts')

current_code = int(version_code_match.group(1))
major, minor, patch = map(int, version_name_match.groups())

new_code = current_code + 1
new_name = f'{major}.{minor}.{patch + 1}'

text = re.sub(r'versionCode\s*=\s*\d+', f'versionCode = {new_code}', text, count=1)
text = re.sub(r'versionName\s*=\s*"\d+\.\d+\.\d+"', f'versionName = "{new_name}"', text, count=1)

path.write_text(text)
print(f'Updated versionCode to {new_code} and versionName to {new_name}')
PY
}

setup_env() {
  setup_java_env
  export ANDROID_HOME="$SDK_DIR"
  export ANDROID_SDK_ROOT="$SDK_DIR"
}

ensure_sdk_packages() {
  setup_env

  if have_recent_success_marker \
    && [ -d "$SDK_DIR/platform-tools" ] \
    && [ -d "$SDK_DIR/platforms/android-34" ] \
    && [ -d "$SDK_DIR/build-tools/34.0.0" ] \
    && [ -d "$ANDROID_NDK_DIR" ] \
    && [ -d "$ANDROID_CMAKE_DIR" ]; then
    log "Skipping SDK verification/install because all components were confirmed within the last 24 hours"
    return
  fi

  log "Accepting Android SDK licenses"
  set +e
  set +o pipefail
  yes | "$CMDLINE_TOOLS_LATEST_DIR/bin/sdkmanager" --sdk_root="$SDK_DIR" --licenses >/dev/null
  local license_status=$?
  set -o pipefail
  set -e
  if [ "$license_status" -ne 0 ]; then
    echo "sdkmanager --licenses failed with exit code $license_status" >&2
    exit "$license_status"
  fi

  log "Installing required Android SDK packages"
  "$CMDLINE_TOOLS_LATEST_DIR/bin/sdkmanager" --sdk_root="$SDK_DIR" \
    "platform-tools" \
    "platforms;android-34" \
    "build-tools;34.0.0" \
    "$ANDROID_NDK_PACKAGE" \
    "$ANDROID_CMAKE_PACKAGE"

  mark_successful_check
}

maybe_build() {
  if [ "${1:-}" != "--build" ]; then
    return
  fi

  setup_env
  bump_android_version
  log "Building debug APK with --no-daemon"
  "$PROJECT_DIR/gradlew" --no-daemon assembleDebug
  auto_commit_if_needed
  log "Stopping any Gradle daemons just in case"
  "$PROJECT_DIR/gradlew" --stop >/dev/null 2>&1 || true
}

main() {
  require_tool curl
  require_tool unzip
  require_tool tar
  require_tool git
  require_tool perl
  require_tool make
  require_tool clang
  require_tool pkg-config
  require_tool readelf

  ensure_dir "$TMP_DIR"
  ensure_dir "$SDK_DIR"

  ensure_jdk17
  ensure_cmdline_tools
  ensure_wrapper_jar
  write_local_properties
  ensure_sdk_packages
  maybe_build "${1:-}"

  log "Environment ready"
  log "Project dir: $PROJECT_DIR"
  log "JDK dir: $JDK_DIR"
  log "SDK dir: $SDK_DIR"
  log "NDK dir: $ANDROID_NDK_DIR"
  log "CMake dir: $ANDROID_CMAKE_DIR"
  log "To cross-compile Git for Android arm64-v8a: bash ./CrossCompileGitForAndroid.sh"
  log "To build without a background Gradle daemon: ./gradlew --no-daemon assembleDebug"
  log "To setup and build in one step: bash ./SetupBuildEnvironment.sh --build"
}

main "$@"