#!/bin/sh

# shellcheck disable=SC2039

# shellcheck source=../conf/default.conf
. "${0%/*}/../conf/default.conf"

# shellcheck disable=SC2016
usage() {
  >&2 echo ''
  >&2 echo 'modify-package-state [options] packages-file:'
  >&2 echo ' modify state of package(s).'
  >&2 echo ''
  >&2 echo 'possible options:'
  >&2 echo '  -b|--block:       Block package(s).'
  >&2 echo '  -f|--faulty:      Mark testing/tested package(s) as faulty.'
  >&2 echo '  -h|--help:        Show this help and exit.'
  >&2 echo '  -n|--no-report:   Do not report what packages were modified.'
  >&2 echo '  -t|--tested:      Mark package(s) as tested.'
  >&2 echo '  -u|--unblock:     Unblock package(s).'
  >&2 echo '  -w|--wait:        Wait for lock if necessary.'
  >&2 echo ''
  >&2 echo 'Exactly one of -b|-f|-t|-u is needed for actual operation.'
  [ -z "$1" ] && exit 1 || exit "$1"
}

eval set -- "$(
  getopt -o bfhntuw \
    --long block \
    --long faulty \
    --long help \
    --long no-report \
    --long tested \
    --long unblock \
    --long wait \
    -n "$(basename "$0")" -- "$@" || \
  echo usage
)"

action=''
report=true
wait_for_lock='-n'

while true
do
  case "$1" in
    -b|--block)
      if [ -n "${action}" ]; then
        >&2 echo 'Conflicting/redundand arguments.'
        usage
      fi
      action='block'
    ;;
    -f|--faulty)
      if [ -n "${action}" ]; then
        >&2 echo 'Conflicting/redundand arguments.'
        usage
      fi
      action='faulty'
    ;;
    -h|--help)
      usage 0
    ;;
    -n|--no-report)
      report=false
    ;;
    -t|--tested)
      if [ -n "${action}" ]; then
        >&2 echo 'Conflicting/redundand arguments.'
        usage
      fi
      action='tested'
    ;;
    -u|--unblock)
      if [ -n "${action}" ]; then
        >&2 echo 'Conflicting/redundand arguments.'
        usage
      fi
      action='unblock'
    ;;
    -w|--wait)
      wait_for_lock=''
    ;;
    --)
      shift
      break
    ;;
    *)
      >&2 echo 'Whoops, forgot to implement option "'"$1"'" internally.'
      exit 42
    ;;
  esac
  shift
done

if [ -z "${action}" ]; then
  >&2 echo 'Expected -b|-f|-t|-u.'
  usage
fi

if [ $# -ne 1 ]; then
  >&2 echo 'Too few or too many arguments.'
  usage
fi

input_file="$1"
if ${report}; then
  if ! [ -w "${input_file}" ]; then
    >&2 printf \
      'Cannot open file "%s" for writing.\n' \
      "${input_file}"
    exit 2
  fi
  move_output() {
    cat "${output_file}" > "${input_file}"
    rm -f "${output_file}"
  }
  output_file=$(mktemp 'tmp.modify-package-state.XXXXXXXXXX')
  trap 'move_output' EXIT
else
  output_file='/dev/null'
fi

if ! [ -r "${input_file}" ]; then
  >&2 printf \
    'Cannot open file "%s" for reading.\n' \
    "${input_file}"
  exit 2
fi

exec 9> "${sanity_check_lock_file}"
if ! flock -s ${wait_for_lock} 9; then
  >&2 echo 'Cannot get sanity-check lock.'
  exit
fi

exec 8> "${package_database_lock_file}"
if ! flock ${wait_for_lock} 8; then
  >&2 echo 'Cannot get package-database lock.'
  exit
fi

while read -r a b; do
  case "${action}" in
    'faulty'|'tested')
      # we expect a sha512sum and binary package identifier
      # (pkgname-epoch:pkgver-pkgrel.sub_pkgrel-arch[".pkg.tar.xz"])
      # and we will only operate on packages in "testing" repositories
      # shellcheck disable=SC2016
      combiner=$(
        printf '`binary_packages`'
        mysql_join_binary_packages_repositories
        mysql_join_repositories_repository_stabilities
        mysql_join_binary_packages_architectures
      )
      # shellcheck disable=SC2016,SC2031
      selector=$(
        extract_pkgname_epoch_pkgver_pkgrel_sub_pkgrel_arch_from_package_name "${b}"
        printf 'WHERE `repository_stabilities`.`name`="testing"'
        printf ' AND `binary_packages`.`%s`=from_base64("%s")' \
          'pkgname'    "$(printf '%s' "${pkgname}" | base64 -w0)" \
          'epoch'      "$(printf '%s' "${epoch}" | base64 -w0)" \
          'pkgver'     "$(printf '%s' "${pkgver}" | base64 -w0)" \
          'pkgrel'     "$(printf '%s' "${pkgrel}" | base64 -w0)" \
          'sub_pkgrel' "$(printf '%s' "${sub_pkgrel}" | base64 -w0)" \
          'sha512sum'  "$(printf '%s' "${a}" | base64 -w0)"
        printf ' AND `architectures`.`name`=from_base64("%s")' \
          "$(printf '%s' "${arch}" | base64 -w0)"
      )
    ;;
    'block'|'unblock')
      # we expect a package source identifier (pkgbase.git_revision.mod_git_revision.repository)
      # and we will only operate on packages in "unbuilt" repositories
      # shellcheck disable=SC2016
      combiner=$(
        printf '`build_assignments`'
        mysql_join_build_assignments_package_sources
        mysql_join_package_sources_upstream_repositories
        mysql_join_build_assignments_binary_packages
        mysql_join_binary_packages_repositories
        mysql_join_repositories_repository_stabilities
      )
      # shellcheck disable=SC2016
      selector=$(
        repository="${a##*.}"
        pkgbase="${a%.*}"
        mod_git_revision="${pkgbase##*.}"
        pkgbase="${pkgbase%.*}"
        git_revision="${pkgbase##*.}"
        pkgbase="${pkgbase%.*}"
        printf 'WHERE `repository_stabilities`.`name`="unbuilt"'
        printf ' AND `upstream_repositories`.`name`=from_base64("%s")' \
          "$(printf '%s' "${repository}" | base64 -w0)"
        printf ' AND `package_sources`.`%s`=from_base64("%s")' \
          'pkgbase'          "$(printf '%s' "${pkgbase}" | base64 -w0)" \
          'git_revision'     "$(printf '%s' "${git_revision}" | base64 -w0)" \
          'mod_git_revision' "$(printf '%s' "${mod_git_revision}" | base64 -w0)"
      )
    ;;
    *)
      >&2 printf 'Whooops, action "%s" not implemented yet.\n' "${action}"
      exit 42
    ;;
  esac
  case "${action}" in
    'block')
      if [ -z "${b}" ]; then
        >&2 printf 'No reason is given for blocking package "%s".\n' "${a}"
        exit 2
      fi
      tester='1'
      # shellcheck disable=SC2016
      modifier=$(
        printf '`build_assignments`.`is_blocked`=from_base64("%s")' \
          "$(printf '%s' "${b}" | base64 -w0)"
      )
    ;;
    'unblock')
      # shellcheck disable=SC2016
      tester='NOT `build_assignments`.`is_blocked` IS NULL'
      # shellcheck disable=SC2016
      modifier='`build_assignments`.`is_blocked`=NULL'
    ;;
    'faulty')
      # shellcheck disable=SC2016
      tester='`binary_packages`.`has_issues`=0'
      # shellcheck disable=SC2016
      modifier='`binary_packages`.`is_tested`=0,`binary_packages`.`has_issues`=1'
    ;;
    'tested')
      # shellcheck disable=SC2016
      tester='`binary_packages`.`is_tested`=0'
      # shellcheck disable=SC2016
      modifier='`binary_packages`.`is_tested`=1'
    ;;
    *)
      >&2 printf 'Whooops, action "%s" not implemented yet.\n' "${action}"
      exit 42
    ;;
  esac
  if printf 'SELECT 1 FROM %s %s AND %s LIMIT 1' "${combiner}" "${selector}" "${tester}" | \
    mysql_run_query | \
    grep -qxF '1'; then
    # shellcheck disable=SC2016
    {
      printf 'UPDATE %s SET %s %s' "${combiner}" "${modifier}" "${selector}"
      printf ';\n'
    } | \
      mysql_run_query
    printf '%s %s\n' "${action}" "${a}"
  fi
done \
  < "${input_file}" \
  > "${output_file}"