#! /bin/bash
set -eu

basedir="$HOME/git/pkglint"
gosrcdir="$basedir/v23"
archivedir="$basedir/archive"
pkgsrcdir="$HOME/git/pkgsrc"
gopath="${GOPATH:-"$HOME/go"}"

native_path() {
  case "$(uname -s)" in (CYGWIN*) cygpath -w -- "$1" ;; (*) printf '%s\n' "$1" ;; esac
}

run_tests=yes

step_msg() {
  printf '%s => %s\n' "$(date +'%H:%M:%S')" "$1"
}

qa_genrunid() {
  if [ -n "${runid-}" ]; then
    return
  fi
  timestamp="$(cd "$basedir" && git show -s --format=%ai HEAD | awk -F '[- :]' '{print substr($1,3)$2$3"-"$4$5}')"
  headcommit=$(cd "$basedir" && git rev-parse --short HEAD)
  if (cd "$basedir" && git diff --quiet && git diff --cached --quiet); then
    runid="$timestamp-$headcommit"
  else
    runid="$timestamp-$headcommit-$(date +'%H%M%S')"
    (cd "$basedir" && git diff && git diff --cached) > "$archivedir/$runid.patch"
  fi
}

qa_notest() {
  run_tests=no
}

qa_test() {
  qa_genrunid
  cd "$gosrcdir" || exit 1
  chmod 644 -- *.go */*.go
  step_msg "Formatting pkglint"
  go fmt ./...
  step_msg "Generating source code"
  go generate ./...
  if [ $run_tests = yes ]; then
    step_msg "Testing pkglint"
    go test -test.count 1 -v ./...
  fi
}

qa_install() {
  qa_test
  step_msg "Installing pkglint"
  go install github.com/rillig/pkglint/v23/...
  cp "$gopath/bin/pkglint.exe" "$archivedir/$runid.exe"
}

qa_analyze() {
  step_msg "Analyzing code style"
  # see https://github.com/golangci/golangci-lint
  (cd "$gosrcdir" && golangci-lint run)
}

qa_gobco() {
  step_msg "Running gobco to measure code coverage"
  # see https://github.com/rillig/gobco
  (cd "$gosrcdir" \
   && gobco > "$archivedir/gobco.txt.tmp" || true \
   && mv -f "$archivedir/gobco.txt.tmp" "$archivedir/gobco.txt")
}

qa_netbsd() {
  step_msg "Checking for release blockers"
  ok=yes
  (cd "$gosrcdir" && grep -n "@beta" -- *.go */*.go) && ok=no
  (cd "$gosrcdir" && grep -n "@BMAKE@" -- *_test.go) && ok=no
  (cd "$gosrcdir" && grep -n "@VERSION@" -- *_test.go) && ok=no
  (cd "$gosrcdir" && grep -F -n '$''NetBSD''$' -- *_test.go) && ok=no
  (cd "$archivedir" && grep -n "Pkglint internal error" -- *.err) && ok=no
  [ "$ok" = 'yes' ] || exit 1

  pkgname=$(ssh netbsd "cd proj/pkgsrc/pkgtools/pkglint && env - PATH=/bin:/usr/bin PKGSRC_RUN_TEST=no \"\$HOME/minipkg/bin/bmake\" -v PKGNAME")
  step_msg "Creating archive"
  rm -rf "$archivedir/tmp"
  mkdir -p "$archivedir/tmp/$pkgname"
  cp -R v23 pkglint.1 "$archivedir/tmp/$pkgname/"
  (cd "$archivedir/tmp" && tar cfz "$pkgname.tar.gz" "$pkgname")
  scp "$archivedir"/tmp/"$pkgname".tar.gz netbsd:.cache/pkgsrc-distfiles/
  rm -rf "$archivedir"/tmp

  step_msg "Installing NetBSD package"
  cmd=". ./.bash_profile"
  cmd="$cmd && cd proj/pkgsrc/pkgtools/pkglint"
  cmd="$cmd && env - PATH=/bin:/usr/bin PKGSRC_RUN_TEST=no \"\$HOME/minipkg/bin/bmake\" mdi"
  cmd="$cmd && env - PATH=/bin:/usr/bin PKGSRC_RUN_TEST=no \"\$HOME/minipkg/bin/bmake\" clean update"
  if [ $run_tests = yes ]; then
    cmd="$cmd && env - PATH=/bin:/usr/bin PKGSRC_RUN_TEST=yes \"\$HOME/minipkg/bin/bmake\" clean update"
  fi
  cmd="$cmd && \"\$HOME/minipkg/bin/pkglint\" -Wall"
  # shellcheck disable=SC2029
  ssh netbsd "$cmd"
}

qa_codewalk() {
  step_msg "Regenerating codewalk.md"
  # See https://github.com/rillig/go-codewalk
  (cd "$gosrcdir" && codewalk codewalk.src.md codewalk.md)
}

qa_pkgsrc() {
  qa_genrunid
  step_msg "Running pkglint on the pkgsrc tree"
  (
    cd "$pkgsrcdir" \
    && printf 'pkgsrc HEAD commit: %s\n' "$(git rev-parse HEAD)" \
    && printf 'pkgsrc-wip HEAD commit: %s\n' "$(git -C wip rev-parse HEAD)" \
    && printf '\n' \
    && { GODEBUG=gctrace=1 "$archivedir/$runid.exe" -s -pr -Wall -Call . wip 2> "$archivedir/$runid.err" || true; } \
  ) > "$archivedir/$runid.tmp" || true
  if grep "panic" "$archivedir/$runid.err" >/dev/null; then
    printf '%s: pkglint crashed\n' "$0" 1>&2
    exit 1
  fi
  mv "$archivedir/$runid.tmp" "$archivedir/$runid.out"
}

qa_cover_pkgsrc() {
  qa_genrunid
  step_msg "Running code coverage on the pkgsrc tree"
  (
    native_pkgsrcdir="$(native_path "$pkgsrcdir")"
    native_archivedir="$(native_path "$archivedir")"
    cd "$gosrcdir" \
    && env \
      PKGLINT_TESTDIR="$native_pkgsrcdir" \
      PKGLINT_TESTCMDLINE="-r -Wall -Call -p -s -e" \
    gobco \
      -test=-test.covermode=count \
      -test=-test.coverprofile="$native_archivedir/$runid.cover.txt" \
      -test=-timeout=1000s \
      -test=-check.f="^Test_Pkglint_Main__realistic" \
      -stats="$native_archivedir/$runid.cover.gobco.json" \
      > "$native_archivedir/$runid.cover.out"
  )
}

qa_diffs() {
  step_msg "Generating diffs"
  (
    cd "$archivedir"
    for out in [0-9]*-*-*.out; do
      case $out in (*.cover.out) continue ;; esac
      diff -u -- *.ref "$out" > "${out%.out}.diff" || true
      perl -e '
        use strict;
        use warnings;

        sub readhisto($) {
          my ($filename) = @_;
          my %histo = ();
          open(my $f, "<", $filename) or die;
          while (defined(my $line = <$f>)) {
            if ($line =~ /^loghisto[\t ]+(\d+)[\t ]+(.*)$/) {
              $histo{$2} = +$1;
            }
          }
          return \%histo;
        }

        my $del = readhisto(shift());
        my $add = readhisto(shift());
        my %all = ();
        foreach my $key (keys %$del) { $all{$key} += $del->{$key}; }
        foreach my $key (keys %$add) { $all{$key} += $add->{$key}; }
        foreach my $key (sort { $all{$b} <=> $all{$a} || $a cmp $b } keys %all) {
          my $ndel = exists($del->{$key}) ? $del->{$key} : 0;
          my $nadd = exists($add->{$key}) ? $add->{$key} : 0;
          if ($ndel != $nadd) {
            printf("%5d   %5d   %+5d   %s\n", $ndel, $nadd, $nadd - $ndel, $key);
          }
        }
        ' -- *.ref "$out" >> "${out%.out}.diff"
      test -s "${out%.out}.diff" || rm "${out%.out}.diff"
    done
  )
}

case "$*" in
"")
  set -- install pkgsrc diffs
  ;;
"notest")
  set -- notest install pkgsrc diffs
  ;;
esac

for cmd in "$@"; do
  "qa_$cmd"
done

step_msg "OK"
