#!/bin/sh

abort() {
  echo "Release aborted: $1"
  exit 1
}

LAST_TAG=$(git describe --tags --abbrev=0)
COMMITS=$(git log "$LAST_TAG..HEAD" --pretty=oneline)

DRY_RUN=false
SKIP_CI=false
TEST=true

while [ $# -gt 0 ]; do
  case "$1" in
    --norelease) DRY_RUN=true ;;
    --noci)      SKIP_CI=true ;;
    --notest)    TEST=false ;;
    *)           abort "Unknown option: $1" ;;
  esac
  shift
done

if [ -f go.mod ]; then
  IS_GO=true
fi

if [ -f package.json ]; then
  IS_NODE=true
fi

if [ -f setup.py ]; then
  IS_PYTHON=true
fi

if [ -f Cargo.toml ]; then
  IS_RUST=true
fi

if [ -f DESCRIPTION ] && grep -q '^Package:' DESCRIPTION; then
  IS_R=true
  R_PKG_PATH="."
elif [ -f R/DESCRIPTION ] && grep -q '^Package:' R/DESCRIPTION; then
  IS_R=true
  R_PKG_PATH="R"
fi

if [ "$COMMITS" = "" ]; then
  abort "I see no work"
fi

if [ "$IS_GO" ]; then
  if ! go vet ./...; then
    abort "go vet failed"
  fi
fi

if [ "$DRY_RUN" = "false" ]; then
  if grep '^\s*"lint":' package.json > /dev/null 2>&1; then
    if ! npm run lint; then
      abort "npm run lint failed"
    fi
  fi

  if [ "$TEST" = "true" ]; then
    if [ "$IS_GO" ]; then
      if ! go test ./...; then
        abort "go test failed"
      fi
    fi

    if grep '^\s*"test":' package.json > /dev/null 2>&1; then
      if ! npm t; then
        abort "npm test failed"
      fi
    fi

    if [ "$IS_R" ]; then
      if ! R CMD check "$R_PKG_PATH"; then
        abort "R CMD check failed"
      fi
    fi

    if [ "$IS_RUST" ]; then
      if ! cargo t; then
        abort "cargo test failed"
      fi
    fi
  fi

  if grep '^\s*"minify":' package.json > /dev/null 2>&1; then
    if ! npm run minify; then
      abort "npm run minify failed"
    fi
  fi
fi

while read -r COMMIT; do
  HASH=$(echo "$COMMIT" | cut -d ' ' -f 1)
  MESSAGE=$(echo "$COMMIT" | cut -d ':' -f 2- | awk '{$1=$1};1')
  TYPE=$(echo "$COMMIT" | cut -d ' ' -f 2 | awk '{$1=$1};1' | cut -d ':' -f 1 | cut -d '(' -f 1 | awk '{$1=$1};1')
  AREA=$(echo "$COMMIT" | cut -d '(' -f 2 | cut -d ')' -f 1 | awk '{$1=$1};1')

  if [ "$AREA" = "$COMMIT" ]; then
    AREA=""
  fi

  if [ "$AREA" != "" ]; then
    AREA="$AREA: "
  fi

  case $TYPE in
    "break"|"breaking")
      BUILD_TYPE="Major"
      if [ "$BREAK_SUMMARY" = "" ]; then
        BREAK_SUMMARY="### Breaking changes\n"
      fi
      BREAK_SUMMARY="$BREAK_SUMMARY\n* $AREA$MESSAGE ($HASH)"
      ;;
    "feat"|"feature")
      if [ "$BUILD_TYPE" != "Major" ]; then
        BUILD_TYPE="Minor"
      fi
      if [ "$FEAT_SUMMARY" = "" ]; then
        FEAT_SUMMARY="### New features\n"
      fi
      FEAT_SUMMARY="$FEAT_SUMMARY\n* $AREA$MESSAGE ($HASH)"
      ;;
    "fix")
      if [ "$FIX_SUMMARY" = "" ]; then
        FIX_SUMMARY="### Bug fixes\n"
      fi
      FIX_SUMMARY="$FIX_SUMMARY\n* $AREA$MESSAGE ($HASH)"
      ;;
    "perf"|"performance")
      if [ "$PERF_SUMMARY" = "" ]; then
        PERF_SUMMARY="### Performance improvements\n"
      fi
      PERF_SUMMARY="$PERF_SUMMARY\n* $AREA$MESSAGE ($HASH)"
      ;;
    "refactor")
      if [ "$REFACTOR_SUMMARY" = "" ]; then
        REFACTOR_SUMMARY="### Refactorings\n"
      fi
      REFACTOR_SUMMARY="$REFACTOR_SUMMARY\n* $AREA$MESSAGE ($HASH)"
      ;;
    *)
      if [ "$OTHER_SUMMARY" = "" ]; then
        OTHER_SUMMARY="### Other changes\n"
      fi
      OTHER_SUMMARY="$OTHER_SUMMARY\n* $AREA$MESSAGE ($HASH)"
      ;;
  esac
done << EOF
$COMMITS
EOF

if [ "$BUILD_TYPE" != "Major" ] && [ "$BUILD_TYPE" != "Minor" ]; then
  BUILD_TYPE="Patch"
fi

if [ "$BREAK_SUMMARY" != "" ]; then
  BREAK_SUMMARY="$BREAK_SUMMARY\n\n"
fi

if [ "$FEAT_SUMMARY" != "" ]; then
  FEAT_SUMMARY="$FEAT_SUMMARY\n\n"
fi

if [ "$FIX_SUMMARY" != "" ]; then
  FIX_SUMMARY="$FIX_SUMMARY\n\n"
fi

if [ "$PERF_SUMMARY" != "" ]; then
  PERF_SUMMARY="$PERF_SUMMARY\n\n"
fi

if [ "$REFACTOR_SUMMARY" != "" ]; then
  REFACTOR_SUMMARY="$REFACTOR_SUMMARY\n\n"
fi

if [ "$OTHER_SUMMARY" != "" ]; then
  OTHER_SUMMARY="$OTHER_SUMMARY\n\n"
fi

SUMMARY="$BREAK_SUMMARY$FEAT_SUMMARY$FIX_SUMMARY$PERF_SUMMARY$REFACTOR_SUMMARY$OTHER_SUMMARY"

MAJOR=$(echo "$LAST_TAG" | cut -d '.' -f 1)
MINOR=$(echo "$LAST_TAG" | cut -d '.' -f 2)
PATCH=$(echo "$LAST_TAG" | cut -d '.' -f 3)

SED_FRIENDLY_LAST_TAG="$MAJOR\\.$MINOR\\.$PATCH"

case $BUILD_TYPE in
  "Major")
    MAJOR=$((MAJOR + 1))
    MINOR=0
    PATCH=0
    ;;
  "Minor")
    MINOR=$((MINOR + 1))
    PATCH=0
    ;;
  "Patch")
    PATCH=$((PATCH + 1))
    ;;
esac

NEW_TAG="$MAJOR.$MINOR.$PATCH"

if [ "$DRY_RUN" = "true" ]; then
  echo "$NEW_TAG"
  exit 0
fi

if [ -f version.go ]; then
  sed -i.release.bak -e "s/$SED_FRIENDLY_LAST_TAG/$NEW_TAG/g" version.go
  rm version.go.release.bak
  MODIFIED_FILES="$MODIFIED_FILES version.go"
fi

if [ "$IS_NODE" = "true" ]; then
  sed -i.release.bak -e "s/\"version\": \"$LAST_TAG\"/\"version\": \"$NEW_TAG\"/" package.json
  rm package.json.release.bak
  MODIFIED_FILES="$MODIFIED_FILES package.json"

  if [ -f package-lock.json ]; then
    npm i
    MODIFIED_FILES="$MODIFIED_FILES package-lock.json"

    if [ -f npm-shrinkwrap.json ]; then
      npm shrinkwrap
      MODIFIED_FILES="$MODIFIED_FILES npm-shrinkwrap.json"
    fi
  elif [ -f pnpm-lock.yaml ]; then
    pnpm i
    MODIFIED_FILES="$MODIFIED_FILES pnpm-lock.yaml"
  elif [ -f yarn.lock ]; then
    yarn
    MODIFIED_FILES="$MODIFIED_FILES yarn.lock"
  fi
fi

if [ "$IS_PYTHON" = "true" ]; then
  sed -i.release.bak -e "s/$SED_FRIENDLY_LAST_TAG/$NEW_TAG/g" setup.py
  rm setup.py.release.bak
  MODIFIED_FILES="$MODIFIED_FILES setup.py"
fi

if [ "$IS_R" = "true" ]; then
  sed -i.release.bak -e "s/^Version: $SED_FRIENDLY_LAST_TAG$/Version: $NEW_TAG/" "$R_PKG_PATH/DESCRIPTION"
  rm "$R_PKG_PATH/DESCRIPTION.release.bak"
  MODIFIED_FILES="$MODIFIED_FILES $R_PKG_PATH/DESCRIPTION"
fi

if [ "$IS_RUST" = "true" ]; then
  sed -i.release.bak -e "s/$SED_FRIENDLY_LAST_TAG/$NEW_TAG/g" Cargo.toml
  rm Cargo.toml.release.bak
  MODIFIED_FILES="$MODIFIED_FILES Cargo.toml"
fi

if [ -f CHANGELOG.md ]; then
  LOG="CHANGELOG.md"
elif [ -f CHANGES.md ]; then
  LOG="CHANGES.md"
elif [ -f HISTORY.md ]; then
  LOG="HISTORY.md"
elif [ -f NEWS.md ]; then
  LOG="NEWS.md"
elif [ -f CHANGELOG.txt ]; then
  LOG="CHANGELOG.txt"
elif [ -f CHANGES.txt ]; then
  LOG="CHANGES.txt"
elif [ -f HISTORY.txt ]; then
  LOG="HISTORY.txt"
elif [ -f NEWS.txt ]; then
  LOG="NEWS.txt"
elif [ -f CHANGELOG ]; then
  LOG="CHANGELOG"
elif [ -f CHANGES ]; then
  LOG="CHANGES"
elif [ -f HISTORY ]; then
  LOG="HISTORY"
elif [ -f NEWS ]; then
  LOG="NEWS"
fi

if [ "$LOG" != "" ]; then
  TEMP="__please_release_me_$LOG.$NEW_TAG.tmp"
  awk "{ gsub(/^## $LAST_TAG$/, \"## $NEW_TAG\n\n$SUMMARY## $LAST_TAG\") }; { print }" "$LOG" > "$TEMP"
  mv "$TEMP" "$LOG"
  MODIFIED_FILES="$MODIFIED_FILES $LOG"
fi

GIT_FRIENDLY_SUMMARY=$(echo "$SUMMARY" | sed "s/#//g" | sed "s/^ //")

# shellcheck disable=SC2086
git add $MODIFIED_FILES
if [ "$SKIP_CI" = "true" ]; then
  git commit -m "release: $NEW_TAG

[skip ci]"
else
  git commit -m "release: $NEW_TAG"
fi

if [ -f Cargo.toml ]; then
  cargo package
fi

git tag -a "$NEW_TAG" -m "$(printf '%b' "$BUILD_TYPE release $NEW_TAG\n\n$GIT_FRIENDLY_SUMMARY")"

