#!/usr/bin/env bash

# Pre-merge validation hook
# - Only 'unreleased' may be merged into 'master'.
# - When merging unreleased -> master, refuse the merge if:
#   a) version contains text (non-numeric characters other than dots), or
#   b) version of 'unreleased' is not strictly higher than version of 'master'.
# On failure, the merge is automatically aborted (git merge --abort) and the hook exits with non-zero status.

set -euo pipefail

TARGET_BRANCH=$(git rev-parse --abbrev-ref HEAD)

# Determine source branch from GIT_REFLOG_ACTION (e.g., "merge unreleased")
SOURCE_BRANCH=""
if [[ "${GIT_REFLOG_ACTION:-}" == merge\ * ]]; then
  SOURCE_BRANCH=$(echo "$GIT_REFLOG_ACTION" | sed 's/^merge //')
fi

# Fallback: try to infer source branch from MERGE_HEAD if not set
if [[ -z "$SOURCE_BRANCH" && -f .git/MERGE_HEAD ]]; then
  # List branches containing MERGE_HEAD and pick the first non-target branch name
  MERGE_COMMIT=$(cat .git/MERGE_HEAD 2>/dev/null || echo "")
  if [[ -n "$MERGE_COMMIT" ]]; then
    SOURCE_BRANCH=$(git branch --contains "$MERGE_COMMIT" --format='%(refname:short)' 2>/dev/null | grep -v "^${TARGET_BRANCH}$" | head -n 1)
  fi
fi

echo "Merging from ${SOURCE_BRANCH:-unknown} to ${TARGET_BRANCH}"

abort_merge() {
  echo "Merge has been cancelled and is now in merge-in-progress mode."
  echo " "
  echo "WARNING: click Abort merge in PhpStorm or run 'git merge --abort' before continuing."
  echo " "
  exit 1
}

# Rule 1: Only unreleased -> master allowed
if [[ "$TARGET_BRANCH" == "master" && "$SOURCE_BRANCH" != "unreleased" ]]; then
    echo "Only branch 'unreleased' can be merged into 'master'." 1>&2
    abort_merge
fi

# Additional checks only when merging unreleased -> master
if [[ "$TARGET_BRANCH" == "master" && "$SOURCE_BRANCH" == "unreleased" ]]; then
  # Helper: extract version value from a version.php file contents
  extract_version() {
    # Reads from STDIN and prints the first capture of 'version' => '...'
    sed -n "s/.*'version'\s*=>\s*'\([^']*\)'.*/\1/p" | head -n 1
  }

  # Read version strings from each branch without touching working copy
  UNR_FILE=$(git show unreleased:config/version.php 2>/dev/null || true)
  MAS_FILE=$(git show master:config/version.php 2>/dev/null || true)

  if [[ -z "$UNR_FILE" || -z "$MAS_FILE" ]]; then
    echo "Failed to read config/version.php from both branches (unreleased/master)." 1>&2
    abort_merge
  fi

  UNR_VERSION=$(echo "$UNR_FILE" | extract_version)
  MAS_VERSION=$(echo "$MAS_FILE" | extract_version)

  if [[ -z "$UNR_VERSION" || -z "$MAS_VERSION" ]]; then
    echo "Could not parse version from config/version.php (unreleased=$UNR_VERSION, master=$MAS_VERSION)." 1>&2
    abort_merge
  fi

  # Define 'contains text' as any character other than digits and dots
  is_numeric_dotted() {
    [[ "$1" =~ ^[0-9]+(\.[0-9]+)*$ ]]
  }

  if ! is_numeric_dotted "$UNR_VERSION"; then
    echo "Version on 'unreleased' contains text or invalid characters: '$UNR_VERSION'." 1>&2
    echo "Expected numeric dotted format like X.Y.Z before merging into master." 1>&2
    echo "Switch to unreleased and manually correct version number. Also describe changes in config/changelog.php." 1>&2
    echo "Switch to unreleased and manually correct version number." 1>&2
    abort_merge
  fi
  if ! is_numeric_dotted "$MAS_VERSION"; then
    echo "Version on 'master' contains text or invalid characters: '$MAS_VERSION'." 1>&2
    echo "Please fix master version to numeric dotted format." 1>&2
    echo "Switch to unreleased and manually correct version number. Also describe changes in config/changelog.php." 1>&2
    echo "Switch to unreleased and manually correct version number." 1>&2
    abort_merge
  fi

  # Compare semantic versions (numeric dotted). Return 0 if $1 > $2, 1 otherwise
  version_gt() {
    local IFS='.'
    read -ra A <<< "$1"
    read -ra B <<< "$2"
    local lenA=${#A[@]}
    local lenB=${#B[@]}
    local len=$(( lenA > lenB ? lenA : lenB ))
    local i
    for (( i=0; i<len; i++ )); do
      local ai=${A[i]:-0}
      local bi=${B[i]:-0}
      # remove leading zeros for numeric compare
      ai=$((10#$ai))
      bi=$((10#$bi))
      if (( ai > bi )); then return 0; fi
      if (( ai < bi )); then return 1; fi
    done
    # equal
    return 1
  }

  if ! version_gt "$UNR_VERSION" "$MAS_VERSION"; then
    echo "Unreleased version ($UNR_VERSION) is not higher than master version ($MAS_VERSION)." 1>&2
    echo "Switch to unreleased and manually correct version number. Also describe changes in config/changelog.php." 1>&2
    echo "Switch to unreleased and manually correct version number." 1>&2
    abort_merge
  fi
fi

exit 0
