# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor@kleen.consulting>, Steffen Jost <jost@tcs.ifi.lmu.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later

{ pkgs ? (import ./nixpkgs.nix {}).pkgs, nixpkgsPath ? null }:

let
  inherit (pkgs.lib) optionalString;

  haskellPackages = pkgs.haskellPackages;

  postgresSchema = pkgs.writeText "schema.sql" ''
    CREATE USER uniworx WITH SUPERUSER;
    CREATE DATABASE uniworx_test;
    GRANT ALL ON DATABASE uniworx_test TO uniworx;
    CREATE DATABASE uniworx;
    GRANT ALL ON DATABASE uniworx TO uniworx;
  '';

  postgresHba = pkgs.writeText "hba_file" ''
    local all all trust
  '';

  develop = pkgs.writeScriptBin "develop" ''
    #!${pkgs.zsh}/bin/zsh -e

    basePath=$(pwd)
    exec 4<>''${basePath}/.develop.env

    flockRes=
    set +e
    ${pkgs.util-linux}/bin/flock -en 4; flockRes=$?
    set -e
    if [[ ''${flockRes} -ne 0 ]]; then
      echo "Could not take exclusive lock; is another develop running?" >&2
      exit ''${flockRes}
    fi

    cleanup() {
      set +e -x
      type cleanup_postgres &>/dev/null && cleanup_postgres
      type cleanup_widget_memcached &>/dev/null && cleanup_widget_memcached
      type cleanup_session_memcached &>/dev/null && cleanup_session_memcached
      type cleanup_cache_memcached &>/dev/null && cleanup_cache_memcached
      type cleanup_minio &>/dev/null && cleanup_minio
      type cleanup_maildev &>/dev/null && cleanup_maildev

      [ -f "''${basePath}/.develop.env" ] && rm -vf "''${basePath}/.develop.env"
      set +x
    }

    trap cleanup EXIT

    export PORT_OFFSET=$(((16#$(sha256sum <<<"$(hostname -f):''${basePath}" | head -c 16)) % 1000))

    if [[ -z "$PGHOST" ]]; then
      set -xe

      pgDir=$(mktemp -d --tmpdir=''${XDG_RUNTIME_DIR} postgresql.XXXXXX)
      pgSockDir=$(mktemp -d --tmpdir=''${XDG_RUNTIME_DIR} postgresql.sock.XXXXXX)
      pgLogFile=$(mktemp --tmpdir=''${XDG_RUNTIME_DIR} postgresql.XXXXXX.log)
      initdb --no-locale -D ''${pgDir}
      pg_ctl start -D ''${pgDir} -l ''${pgLogFile} -w -o "-k ''${pgSockDir} -c listen_addresses=''' -c hba_file='${postgresHba}' -c unix_socket_permissions=0700 -c max_connections=9990 -c shared_preload_libraries=pg_stat_statements -c session_preload_libraries=auto_explain -c auto_explain.log_min_duration=100ms"
      psql -h ''${pgSockDir} -f ${postgresSchema} postgres
      printf "Postgres logfile is %s\nPostgres socket directory is %s\n" ''${pgLogFile} ''${pgSockDir}

      export PGHOST=''${pgSockDir}
      export PGLOG=''${pgLogFile}

      cleanup_postgres() {
          set +e -x
          pg_ctl stop -D ''${pgDir}
          rm -rvf ''${pgDir} ''${pgSockDir} ''${pgLogFile}
          set +x
      }

      set +xe
    fi

    if [[ -z "$WIDGET_MEMCACHED_HOST" ]]; then
      set -xe

      memcached -l localhost -p $(($PORT_OFFSET + 11211)) &>/dev/null &
      widget_memcached_pid=$!

      export WIDGET_MEMCACHED_HOST=localhost
      export WIDGET_MEMCACHED_PORT=$(($PORT_OFFSET + 11211))

      cleanup_widget_memcached() {
        [[ -n "$widget_memcached_pid" ]] && kill $widget_memcached_pid
      }

      set +xe
    fi

    if [[ -z "$SESSION_MEMCACHED_HOST" ]]; then
      set -xe

      memcached -l localhost -p $(($PORT_OFFSET + 11212)) &>/dev/null &
      session_memcached_pid=$!

      export SESSION_MEMCACHED_HOST=localhost
      export SESSION_MEMCACHED_PORT=$(($PORT_OFFSET + 11212))

      cleanup_session_memcached() {
        [[ -n "$session_memcached_pid" ]] && kill $session_memcached_pid
      }

      set +xe
    fi

    if [[ -z "$MEMCACHED_HOST" ]]; then
      set -xe

      memcached -l localhost -p $(($PORT_OFFSET + 11213)) &>/dev/null &
      memcached_pid=$!

      export MEMCACHED_HOST=localhost
      export MEMCACHED_PORT=$(($PORT_OFFSET + 11212))

      cleanup_session_memcached() {
        [[ -n "$memcached_pid" ]] && kill $memcached_pid
      }

      set +xe
    fi

    if [[ -z "$UPLOAD_S3_HOST" ]]; then
       set -xe

       cleanup_minio() {
         [[ -n "$minio_pid" ]] && kill $minio_pid
         [[ -n "''${MINIO_DIR}" ]] && rm -rvf ''${MINIO_DIR}
         [[ -n "''${MINIO_LOGFILE}" ]] && rm -rvf ''${MINIO_LOGFILE}
       }

       export MINIO_DIR=$(mktemp -d --tmpdir=''${XDG_RUNTIME_DIR} minio.XXXXXX)
       export MINIO_LOGFILE=$(mktemp --tmpdir=''${XDG_RUNTIME_DIR} minio.XXXXXX.log)
       export MINIO_ACCESS_KEY=$(${pkgs.pwgen}/bin/pwgen -s 16 1)
       export MINIO_SECRET_KEY=$(${pkgs.pwgen}/bin/pwgen -s 32 1)

       minio server --address localhost:$(($PORT_OFFSET + 9000)) ''${MINIO_DIR} &>''${MINIO_LOGFILE} &
       minio_pid=$!

       export UPLOAD_S3_HOST=localhost
       export UPLOAD_S3_PORT=$(($PORT_OFFSET + 9000))
       export UPLOAD_S3_SSL=false
       export UPLOAD_S3_KEY_ID=''${MINIO_ACCESS_KEY}
       export UPLOAD_S3_KEY=''${MINIO_SECRET_KEY}

       sleep 1

       set +xe
    fi

    ${optionalString (pkgs.nodePackages ? "maildev") ''
      if [[ -z "$SMTPHOST" ]]; then
        set -xe

        cleanup_maildev() {
          [[ -n "$maildev_pid" ]] && kill $maildev_pid
        }

        TMPDIR=''${XDG_RUNTIME_DIR} ${pkgs.nodePackages.maildev}/bin/maildev --smtp $(($PORT_OFFSET + 1025)) --web $(($PORT_OFFSET + 8080)) --ip localhost --web-ip localhost &>/dev/null &
        maildev_pid=$!

        export SMTPHOST=localhost
        export SMTPPORT=$(($PORT_OFFSET + 1025))
        export SMTPSSL=none

        set +xe
      fi
    ''}

    set -xe

    cat >&4 <<EOF
    PORT_OFFSET=''${PORT_OFFSET}

    PGHOST=''${pgSockDir}
    PGLOG=''${pgLogFile}

    AVSPASS=''${AVSPASS:-nopasswordset}

    WIDGET_MEMCACHED_HOST=localhost
    WIDGET_MEMCACHED_PORT=$(($PORT_OFFSET + 11211))

    SESSION_MEMCACHED_HOST=localhost
    SESSION_MEMCACHED_PORT=$(($PORT_OFFSET + 11212))

    MEMCACHED_HOST=localhost
    MEMCACHED_PORT=$(($PORT_OFFSET + 11212))

    MINIO_DIR=''${MINIO_DIR}
    MINIO_LOGFILE=''${MINIO_LOGFILE}
    UPLOAD_S3_HOST=localhost
    UPLOAD_S3_PORT=$(($PORT_OFFSET + 9000))
    UPLOAD_S3_SSL=false
    UPLOAD_S3_KEY_ID=''${MINIO_ACCESS_KEY}
    UPLOAD_S3_KEY=''${MINIO_SECRET_KEY}

    SMTPHOST=''${SMTPHOST}
    SMTPPORT=''${SMTPPORT}
    SMTPSSL=''${SMTPSSL}
    EOF

    set +xe

    if [ -n "$ZSH_VERSION" ]; then
      autoload -U +X compinit && compinit
      autoload -U +X bashcompinit && bashcompinit
    fi
    # eval "$(stack --bash-completion-script stack)"

    $(getent passwd $USER | cut -d: -f 7)
  '';

  inDevelop = pkgs.writeScriptBin "in-develop" ''
    #!${pkgs.zsh}/bin/zsh -e

    if [[ -z "''${PORT_OFFSET}" ]]; then
      echo "Not in develop"
    else
      echo "In develop"
    fi
  '';

  killallUni2work = pkgs.writeScriptBin "killuni2work" ''
    #!${pkgs.zsh}/bin/zsh

    set -o pipefail

    lockFile=

    if [[ ''${#@} -gt 0 ]]; then
      lockFile=''${1}
      shift

      if [[ -d ''${lockFile} && -f ''${lockFile}/.stack-work.lock ]]; then
        lockFile=''${lockFile}/.stack-work.lock
      fi
    else
      if [[ -f .stack-work.lock ]]; then
        lockFile=.stack-work.lock
      elif [[ -f ~/projects/uni2work/.stack-work.lock ]]; then
        lockFile=~/projects/uni2work/.stack-work.lock
      fi
    fi

    if [[ -z "''${lockFile}" && !(-f ''${lockFile}) ]]; then
      echo "Could not find lockfile" >&2
      exit 1
    fi

    printf "Killing users of %s...\n" ''${lockFile}

    while ${pkgs.psmisc}/bin/fuser ''${lockFile} 2>/dev/null | ${pkgs.coreutils}/bin/cut -d ':' -f 2- | ${pkgs.findutils}/bin/xargs -tr -- ${pkgs.util-linux}/bin/kill; do
      sleep 1
    done
  '';

  diffRunning = pkgs.writeScriptBin "diff-running" ''
    #!${pkgs.zsh}/bin/zsh

    git diff $(cut -d '-' -f 1 <(curl -sH 'Accept: text/plain' https://uni2work.ifi.lmu.de/version))
  '';
in pkgs.mkShell {
  name = "fradrive";
  shellHook = ''
    ${pkgs.lib.optionalString (nixpkgsPath != null) ''
      export NIX_PATH=nixpkgs=${nixpkgsPath}
    ''}

    export CHROME_BIN=${pkgs.chromium}/bin/chromium
  '';
  nativeBuildInputs = [develop inDevelop killallUni2work diffRunning]
  ++ (with pkgs;
    [ stdenv coreutils stack nodejs_21 postgresql_12 openldap exiftool expat bzip2 glibc memcached minio minio-client
      gup reuse pre-commit
      # busybox # for print services, but interferes with build commands in develop-shell
      htop
      pdftk # pdftk just for testing pdf-passwords
      # texlive.combined.scheme-full # works
      # texlive.combined.scheme-medium
      # texlive.combined.scheme-small
      (texlive.combine {
        inherit (texlive) scheme-basic
        babel-german babel-english booktabs textpos
        enumitem eurosym koma-script parskip xcolor dejavu
        luatexbase lualatex-math unicode-math selnolig # required for LuaTeX
        ;
      })
    ]
  ) ++ (with pkgs.haskellPackages; [ yesod-bin hlint cabal-install weeder ]);
}
