#!/bin/bash

# SPDX-FileCopyrightText: 2024 Benedikt Franke <benedikt.franke@dlr.de>
# SPDX-FileCopyrightText: 2024 Florian Heinrich <florian.heinrich@dlr.de>
#
# SPDX-License-Identifier: Apache-2.0

###############################################################################
# Helper commands #
###################
# This script contains a few little helper commands to make development easier.
###############################################################################
set -e
set -o pipefail

# imports
source "$(dirname "${BASH_SOURCE[0]}")/scripts/utils.sh"

# global variables
PACKAGE_NAME=dlr.fl.client
PACKAGE_PATH=${PACKAGE_NAME//./\/}

PROJECT_ROOT="$(pwd)"
BUILD_ROOT="${PROJECT_ROOT}/build"
PACKAGE_ROOT="${PROJECT_ROOT}/${PACKAGE_PATH}"
DOC_ROOT="${PROJECT_ROOT}/docs"
SCRIPTS_ROOT="${PROJECT_ROOT}/scripts"

# default values of arguments
HTTP_SERVE=true

# parse arguments
action=${1,,}; shift
[ "$action" == "--help" ] || [ "$action" == "-h" ] && action=help
while [[ $# -gt 0 ]]; do
  case $1 in
    --no-http-serve) HTTP_SERVE=false; shift ;;
    --help|-h) action=help; shift ;;
    --version) action=version; shift ;;
    --) shift; break ;;
    *)
      # warn "Unknown argument: '$1'; Skip further parsing.";
      break ;;
  esac
done


no_actions="$(compgen -A function)"
###############################################################################
# action functions

function help() {
  actions="$(actions="$(printf '%s,' "${action_map[@]}")"; echo "{${actions%,}}")"
  [ ${#actions} -lt 22 ] && actions=$(printf '%-22s' "$actions") || actions="$actions\n$(printf %24s "")"
  info2 "usage: $0 <action> [options]"
  info2 ""
  info2 "positional arguments:"
  info2 -e "  ${actions}Available sub commands"
  info2 "    help                Show this help message and exit"
  info2 "    start               Run the application"
  info2 "    docker-build        Build docker images for local development"
  info2 "    test                Run all tests"
  info2 "    lint                Run all linter"
  info2 "    lint-code           Run code linter"
  info2 "    lint-doc            Run documentation linter"
  info2 "    lint-scripts        Run bash script linter"
  info2 "    mypy                Run type checker"
  info2 "    coverage            Run unit tests"
  info2 "    coverage-report     Generate test coverage report"
  info2 "    doc                 Start documentation server"
  info2 "    doc-build           Build documentation"
  info2 "    licenses            Check licenses"
  info2 "    safety-check        Check dependencies for known security vulnerabilities"
  info2 "    install             Install package"
  info2 "    clean               Clean up local files"
  info2 "    version             Show package version"
  info2 "    versions            Show versions"
  info2 ""
  info2 "options:"
  info2 "  --no-http-serve       Do not serve the action result via HTTP"
}

function version() {
  awk -F "=" '/version/ {print $2}' "${PROJECT_ROOT}/pyproject.toml" | awk -F'"' '{print $2}' | awk NF | head -n 1
}

function versions() {
  info "versions"
  info "$(python --version)"
  info "$(python -m pip --version)"
  if ! command -v docker > /dev/null 2>&1; then
    warn "docker not found, skipping docker version"
  else
    info "$(docker --version)"
    info "$(docker compose version)"
  fi
  info -n "package version: "
  version
}

function install() {
  versions
  info "install package"
  if [ "$#" -eq "0" ]; then
    python -m pip install -e .
  else
    python -m pip install "$@"

    # check if "doc" extra should be installed
    install_doc=false
    for value in "$@"; do
      value="${value//[[:blank:]]/}"
      [[ "$value" =~ \[(.*,)?(doc|all)(,.*)?\] ]] && install_doc=true
    done
    if [[ "$install_doc" == "true" ]]; then
      info "install markdown linter"
      if command -v npm > /dev/null 2>&1; then
        npm install --no-save markdownlint-cli2
      else
        warn "npm not found, skipping markdownlint-cli2 installation"
      fi
    fi
  fi
  #info "post-install"
  #py_pack_dir="$(python -c 'import site; print(site.getsitepackages()[0])')"
  #info " + setup user documentation plugins"
  #cp "$py_pack_dir/plantuml_markdown.py" "$py_pack_dir/markdown/extensions/"
}

function clean() {
  info "remove __pycache__|.pyc|.pyo"
  find "${PROJECT_ROOT}" | grep -E "(__pycache__|\.pyc$$|\.pyo$$)" | xargs rm -rf
  info "remove builds"
  rm -rf "${BUILD_ROOT}"
  rm -rf "${PROJECT_ROOT}/site"
  info "remove egg-info"
  rm -rf "${PROJECT_ROOT}/*.egg-info"
  info "remove tox"
  rm -rf "${PROJECT_ROOT}/.tox"
  info "remove pytest cache"
  rm -rf "${PROJECT_ROOT}/.pytest_cache"
  info "remove mypy cache"
  rm -rf "${PROJECT_ROOT}/.mypy_cache"
}

function start() {
  error "not implemented yet"
  exit 1
}

function docker-build() {
  versions
  info "build docker images for local development"
  project_name="$(awk -F "=" '/name/ {print $2}' "${PROJECT_ROOT}/pyproject.toml" | awk -F'"' '{print $2}' | awk NF | head -n 1)"
  image_name="local/${project_name}:latest"
  info "build docker image: ${image_name}"
  docker build -t "${image_name}" . "$@"
}

function test() {
  versions
  info "run all tests"
  lint
  mypy
  coverage
  coverage-report
  if [ "${HTTP_SERVE}" = "true" ]; then
    if [ -d "${BUILD_ROOT}/htmlcov" ]; then
      python -m http.server --directory "${BUILD_ROOT}/htmlcov" 8080
    else
      error "no coverage report found"
      exit 1
    fi
  fi
}

function lint() {
  versions
  info "linting"
  lint-code
  lint-doc
  lint-scripts
}

function lint-code() {
  info "lint code"
  info "flake8 version: $(python -m flake8 --version | xargs)"
  python -m flake8 "${PACKAGE_ROOT}"
  python -m flake8 "${SCRIPTS_ROOT}"
}

function lint-doc() {
  info "lint documentation"
  # use markdownlint from David Anson (based on nodejs)
  # https://github.com/DavidAnson/markdownlint
  npm exec markdownlint-cli2 "${DOC_ROOT}/**/*.md" "${PROJECT_ROOT}/README.md"
}

function lint-scripts() {
  info "lint bash scripts"
  info "shellcheck $(shellcheck --version | head -n 2 | tail -n 1)"
  shellcheck --external-sources --shell bash --source-path "${PROJECT_ROOT}" "${PROJECT_ROOT}/dev"
  shellcheck --external-sources --shell bash --source-path "${SCRIPTS_ROOT}" "${SCRIPTS_ROOT}/"*.sh
}

function mypy() {
  info "type checking"
  python -m mypy "${PACKAGE_ROOT}"
  if find "${SCRIPTS_ROOT}" -type f -name "*.py" | grep -q .; then
    python -m mypy "${SCRIPTS_ROOT}"
  fi
}

function coverage() {
  info "run python tests with coverage"
  warn "no tests implemented yet"
  #python -m coverage run -m pytest
  #python -m coverage html --directory "${BUILD_ROOT}/htmlcov"
}

function coverage-report() {
  info "print test coverage report"
  warn "no tests implemented yet"
  #python -m coverage report
}

function doc() {
  versions
  mkdocs_version="$(python -m mkdocs --version)"
  info "${mkdocs_version#"python -m "}"
  info "start documentation server"
  # check if the user has passed the --dirtyreload flag
  dirty_flag=false
  for value in "$@"; do
    [[ "--dirty" = "$value" ]] && dirty_flag=true
  done
  if [[ "$dirty_flag" == "false" ]]; then
    warn "consider using --dirty to reload only file changes instead of the"
    warn "whole project. This can lead to a significant speed up during the"
    warn "documentation development."
  fi
  # create and serve documentation
  python -m mkdocs serve "$@"
}

function doc-build() {
  versions
  mkdocs_version="$(python -m mkdocs --version)"
  info "${mkdocs_version#"python -m "}"
  info "build documentation"
  python -m mkdocs build "$@"
}

function licenses() {
  info "search for license conflicts"
  licensecheck
  info "search for non-compliant files with REUSE"
  python -m reuse lint
}

function safety-check() {
  info "check dependencies for known security vulnerabilities"
  # main only no dev dependencies etc.
  python -m tox --recreate -e safety
  # alternative
  #python -m pip install -U safety
  #safety check
  ##python -m pip uninstall safety
}


# create array with all action functions (above)
readarray -t action_map <<< "$(comm -3 <(compgen -A function) <(echo "$no_actions"))"
###############################################################################
# run action

if ! printf '%s\n' "${action_map[@]}" | grep -x -q "$action"; then
  echo "Invalid action : $action"
  echo "Allowed actions: ${action_map[*]}"
  echo "Use --help for more information"
  exit 1
fi

$action "$@"
