#!/bin/bash

function errexit(){
  echo "$@" >&2
  exit 1
}

SCRIPT=$(readlink -f "$0")
FOLDER=$(dirname "$SCRIPT")
VENV="${FOLDER}/py3venv"
cd "${FOLDER}" || errexit "FATAL BUG: script exists but parent directory doesn't."



function run(){
  if [ "$1" == "--append" ]; then
    local append=1
    shift
  fi
  local action="$1"
  shift
  echo "ACTION: ${action}"
  echo "  RUN:  $*"
  if [ "$append" == "1" ]; then
    echo "*** RUN:  $*" >> ./op.log
    "$@" >> ./op.log 2>&1
  else
    "$@" &> ./op.log
  fi
  local rc=$?

  if [ "$rc" == 0 ]; then
    echo "  RESULT: success"
  else
    echo "  RESULT: error (RC=$rc)"
    sed 's/^/  | /' ./op.log
    errexit "Error: action '$action' failed."
  fi
}

function usage(){
  cat <<EOF
Usage:
    ops.sh COMMAND [arg [arg [...]]]

Where COMMAND is one of:
  build-venv   - Build the development environment.
  rebuild-venv - Delete and reuild the development environment.
  test                - Run tox testing (Note 1).
  test-envs           - Run tox testing on specified environments (Note 2).
  test-py-all         - Run tox testing on python unit test environments (Note 1).
  test-style-quality  - Run tox testing on style and quality test environments (Note 1).
  test-coverage       - Run tox testing on coverage test environment (Note 1).
  unit-test           - Run unit testing directly in current environment (Note 3).
  coverage            - Generate code coverage data (Note 3).
  docs                - Generate documentation (Note 4,5)
  quality             - Run code quality analysis (Note 4,6)
  style               - Run code style analysis and tests (Note 6)
  -h|--help|help      - Display this help and exit.

Notes:
1) additional args passed directly to tox
2) additional args specify desired tox test environments to run
3) additional args passed directly to pytest
4) automatically generates coverage data first 
5) additional args passed directly to setup.py
6) extra args ignored.
EOF
  exit 0
}

function activate_venv(){
  if ! . "${VENV}/bin/activate"; then
    errexit "Error: can't activate python3 environment."
  fi
}
function has_untracked_files(){
  [ "$(git status --porcelain 2>/dev/null | grep -Ec '^(M| M|\?\?|u) ')" != 0 ]
}
function require_working_directory_clean(){
  if has_untracked_files; then
    echo "Working directory is not clean." 1>&2
    git status
    false
  else
    true
  fi
}

function do_action(){
  case "$1" in
    rebuild-venv)
      local interpreter=${2-python3}
      run "Delete python3 environment." rm -rf "${VENV}"
      "${SCRIPT}" build-venv "${interpreter}"
    ;;

    build-venv)
      local interpreter=${2-python3}
      if [ ! -d "${VENV}" ]; then
        run "Create python3 environment" "${interpreter}" -m venv "${VENV}"          
      fi
      activate_venv
      run "Upgrade python3 pip"                pip install --upgrade pip            
      run "Install development requirements"   pip install -r "requirements-dev.txt"
      run "Install runtime requirements"       pip install -r "requirements.txt"    
      run "Install python project"             python "${FOLDER}/setup.py" develop  
    ;;

    test)
      shift
      tox -vv "$@"
    ;;

    test-envs)
      shift
      envs="$*"
      tox -vv -e ${envs// /,}
    ;;

    test-py-all)
      shift
      tox -vv "$@" -e py36,py37,py38,py39
    ;;

    test-style-quality)
      shift
      tox -vv "$@" -e style,quality
    ;;

    test-coverage)
      shift
      tox -vv "$@" -e coverage
    ;;

    unit-test)
      shift
      activate_venv
      export PYTHONUNBUFFERED=yes
      pytest -vv --basetemp="${FOLDER}/tests" "$@"
    ;;

    coverage)
      shift
      activate_venv
      run "Generate Coverage Data" pytest --cov-config=./.coveragerc --cov=pt.ptutils --cov-report html -vv --basetemp="${FOLDER}/src" "$@"
    ;;

    docs)
      shift
      do_action coverage
      run "Clean autogenerated doc stubs" rm -rf "${FOLDER}/docs/_autosummary"
      run "Clean previously built docs" rm -rf "${FOLDER}/docs/build"
      run "Build Documentation" python3 setup.py build_sphinx "$@"
    ;;

    quality)
      shift
      do_action coverage
      rm ./op.log
      touch ./op.log
      run --append "Compute Cyclomatic Complexity (CC)" radon cc -s -i '__pending*' ./src
      run --append "Compute Maintainability Index (MI)" radon mi -s -i '__pending*' ./src
      run --append "Compute raw statistics (RAW)" radon raw -s -i '__pending*' ./src
      run --append "Analyze Code Quality" xenon -b C -m A -a A -i '__pending*' ./src
    ;;

    style)
      shift
      activate_venv
      export PYTHONUNBUFFERED=yes
      run "Check Source code Style Compliance" flake8 --max-line-length=120 --ignore=E201,E202,E401,E221,E241,E251,W504 --exclude '__pending*' src
    ;;
    -h|--help|help)
      usage
    ;;

    prerelease)
      run "Ensure working directory is clean." require_working_directory_clean
      do_action unit-test
      do_action coverage
      do_action quality
      do_action style
      do_action docs
    ;;

    release-major)
      do_action prerelease
      run "Increment the version number (major)." bumpversion minor
      run "Push changes" git push
      run "Push tags"    git push --tags
    ;;

    release-minor)
      do_action prerelease
      run "Increment the version number (minor)." bumpversion minor
      run "Push changes" git push
      run "Push tags"    git push --tags
    ;;

    release-patch)
      do_action prerelease
      run "Increment the version number (patch)." bumpversion minor
      run "Push changes" git push
      run "Push tags"    git push --tags
    ;;

    *)
      errexit "Unknown action '$1'. Try '$0 --help' for more information."
    ;;
  esac
}

do_action "$@"
