# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later

{ pkgs
, prev ? pkgs
, doPortOffset ? true
, doDevelopEnv ? true
}:

with prev.lib;

let
  withDevelop = action: ''
    #!${pkgs.zsh}/bin/zsh -e

    ${optionalString doDevelopEnv ''
        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

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

    trap cleanup EXIT

    export PORT_OFFSET=${if doPortOffset then "$(((16#$(echo \"fradrive $(whoami)\" | sha256sum | head -c 16)) % 1000))" else "0"}

    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 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
    ''}

    ${optionalString doDevelopEnv ''
        set -xe

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

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

        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
    ''}
    
    ${action}
  '';

  postgresSchema = prev.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 = prev.writeText "hba_file" ''
    local all all trust
  '';
in withDevelop
