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

# Pre-commit hook: runs clang-format and swiftformat on staged source files.
# Automatically reformats and re-stages non-conforming files.

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"

source "$REPO_ROOT/tools/hooks/format-config.bash"

# Usage: staged_files <pathspec>...
staged_files() {
  git diff --cached --name-only --diff-filter=AM -- "$@"
}

STAGED_FILES=$(staged_files)

if [ -z "$STAGED_FILES" ]; then
  exit 0
fi

REFORMATTED=0

# --- clang-format -----------------------------------------------------------
# Files excluded from formatting are skipped by clang-format itself, see .clang-format-ignore.
CLANG_FILES=$(staged_files "${CLANG_FORMAT_TARGETS[@]}")

if [ -n "$CLANG_FILES" ]; then
  CLANG_FORMAT=$(resolve_clang_format)
  echo "pre-commit: using $($CLANG_FORMAT --version)"

  while IFS= read -r FILE; do
    [ -f "$FILE" ] || continue
    if ! "$CLANG_FORMAT" --dry-run --Werror "$FILE" > /dev/null 2>&1; then
      echo "clang-format: reformatting $FILE"
      "$CLANG_FORMAT" -i "$FILE"
      git add "$FILE"
      REFORMATTED=1
    fi
  done <<< "$CLANG_FILES"
fi

# --- swiftformat -------------------------------------------------------------
# swiftformat takes directories, the hook needs per-file pathspecs: iphone -> iphone/*.swift.
SWIFT_FILES=$(staged_files "${SWIFTFORMAT_TARGETS[@]/%//*.swift}")

if [ -n "$SWIFT_FILES" ]; then
  if ! command -v swiftformat >/dev/null 2>&1; then
    echo "Warning: swiftformat not found, skipping Swift files."
  else
    echo "pre-commit: using swiftformat $(swiftformat --version)"

    while IFS= read -r FILE; do
      [ -f "$FILE" ] || continue
      if ! swiftformat --lint "$FILE" > /dev/null 2>&1; then
        echo "swiftformat: reformatting $FILE"
        swiftformat "$FILE"
        git add "$FILE"
        REFORMATTED=1
      fi
    done <<< "$SWIFT_FILES"
  fi
fi

# --- ktlint ------------------------------------------------------------------
KTLINT_FILES=$(staged_files "${KTLINT_TARGETS[@]}")

if [ -n "$KTLINT_FILES" ]; then
  if KTLINT=$(resolve_ktlint); then
    echo "pre-commit: using ktlint $($KTLINT --version)"

    # Collect existing files for a single JVM invocation (~2s total instead of ~2s per file).
    KTLINT_ARGS=()
    while IFS= read -r FILE; do
      [ -f "$FILE" ] && KTLINT_ARGS+=("$FILE")
    done <<< "$KTLINT_FILES"

    if [ ${#KTLINT_ARGS[@]} -gt 0 ]; then
      # ktlint has no --dry-run; hash files before/after to skip re-staging
      # when ktlint made no changes (avoids spurious "reformatted" lines and
      # avoids re-staging unrelated worktree state when nothing was changed).
      KTLINT_HASHES=()
      for i in "${!KTLINT_ARGS[@]}"; do
        KTLINT_HASHES[$i]=$(git hash-object "${KTLINT_ARGS[$i]}")
      done

      set +e
      "$KTLINT" --editorconfig="$REPO_ROOT/android/.editorconfig" --format "${KTLINT_ARGS[@]}"
      kt_rc=$?
      set -e

      for i in "${!KTLINT_ARGS[@]}"; do
        AFTER=$(git hash-object "${KTLINT_ARGS[$i]}")
        if [ "${KTLINT_HASHES[$i]}" != "$AFTER" ]; then
          echo "ktlint: reformatted ${KTLINT_ARGS[$i]}"
          git add "${KTLINT_ARGS[$i]}"
          REFORMATTED=1
        fi
      done

      if [ $kt_rc -ne 0 ]; then
        echo "ktlint: unfixable violations above — fix manually and re-commit."
        exit 1
      fi
    fi
  else
    echo "Warning: ktlint not found, skipping Kotlin files."
  fi
fi

# --- Store metadata (release notes, descriptions, URLs, etc.) ---------------
METADATA_FILES=$(staged_files 'android/app/src/fdroid/play/*.txt' \
                             'android/app/src/google/play/*.txt' \
                             'iphone/metadata/*.txt')

if [ -n "$METADATA_FILES" ]; then
  METADATA_ARGS=()
  while IFS= read -r FILE; do
    [ -f "$FILE" ] && METADATA_ARGS+=("$FILE")
  done <<< "$METADATA_FILES"

  if [ ${#METADATA_ARGS[@]} -gt 0 ]; then
    set +e
    "$REPO_ROOT/tools/python/check_store_metadata.py" files --fix "${METADATA_ARGS[@]}"
    meta_rc=$?
    set -e

    # Re-stage anything --fix mutated. `git diff --quiet` compares working tree
    # to index; non-zero means the file changed, so add it and flag REFORMATTED.
    for FILE in "${METADATA_ARGS[@]}"; do
      if ! git diff --quiet -- "$FILE"; then
        git add -- "$FILE"
        REFORMATTED=1
      fi
    done

    if [ $meta_rc -ne 0 ]; then
      echo "pre-commit: store metadata validation failed — fix manually and re-commit."
      exit 1
    fi
  fi
fi

# -----------------------------------------------------------------------------
# Fail if reformatting eliminated all staged changes (would create an empty commit).
if [ "$REFORMATTED" -eq 1 ]; then
  if git diff --cached --quiet; then
    echo "pre-commit: nothing to commit after reformatting — all staged changes were formatting-only."
    exit 1
  fi
  echo "pre-commit: some files were reformatted and re-staged."
fi

exit 0
