#!/usr/bin/env bash
set -euo pipefail
source "$SHELLOPS_DIR/.envrc"

USAGE="$(cat <<EOF
Intelligently bump the git version based on the latest version tag.

Usage: "ops git-bump [flags...]"

Flags:
  -h, --help       Show this help text
  -M, --major      Bump the major release version (0.9.9 -> 1.0.0)
  -m, --minor      Bump the minor release version (0.0.9 -> 0.1.0)
  -p, --patch      Bump the patch release version (0.0.9 -> 0.0.10)
  -d, --dev        Start (or increment) the pre-release dev version (1.0.0 -> 1.0.1-dev.1 -> 1.0.1-dev.2)
  -a, --alpha      Start (or increment) the pre-release alpha version (1.0.0 -> 1.0.1-alpha.1 -> 1.0.1-alpha.2)
  -b, --beta       Start (or increment) the pre-release beta version (1.0.0 -> 1.0.1-beta.1 -> 1.0.1-beta.2)
  -c, --rc         Start (or increment) the pre-release rc version (1.0.0 -> 1.0.1-rc.1 -> 1.0.1-rc.2)
  -r, --release    Transition from pre-release to release (1.0.0-rc.1 -> 1.0.0)
  --dry-run        Output the planned version bump without creating a tag
  --confidence-gate <ch>  Minimum channel for release without warning (default: beta)
  --strict-release        Treat below-gate releases as errors instead of warnings

Notes:
  When using a pre-release flag on an existing release version, you can also pair it with --major or --minor for the version you're planning on targeting with the bump; otherwise, --patch is assumed. Pre-release versions always target a future release version.

  Running the script without any flags always gravitates toward the dev
  track. Non-dev prerelease channels revert to dev; promoting to a higher
  channel (alpha, beta, rc) requires an explicit flag.

  Without flags:
    - 1.0.0 --> 1.0.1
    - 1.0.0-dev.1 --> 1.0.0-dev.2
    - 1.0.0-alpha.1 --> 1.0.0-dev.N  (reverts to dev track)
    - 1.0.0-rc.1 --> 1.0.0-dev.N     (reverts to dev track)
EOF
)"

import \
  "$SHELLOPS_DIR/lib/bump.api.sh"

REGEX_SEMVER='^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z]+(\.[0-9]+)?)?$'
REGEX_SEMVER_GIT_TAG="^v${REGEX_SEMVER#^}"  # ^v[0-9]+...

FLAG_MAJOR=false
FLAG_MINOR=false
FLAG_PATCH=false
FLAG_DEV=false
FLAG_ALPHA=false
FLAG_BETA=false
FLAG_RC=false
FLAG_RELEASE=false
FLAG_DRY_RUN=false
FLAG_CONFIDENCE_GATE=""
FLAG_STRICT_RELEASE=false

function main() {
  local current_version planned_version

  parse_args "$@"
  if ! release_flags_valid; then
    log "Only one release flag may be specified at a time. Use --help for more information."
    exit 1
  fi

  if ! prerelease_flags_valid; then
    log "Only one pre-release flag may be specified at a time. Use --help for more information."
    exit 1
  fi

  if ! needs_version_bump; then
    current_version="$(latest_version)"
    log "HEAD is already tagged with version '$current_version'"
    exit 1
  fi

  if ! planned_version="$(plan_bump "$(bump_options)")"; then
    exit 1
  fi

  if [[ $FLAG_DRY_RUN = true ]]; then
    echo "$planned_version"
    exit 0
  fi

  perform_bump "$planned_version"

  exit 0
}

: <<'DOC'
  Parses CLI flags.
  See USAGE for flag descriptions.
DOC
function parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      -M|--major)   FLAG_MAJOR=true;;
      -m|--minor)   FLAG_MINOR=true;;
      -p|--patch)   FLAG_PATCH=true;;
      -d|--dev)     FLAG_DEV=true;;
      -a|--alpha)   FLAG_ALPHA=true;;
      -b|--beta)    FLAG_BETA=true;;
      -c|--rc)       FLAG_RC=true;;
      -r|--release) FLAG_RELEASE=true;;
      --dry-run) FLAG_DRY_RUN=true;;
      --confidence-gate) FLAG_CONFIDENCE_GATE="$2"; shift;;
      --strict-release) FLAG_STRICT_RELEASE=true;;
      -h|--help)  log "$USAGE" && exit 0;;
      *)
        log "Unknown option: $1"
        log "$USAGE"
        exit 1
        ;;
    esac
    shift
  done
}


: <<'DOC'
  Checks if the repository should actually skip this attempt to bump.

  Returns false if there is already a valid semver tag at HEAD, OR if any flags are passed into the script, implying user intent beyond typical CI/CD flows. Returns true otherwise.
DOC
function needs_version_bump() {
  # Always bump when specific flags are provided.
  if is_bumping_prerelease || is_bumping_release || [[ $FLAG_RELEASE = true ]]; then
    return 0
  fi

  # If there's already a valid version tag
  if git tag --points-at HEAD | grep -qE "$REGEX_SEMVER_GIT_TAG"; then
    return 1
  fi

  return 0
}

function release_flags_valid() {
  $FLAG_MAJOR && $FLAG_MINOR && return 1
  $FLAG_MINOR && $FLAG_PATCH && return 1
  $FLAG_MAJOR && $FLAG_PATCH && return 1
  return 0
}

function prerelease_flags_valid() {
  $FLAG_DEV && $FLAG_ALPHA && return 1
  $FLAG_DEV && $FLAG_BETA && return 1
  $FLAG_DEV && $FLAG_RC && return 1
  $FLAG_ALPHA && $FLAG_BETA && return 1
  $FLAG_ALPHA && $FLAG_RC && return 1
  $FLAG_BETA && $FLAG_RC && return 1
  return 0
}

function bump_options() {
  jq_set '{}' \
    '.release_type' "$(get_release_flag)" \
    '.prerelease_type' "$(get_prerelease_flag)" \
    '.release' "$FLAG_RELEASE" \
    '.confidence_gate' "$FLAG_CONFIDENCE_GATE" \
    '.strict_release' "$FLAG_STRICT_RELEASE"
}

function is_bumping_release() {
  [[
    $FLAG_MAJOR = true ||
    $FLAG_MINOR = true ||
    $FLAG_PATCH = true
  ]]
}

function is_bumping_prerelease() {
  [[
    $FLAG_DEV = true ||
    $FLAG_ALPHA = true ||
    $FLAG_BETA = true ||
    $FLAG_RC = true
  ]]
}

function get_prerelease_flag() {
  if $FLAG_DEV; then
    echo "dev"
    return 0
  elif $FLAG_ALPHA; then
    echo "alpha"
    return 0
  elif $FLAG_BETA; then
    echo "beta"
    return 0
  elif $FLAG_RC; then
    echo "rc"
    return 0
  fi

  echo ""
  return 1
}

function get_release_flag() {
  if $FLAG_MAJOR; then
    echo "major"
    return 0
  elif $FLAG_MINOR; then
    echo "minor"
    return 0
  elif $FLAG_PATCH; then
    echo "patch"
    return 0
  fi

  echo ""
  return 1
}

: <<'DOC'
Executes the planned version bump, creating a new git tag.
DOC
function perform_bump() {
  local version

  version="v$1"
  git tag -a "$version" -m "$version"
  echo "Bumped to version '$version'"
}

main "$@"