#!/bin/sh

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

# TODO: can this be faster?

# Create a lock file.

if [ $# -eq 0 ]; then
  broken=$(
    printf 'CALL show_broken_packages_and_dependencies;\n' | \
      mysql_run_query | \
      sed '
        s/\s.*$//
      ' | \
      sort -u
    printf 'ALL\n'
  )
  # shellcheck disable=SC2086
  "$0" ${broken}
  {
    find "${webserver_directory}/graphs" -maxdepth 1 -name '*.png'
    # shellcheck disable=SC2086
    printf "${webserver_directory}"'/graphs/%s.png\n' ${broken} ${broken}
  } | \
    sort | \
    uniq -u | \
    xargs -r rm
  exit
fi

if pgrep -f '^\S+ '"$0"'.' | \
  grep -vxF "$$" >&2; then

  >&2 echo $$
  >&2 echo 'I was running already.'
  exit
fi

exec 9> "${work_dir}/${0##*/}.lock"
if ! flock -n 9; then
  >&2 echo 'Cannot get show-dependencies lock.'
  exit
fi

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

tmp_dir=$(mktemp -d 'tmp.show-dependencies.1.XXXXXXXXXX' --tmpdir)
trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT

for target_package in "$@"; do

  output="${webserver_directory}/graphs/${target_package}.png"

  # shellcheck disable=SC2016
  {
    printf 'CREATE TEMPORARY TABLE `relevant_binary_packages` (`id` BIGINT, UNIQUE KEY (`id`));\n'
    printf 'CREATE TEMPORARY TABLE `relevant_binary_packages_copy` (`id` BIGINT, UNIQUE KEY (`id`));\n'
    printf 'CREATE TEMPORARY TABLE `relevant_install_targets` (`id` BIGINT, UNIQUE KEY (`id`));\n'

    if [ "${target_package}" = 'ALL' ]; then

      printf 'INSERT IGNORE INTO `relevant_binary_packages` (`id`)'
      printf ' SELECT DISTINCT `binary_packages`.`id`'
      printf ' FROM `repositories`'
      mysql_join_repositories_repository_stabilities
      printf ' AND `repository_stabilities`.`name` IN ("unbuilt","staging")'
      mysql_join_repositories_binary_packages
      printf ';\n'

    else

      printf 'CALL calculate_dependencies_of_package_upto_first_built_one(from_base64("%s"));\n' \
        "$(
          printf '%s' "${target_package}" | \
            base64 -w0
        )"

    fi

    printf 'INSERT IGNORE INTO `relevant_binary_packages_copy` (`id`)'
    printf ' SELECT `relevant_binary_packages`.`id` FROM `relevant_binary_packages`'
    printf ';\n'
    printf 'INSERT IGNORE INTO `relevant_install_targets` (`id`)'
    printf ' SELECT DISTINCT `install_target_providers`.`install_target`'
    printf ' FROM `relevant_binary_packages`'
    mysql_join_binary_packages_install_target_providers 'relevant_binary_packages'
    mysql_join_install_target_providers_dependencies
    mysql_join_dependencies_dependency_types
    printf ' AND `dependency_types`.`relevant_for_building`'
    printf ' JOIN `relevant_binary_packages_copy` ON `relevant_binary_packages_copy`.`id`=`dependencies`.`dependent`'
    printf ';\n'

    # we return lines with either:
    # "knot" $type $identifier $label
    # or
    # "edge" $type $from_knot $to_knot
    printf 'SELECT DISTINCT'
    printf ' "knot",'
    printf 'IF(`build_assignments`.`is_%s`,"%s-build-list-pkgbase",' \
      'blocked' 'broken' \
      'broken' 'broken'
    printf '"build-list-pkgbase"'
    printf ')),'
    printf 'CONCAT("pkgbase:",`package_sources`.`id`),'
    printf '`package_sources`.`pkgbase`'
    printf ' FROM `relevant_binary_packages`'
    printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
    mysql_join_binary_packages_repositories
    mysql_join_repositories_repository_stabilities
    printf ' AND `repository_stabilities`.`name`="unbuilt"'
    mysql_join_binary_packages_build_assignments
    mysql_join_build_assignments_package_sources
    printf ';\n'

    printf 'SELECT DISTINCT'
    printf ' "knot",'
    printf 'CONCAT("pkgname-",`repository_stabilities`.`name`),'
    printf 'CONCAT("pkgname:",`binary_packages`.`id`),'
    printf '`binary_packages`.`pkgname`'
    printf ' FROM `relevant_binary_packages`'
    printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
    mysql_join_binary_packages_repositories
    mysql_join_repositories_repository_stabilities
    printf ';\n'

    printf 'SELECT DISTINCT'
    printf ' "knot","install-target",'
    printf 'CONCAT("install-target:",`install_targets`.`id`),'
    printf '`install_targets`.`name`'
    printf ' FROM `relevant_install_targets`'
    printf ' JOIN `install_targets` ON `install_targets`.`id`=`relevant_install_targets`.`id`'
    printf ';\n'

    printf 'SELECT DISTINCT'
    printf ' "edge","builds"'
    printf ',CONCAT("%s:",`%s`.`id`)' \
      'pkgbase' 'package_sources' \
      'pkgname' 'binary_packages'
    printf ' FROM `relevant_binary_packages`'
    printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
    mysql_join_binary_packages_repositories
    mysql_join_repositories_repository_stabilities
    printf ' AND `repository_stabilities`.`name`="unbuilt"'
    mysql_join_binary_packages_build_assignments
    mysql_join_build_assignments_package_sources
    printf ';\n'

    printf 'SELECT DISTINCT'
    printf ' "edge","provides"'
    printf ',CONCAT("%s:",`%s`.`id`)' \
      'pkgname' 'binary_packages' \
      'install-target' 'install_targets'
    printf ' FROM `relevant_binary_packages`'
    printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
    mysql_join_binary_packages_install_target_providers
    printf ' JOIN `relevant_install_targets` ON `relevant_install_targets`.`id`=`install_target_providers`.`install_target`'
    mysql_join_install_target_providers_install_targets
    printf ';\n'

    printf 'SELECT DISTINCT'
    printf ' "edge",CONCAT("depends:",`dependency_types`.`name`)'
    printf ',CONCAT("%s:",`%s`.`id`)' \
      'install-target' 'install_targets' \
      'pkgname' 'binary_packages'
    printf ' FROM `relevant_binary_packages`'
    printf ' JOIN `binary_packages` ON `relevant_binary_packages`.`id`=`binary_packages`.`id`'
    mysql_join_binary_packages_dependencies
    printf ' JOIN `relevant_install_targets` ON `relevant_install_targets`.`id`=`dependencies`.`depending_on`'
    mysql_join_dependencies_dependency_types
    printf ' AND `dependency_types`.`relevant_for_building`'
    mysql_join_dependencies_install_targets
    printf ';\n'
  } | \
    mysql_run_query | \
    sed '
      y/\t/ /
      /^knot /{
        s/^\S\+ //
        w '"${tmp_dir}"'/knots
        d
      }
      /^edge /{
        s/^\S\+ //
        w '"${tmp_dir}"'/edges
        d
      }
    ' >&2

  mkdir "${tmp_dir}/find-identical"

  awk '{
    print $1 >> "'"${tmp_dir}"'/find-identical/characteristics-"$2
  }' < \
    "${tmp_dir}/knots"
  awk '{
    print $1 " X " $3 >> "'"${tmp_dir}"'/find-identical/characteristics-"$2
    print $1 " " $2 " X" >> "'"${tmp_dir}"'/find-identical/characteristics-"$3
  }' < \
    "${tmp_dir}/edges"

  modifier=$(
    find "${tmp_dir}/find-identical" -maxdepth 1 \
      -type f \
      -name 'characteristics-*' | \
      while read -r file; do
        printf '%s ' "${file##*/}"
        sort -u "${file}" | \
          sha512sum | \
          awk '{print $1}'
      done | \
      sort -k2,2 | \
      uniq -Df1 | \
      sed 's,^characteristics-,,' | \
      sed '
        :a
          $!N
          s/^\(\S\+\) \(\S\+\)\n\(\S\+ \)\2$/\1+\3\2/
          ta
          P
          D
      ' | \
      cut -d' ' -f1 | \
      sed '
        s/^/s,\\(/
        s/+\([^+[:space:]]\+\)$/\\)\\(\\s\\|$\\),\1\\2,g;/
        s/+/\\|/g
      '
  )
  rm -rf --one-file-system "${tmp_dir}/find-identical"

  sed "${modifier}" "${tmp_dir}/knots" | \
    sort -k2,2 -k1,1 | \
    sed '
      :a
        $!N
        s/^\(\S\+ \S\+ \)\(\S\+\)\n\1\(\S\+\)$/\1\2<nl>\3/
        ta
        P
        D
    ' | \
    sponge "${tmp_dir}/knots"

  sed "${modifier}" "${tmp_dir}/edges" | \
    sort -u | \
    sponge "${tmp_dir}/edges"

  {
    printf '%s\n' \
      'digraph dependencies {' \
      '  fontname=dejavu;'

    # knots: $type $identifier $label
    # edges: $type $from_knot $to_knot

    while read -r type id label; do
      case "${type}" in
        'broken-build-list-pkgbase')
          color='#ff0000'
        ;;
        'blocked-build-list-pkgbase')
          color='#d00000'
        ;;
        'build-list-pkgbase')
          color='#800000'
        ;;
        'pkgname-unbuilt')
          color='#800000'
        ;;
        'pkgname-staging')
          color='#008000'
        ;;
        'pkgname-stable'|'pkgname-testing'|'pkgname-standalone')
          color='#000000'
        ;;
        'install-target')
          color='#000080'
        ;;
        *)
          color='#ff80ff'
          label="${label} (${type})"
        ;;
      esac
      printf '  "%s" [label="%s", fontcolor="%s"];\n' "${id}" "${label}" "${color}"
    done < \
      "${tmp_dir}/knots"

    while read -r type from_knot to_knot; do
      printf '"%s" -> "%s";\n' \
        "${from_knot}" "${to_knot}"
    done < \
      "${tmp_dir}/edges"

    printf '%s\n' \
      '}'

  } > \
    "${tmp_dir}/input"

  sed -i '
    s|<nl>|,\\n|g
    s|<sp>|, |g
  ' "${tmp_dir}/input"

  line_count=$(wc -l < "${tmp_dir}/input")
  if [ "${target_package}" != 'ALL' ] && [ "${line_count}" -gt 1000 ]; then
    rm -f "${output}"
    >&2 printf 'Skipping graph for "%s" - would be too big (%d).\n' \
      "${target_package}" \
      "${line_count}"
    continue
  fi
  printf 'small enough (%s): %d\n' \
    "${target_package}" \
    "${line_count}"

  touch "${output}"
  chmod 644 "${output}"

  if ! dot -Tpng -o "${output}" "${tmp_dir}/input"; then
    rm -f "${output}"
    continue
  fi

done

rm "${work_dir}/${0##*/}.lock"