#!/usr/bin/env bash

#  Copyright (c) 2019-2024, Schweizer Blasmusikverband. This file is part of
#  hitobito and licensed under the Affero General Public License version 3
#  or later. See the COPYING file at the top-level directory or at
#  https://github.com/hitobito/hitobito.

# Disabled Checks:
# SC2068: Double quote array expansions to avoid re-splitting elements.
#
# shellcheck disable=SC2068

# strict mode
set -euo pipefail
IFS=$'\n\t'

# args
cmd=${1:-'help'}
wagon_name=${2-'no wagon selected'}

# useful variables
core_bin_dir="$(readlink -f "$(dirname "$0")")"        # bin-directory of hitobito (-core)
core_dir="$(readlink -f "$(dirname "$core_bin_dir")")" # directory of hitobito (-core)

# helpers
function require_wagon_name() {
  if [[ $wagon_name = 'No wagon selected' ]]; then
    echo "You must select a wagon to manage"
    "$core_bin_dir/active_wagon" nonexisting
    exit 1
  fi
}

function existing_wagon_names() {
  find "../" -maxdepth 1 -type d |
    grep 'hitobito_.*' |
    sed 's!../hitobito_!!' |
    cut -d/ -f 1 |
    grep -v '\.\.' |
    uniq | sort
}

function clone_wagon() {
  (if [ ! -d "../hitobito_${wagon_name}" ]; then git clone "git@github.com:hitobito/hitobito_${wagon_name}.git" ../hitobito_${wagon_name}; fi)
}

function recreate_dbs() {
  recreate_development_db
  recreate_test_db
}

function recreate_development_db() {
  recreate_db $(printenv RAILS_DB_NAME)
}

function recreate_test_db() {
  recreate_db $(printenv RAILS_TEST_DB_NAME)
}

function recreate_db() {
  db_name=$1

  if [[ -z ${db_name} ]]; then
    echo "$1 must be set."
    echo "Maybe you forgot to activate a WAGON?"
    exit 1
  fi
  echo "Recreating DB (${db_name})"

  cmds=$(
    cat <<SQL
DROP DATABASE IF EXISTS ${db_name} WITH ( FORCE );
CREATE DATABASE ${db_name};
SQL
  )

  echo "$cmds"

  if [[ -v RAILS_DB_SOCKET ]]; then
    echo "$cmds" | PGPASSWORD="${RAILS_DB_PASSWORD:-hitobito}" psql -U "${RAILS_DB_USERNAME:-hitobito}" -d "postgres"
  else
    echo "$cmds" | PGPASSWORD="${RAILS_DB_PASSWORD:-hitobito}" psql -U "${RAILS_DB_USERNAME:-hitobito}" -h "${RAILS_DB_HOST:-127.0.0.1}" -p "${RAILS_DB_PORT:-33066}" -d "postgres"
  fi
}

function setup_dbs() {
  setup_development_db
  reload_test_db
}

function setup_development_db() {
  echo "Migrating and seeding development database"
  bundle exec rails db:migrate
  bundle exec rails wagon:migrate
  bundle exec rails db:seed
  bundle exec rails wagon:seed
}

function setup_test_db() {
  echo "Preparing test database"

  RAILS_ENV="test" bundle exec rake db:migrate

  if test -n "${WAGONS:-""}" -a "${RAILS_TEST_DB_NAME}" != "hit_core_test"; then
    RAILS_ENV="test" bundle exec rake wagon:migrate
  fi
}

function reload_test_db() {
    echo "Preparing test database"
    bundle exec rails db:structure:dump_sql
    echo "DROP SCHEMA public" | rails db -p -e test
    RAILS_ENV=test rails db:structure:load_sql
    rm -f db/structure.sql
}

function indent2 {
  sed -u 's/^/  /'
}

function red {
  # From http://www.andrewnoske.com/wiki/Bash_-_adding_color#Sed_-_used_to_add_color_through_string_substitution
  sed -ure "s/.*/\x1b[31m\0\x1b[0m/g"
}

function highlight_phrases {
  # From http://www.andrewnoske.com/wiki/Bash_-_adding_color#Sed_-_used_to_add_color_through_string_substitution
  # sed -ure "s/unchanged/\x1b[33m\0\x1b[0m/g; s/configured/\x1b[32m\0\x1b[0m/g; s/created/\x1b[34m\0\x1b[0m/g"
  sed -ure "s/>> grepping in .*<</\x1b[33m\0\x1b[0m/g;"
}

# commands
case $cmd in
activate)
  require_wagon_name
  "$core_bin_dir/active_wagon" "$wagon_name"
  direnv allow
  ;;

bootstrap)
  require_wagon_name
  clone_wagon
  "$core_bin_dir/active_wagon" "$wagon_name"
  recreate_dbs
  setup_dbs
  ;;

clone)
  require_wagon_name
  clone_wagon
  ;;

direnv-allow-all)
  for wagon in $(existing_wagon_names); do
    echo "$wagon" | red
    ("$core_bin_dir/active_wagon" "$wagon" && cd "../hitobito_${wagon}" && direnv allow)
  done
  ;;

each)
  shift # loose the "each" argument
  for wagon in $(existing_wagon_names); do
    echo "$wagon" | red
    (
      cd "../hitobito_${wagon}" &&
        direnv exec "../hitobito_${wagon}" $@
    )
  done
  ;;

reset-database)
  recreate_dbs
  setup_dbs
  ;;

reload-test-database)
  reload_test_db
  ;;

create-database)
  recreate_dbs
  ;;

reset-test-database)
  recreate_test_db
  setup_test_db
  ;;

gemfile)
  bundle check || bundle
  echo "$WAGONS" | xargs -L1 -d' ' echo | grep -v '^$' | xargs -I% \
    cp -v Gemfile.lock "../hitobito_%/"
  ;;

configs)
  shift # loose the "configs" argument
  configs_to_copy=${@:-.rspec}

  echo "$WAGONS" | xargs -L1 -d' ' echo | grep -v '^$' | xargs -I% \
    cp -v $configs_to_copy "../hitobito_%/"
  ;;

binstubs)
  echo "$WAGONS" | xargs -L1 -d' ' echo | grep -v '^$' | xargs -I% \
    cp -vf bin/binstubs/* "../hitobito_%/bin/"
  ;;

test-prepare)
  export RAILS_ENV=test
  echo 'Recreating the test-DB fresh to have a clean slate'
  bundle exec rake db:drop db:create
  echo 'Migrating Core'
  bundle exec rake db:migrate

  if [[ "$RAILS_TEST_DB_NAME" != "hit_core_test" ]]; then
    echo 'Migrating Wagons'
    bundle exec rake wagon:migrate

    echo 'Compile Assets to match the selected wagons to ensure better spec-results'
    "$core_bin_dir/webpack-test-compile"
  fi
  ;;

grep)
  if [[ $# -ne 2 ]]; then
    echo "You must provide a String to grep"
    exit 1
  fi
  set +e

  # grep in core
  echo ">> grepping in hitobito <<" | highlight_phrases
  git --no-pager grep "${@:2}"

  # grep in installed wagons
  for wagon in $(existing_wagon_names); do
    echo ">> grepping in ../hitobito_$wagon/ <<" | highlight_phrases
    (
      cd "../hitobito_$wagon/"
      git --no-pager grep --color=always "${@:2}"
    )
  done
  ;;

spec-all)
  shift # loose the "spec-all" argument
  for wagon in $(existing_wagon_names); do
    echo "$wagon" | red
    wagon spec $wagon $@
  done
  ;;

spec)
  require_wagon_name
  shift # loose the "spec" argument
  shift # loose the wagon-name argument

  previous_wagon=$(grep PRIMARY_WAGON .envrc | sed 's/.*=//')

  # setup

  # write and allow .envrc
  $0 activate "$wagon_name"
  direnv allow
  direnv exec ../hitobito/ bundle
  direnv exec ../hitobito/ $0 reload-test-database
  rm -rf public/packs*
  direnv exec bin/webpack-test-compile
  if [[ -x $(command -v direnv) ]]; then direnv allow; fi

  # setup for specs
  direnv exec ../hitobito/ $0 gemfile

  # prepare DB
#  export RAILS_ENV=test
#  direnv exec ../hitobito/ bundle exec rake db:migrate
#  direnv exec ../hitobito/ bundle exec rake wagon:migrate

  # run specs in subshell to return to current directory
  (
    cd "../hitobito_${wagon_name}" &&
      direnv exec "../hitobito_${wagon_name}" rspec $@
  )

  # reset
  $0 activate "$previous_wagon"
  ;;

git-all)
  shift # loose the "git-all" argument
  echo "hitobito" | red
  git $@
  for wagon in $(existing_wagon_names); do
    (cd "../hitobito_${wagon}" && (echo "$wagon" | red) && git $@)
  done
  ;;

git)
  shift # loose the "git" argument
  echo "hitobito" | red
  git $@
  for wagon in $(echo $WAGONS | tr " " "\n"); do
    (cd "../hitobito_${wagon}" && (echo "$wagon" | red) && git $@)
  done
  ;;

list)
  existing_wagon_names | xargs -d' ' -n 1 echo | grep -v '^$'
  ;;

update-copyright)
  # git_range="..master"
  # git diff --name-only "$git_range" |\

  echo "hitobito" | red
  git diff --staged --name-only | xargs -L1 $core_bin_dir/single-file-update-copyright

  for wagon in $(echo ${WAGONS:-''} | tr " " "\n"); do
    (
      cd "../hitobito_${wagon}" && (echo "$wagon" | red) &&
        git diff --staged --name-only | xargs -L1 $core_bin_dir/single-file-update-copyright
    )
  done
  ;;

build)
  require_wagon_name
  shift # loose the "build" argument
  shift # loose the wagon-name argument
  target=${1:-'app'}
  shift # loose the target

  if [[ $target == 'app' ]]; then
    name='rails'
  else
    name=$target
  fi

  (
    cd "../ose_composition_${wagon_name}" &&
      docker buildx build . --file hitobito/Dockerfile --target $target -t hitobito/$wagon_name/$name:latest $@
  )
  ;;

create)
  require_wagon_name
  rails generate wagon "$wagon_name"

  new_wagon_dir="../hitobito_$wagon_name"

  mv -nfv vendor/wagons/$wagon_name/* "$new_wagon_dir"
  mv -nfv vendor/wagons/$wagon_name/.{ruby-version,rubocop.yml,gitignore} "$new_wagon_dir"
  rm -rf vendor/

  pushd "$new_wagon_dir"
  mv github .github
  git init
  git add .
  git commit -m "Create $wagon_name Wagon"
  popd

  $0 activate "$wagon_name"
  if [[ -x $(command -v direnv) ]]; then direnv allow; fi
  direnv exec ../hitobito $0 gemfile
  direnv exec ../hitobito $0 configs

  pushd "$new_wagon_dir"
  if [[ -x $(command -v direnv) ]]; then direnv allow; fi

  git add .
  git commit -m "Add common configs"
  popd

  pushd "$new_wagon_dir"
  $EDITOR lib/tasks/license.rake
  $EDITOR COPYING
  $EDITOR AUTHORS
  $EDITOR hitobito_${wagon_name}.gemspec

  git add .
  git commit -m "Add License and Copyright Info"
  popd

  pushd "$new_wagon_dir"
  bundle exec rake app:license:insert
  git add .
  git commit -m "Insert License Headers"
  popd

  echo "Wagon has been created in ${new_wagon_dir}. The License has been inserted."
  echo "Next Steps would be to add the structure, adapt the design and such..."
  ;;

# TODO: needs to work from a wagon
# TODO: needs to filter the current wagon name (sed 's/_youth//')
overwritten-in-wagon)
  git diff master.. --name-only app/views/ |
    xargs -I% find ../ -path "*/%" -a \! -regex ".*\(ose_composition\|hitobito-development\).*"
  ;;

completion)
  cat <<"COMPLETION"
function __wagon_commands() {
  echo '
    activate
    binstubs
    build
    bootstrap
    clone
    configs
    create
    create-database
    gemfile
    git
    git-all
    grep
    help
    list
    overwritten-in-wagon
    reset-database
    reset-test-database
    spec
    spec-all
    test-prepare
    update-copyright
  ' | xargs
}

function __wagon_list() {
  find "../" -maxdepth 1 -type d |\
  grep 'hitobito_.*' |\
  sed 's!../hitobito_!!' |\
  cut -d/ -f 1 |\
  grep -v '\.\.' |\
  uniq |\
  xargs
}

function __wagon_completion() {
  program=$1
  cur=$2
  prev=$3

  case $prev in
    wagon)
      COMPREPLY=( $(compgen -W "$(__wagon_commands)" -- "$cur" ) )
      return
      ;;

    activate | spec | build)
      COMPREPLY=( $( compgen -W "$(__wagon_list)" -- "$cur" ) )
      return
      ;;
  esac
}

complete -F __wagon_completion -o default wagon
COMPLETION
  ;;

help | *)
  echo "USAGE: $0 [activate] <WAGON>|core"
  echo "       $0 [clone|bootstrap] <WAGON>"
  echo "       $0 [create-datbase|reset-database|test-prepare|update-copyright]"
  echo "       $0 [spec|build] <WAGON>"
  echo "       $0 [spec-all|reset-test-database|overwritten-in-wagon]"
  echo "       $0 [grep] <TERM>"
  echo "       $0 [git|git-all] <ARGS>"
  echo "       $0 [gemfile|configs|binstubs|list]"
  echo
  echo "Enable bash-completion with \"source <($0 completion)\""
  ;;
esac
