
export SHELL=bash

# MAKE=make -f Makefile-loggingsymbols
# MAKE=make -d

# System information
export CPU_CORES = $(shell cat /proc/cpuinfo | grep '^processor' | wc -l)

export CONTAINER_COMMAND ?= podman
export CONTAINER_BGRUN ?= $(CONTAINER_COMMAND) run -dit --network=host --replace
export CONTAINER_FGRUN ?= $(CONTAINER_COMMAND) run -it --network=host --replace

export IMAGE_REGISTRY = docker.io
export MEMCACHED_IMAGE = $(IMAGE_REGISTRY)/memcached:latest
export MINIO_IMAGE = $(IMAGE_REGISTRY)/minio/minio:latest
export MAILDEV_IMAGE = $(IMAGE_REGISTRY)/maildev/maildev:latest # TODO: needs different port than 1025 to avoid conflicts

export IN_CONTAINER ?= false
export IN_CI ?= false
export CONTAINER_FILE
export CONTAINER_IDENT
export CF_PREFIX
export DEVELOP
export CONTAINER_ATTACHED
export CONTAINER_INIT
export CONTAINER_CLEANUP
export PROJECT_DIR=/fradrive

export SERVICE
export SERVICE_VARIANT ?= $(SERVICE)
export JOB
export IMAGE
export SET_IMAGE
export ENTRYPOINT
export EXEC_OPTS

export STACK_CORES = $(shell echo $(($(CPU_CORES)/2)))
export BASE_PORTS
export UNIWORXDB_OPTS ?= -cf
export PROD ?= false
export SRC

ifneq ($(PROD),true)
  export --DEVELOPMENT=--flag uniworx:dev
endif

export DATE := $(shell date +'%Y-%m-%dT%H-%M-%S')

export CURR_DEV = $(shell cat develop/.current 2>/dev/null)
export SET_DEVELOP = $(eval DEVELOP=develop/$$(CURR_DEV))
export NEW_DEVELOP = $(eval DEVELOP=develop/$$(DATE))


.PHONY: help
# HELP: print out this help message
help:
	@if [ -z "$$(which perl 2>/dev/null)" ] ; then \
	  $(CONTAINER_FGRUN) .:/mnt 'debian:12.5' '/mnt/utils/makehelp.pl' '/mnt/Makefile' ; \
	else \
	  utils/makehelp.pl Makefile ; \
	fi

.PHONY: clean
# HELP: stop all running containers and remove all compilation results in the directory (but leave images including dependencies unharmed)
clean:
	rm -rf develop
	-rm -rf node_modules .npm .cache assets/icons assets/favicons static well-known config/manifest.json frontend/src/env.sass
	-rm -rf .stack-work .stack-work.lock
	-rm -rf bin .Dockerfile develop
	-$(CONTAINER_COMMAND) container prune --force
.PHONY: clean-all
# HELP: like clean but with full container, image, and volume prune
clean-all: clean
	-rm -rf .stack
	-$(CONTAINER_COMMAND) system prune --all --force --volumes
	-$(CONTAINER_COMMAND) image prune --all --force
	-$(CONTAINER_COMMAND) volume prune --force

.PHONY: release
# HELP: create, commit and push a new release
release:
	./.gitlab-ci/version.pl -changelog CHANGELOG.md
	git add CHANGELOG.md
	VERSION=`.gitlab-ci/version.pl`
	git tag $${VERSION}
	git commit -m "chore(release): $${VERSION}"
#	git push

.PHONY: compile
compile:
	$(MAKE) compile-frontend
	$(MAKE) compile-backend

.PHONY: start
start:
	$(MAKE) start-postgres
	$(MAKE) start-memcached
	$(MAKE) start-minio
	$(MAKE) compile-frontend
	$(MAKE) start-backend

.PHONY: %-backend
%-backend: SERVICE=backend
%-backend: SERVICE_VARIANT=backend
%-backend: IMAGE=localhost/fradrive/backend
%-backend: BASE_PORTS = "DEV_PORT_HTTP=3000" "DEV_PORT_HTTPS=3443"

.PHONY: %-uniworxdb
%-uniworxdb: SERVICE=backend
%-uniworxdb: SERVICE_VARIANT=uniworxdb
%-uniworxdb: IMAGE=localhost/fradrive/backend

.PHONY: %-ghci
%-ghci: SERVICE=backend
%-ghci: SERVICE_VARIANT=ghci
%-ghci: IMAGE=localhost/fradrive/backend

.PHONY: %-hoogle
%-hoogle: SERVICE=backend
%-hoogle: SERVICE_VARIANT=hoogle
%-hoogle: BASE_PORTS = "HOOGLE_PORT=8081"
%-hoogle: IMAGE=localhost/fradrive/backend
--start-hoogle:
	HOOGLE_PORT=`cat $(CONTAINER_FILE) | grep 'HOOGLE_PORT=' | sed 's/HOOGLE_PORT=//'` ; \
	stack $(STACK_CORES) hoogle -- server --local --port $${HOOGLE_PORT}

.PHONY: %-frontend
%-frontend: SERVICE=frontend
%-frontend: SERVICE_VARIANT=frontend
%-frontend: IMAGE=localhost/fradrive/frontend

.PHONY: %-postgres
%-postgres: SERVICE=postgres
%-postgres: SERVICE_VARIANT=postgres
%-postgres: BASE_PORTS = "PGPORT=5432"
%-postgres: IMAGE=localhost/fradrive/postgres

.PHONY: %-memcached
%-memcached: SERVICE=memcached
%-memcached: SERVICE_VARIANT=memcached
%-memcached: SET_IMAGE=$$(MEMCACHED_IMAGE) --port=`cat $$(CONTAINER_FILE) | grep 'MEMCACHED_PORT=' | sed 's/MEMCACHED_PORT=//'`
%-memcached: BASE_PORTS = "MEMCACHED_PORT=11211"

.PHONY: %-minio
%-minio: SERVICE=minio
%-minio: SERVICE_VARIANT=minio
%-minio: SET_IMAGE=$$(MINIO_IMAGE) -- server `mktemp` --address=:`cat $$(CONTAINER_FILE) | grep 'UPLOAD_S3_PORT=' | sed 's/UPLOAD_S3_PORT=//'`
%-minio: BASE_PORTS = "UPLOAD_S3_PORT=9000"

.PHONY: start-%
start-%: JOB=start
start-%: CF_PREFIX = start-
start-%: CONTAINER_ATTACHED = false
start-%: --act ;

.PHONY: compile-%
compile-%: JOB=compile
compile-%: CF_PREFIX = compile-
compile-%: CONTAINER_ATTACHED = true
compile-%: --act ;

.PHONY: dependencies-%
dependencies-%: JOB=dependencies
dependencies-%: CF_PREFIX = dependencies-
dependencies-%: CONTAINER_ATTACHED = true
dependencies-%: --act ;

.PHONY: test-%
test-%: JOB=test
test-%: CF_PREFIX = test-
test-%: CONTAINER_ATTACHED = true
test-%: --act ;

.PHONY: lint-%
lint-%: JOB=lint
lint-%: CF_PREFIX = lint-
lint-%: CONTAINER_ATTACHED = true
lint-%: --act ;

.PHONY: shell-%
# HELP(shell-$SERVICE): launch (bash) shell inside a new $SERVICE container
shell-%: JOB=shell
shell-%: CF_PREFIX=shell-
shell-%: CONTAINER_ATTACHED=true
shell-%: --act ;
.PHONY: ghci
# HELP(ghci): launch new backend instance and enter interactive ghci shell
ghci: shell-ghci;

--act: --develop_containerized;

--develop_%: PORTS = $(foreach PORT,$(BASE_PORTS),$(shell utils/next_free_port.pl $(PORT)))
--develop_%: --ensure-develop
	DEVELOP=develop/`cat develop/.current` ; \
	CONTAINER_IDENT=$(CF_PREFIX)$(SERVICE_VARIANT) ; \
	CONTAINER_FILE=$${DEVELOP}/$${CONTAINER_IDENT} ; \
	if [[ -e $${CONTAINER_FILE} ]]; then \
	  >&2 echo "Another $* service is already running! Use \"make new-develop\" to start a new develop instance despite currently running services." ; \
	  exit 1 ; \
	fi ; \
	echo "$(PORTS)" | sed 's/ /\n/g' > $${CONTAINER_FILE} ; \
	$(MAKE) -- --$* CONTAINER_FILE=$${CONTAINER_FILE} CONTAINER_IDENT=$${CONTAINER_IDENT} JOB=$(JOB)

.PHONY: rebuild-%
# HELP(rebuild-{backend,frontend,database,memcached,minio}): force-rebuild a given container image
rebuild-%:
	$(MAKE) -- --image-build SERVICE=$* NO_CACHE=--no-cache
--image-build:
ifeq "$(IMAGE)" "localhost/fradrive/$(SERVICE)"
	rm -f .Dockerfile
	ln -s docker/$(SERVICE)/Dockerfile .Dockerfile
	PROJECT_DIR=/fradrive; \
	if [ "$(IN_CONTAINER)" == "false" ] ; then \
	  $(CONTAINER_COMMAND) build $(NO_CACHE) \
	    -v $(PWD):$${PROJECT_DIR}:rw \
		--build-arg PROJECT_DIR=$${PROJECT_DIR} \
	    --env IN_CONTAINER=true \
		--env JOB=$(JOB) \
		--tag fradrive/$(SERVICE) \
		--file $(PWD)/.Dockerfile ; \
	fi
else
	:
endif

--containerized: --image-build
	DEVELOP=`cat develop/.current` ; \
	./utils/watchcontainerrun.sh "$(CONTAINER_COMMAND)" "$(CONTAINER_FILE)" "$(CONTAINER_INIT)" "$(CONTAINER_CLEANUP)" & \
	CONTAINER_NAME=fradrive.$(CURR_DEV).$(CONTAINER_IDENT) ; \
	if ! [ -z "$(SET_IMAGE)" ] ; \
	  then \
	    IMAGE="$(SET_IMAGE)" ; \
	  else \
	    IMAGE=$(IMAGE) ; \
	fi ; \
	CONTAINER_ID=`$(CONTAINER_BGRUN) \
	  -v $(PWD):$(PROJECT_DIR):rw \
	  --env IN_CONTAINER=true \
	  --env CONTAINER_FILE=$(CONTAINER_FILE) \
	  --env CONTAINER_NAME=$${CONTAINER_NAME} \
	  --env JOB=$(JOB) \
	  --env SRC=$(SRC) \
	  --name $${CONTAINER_NAME} \
	  $${IMAGE} \
	  make -- --$(JOB)-$(SERVICE_VARIANT) IN_CONTAINER=true \
	` ; \
	printf "CONTAINER_ID=$${CONTAINER_ID}" >> "$(CONTAINER_FILE)" ; \
	if [[ "true" == "$(CONTAINER_ATTACHED)" ]] ; then \
	  $(CONTAINER_COMMAND) attach $${CONTAINER_ID} || : ; \
	fi

# For Reverse Proxy Problem see: https://groups.google.com/g/yesodweb/c/2EO53kSOuy0/m/Lw6tq2VYat4J
# HELP(start-backend): start development instance
--start-backend:
	export YESOD_IP_FROM_HEADER=true; \
	export DEV_PORT_HTTP=`cat $(CONTAINER_FILE) | grep 'DEV_PORT_HTTP=' | sed 's/DEV_PORT_HTTP=//'`; \
	export DEV_PORT_HTTPS=`cat $(CONTAINER_FILE) | grep 'DEV_PORT_HTTPS=' | sed 's/DEV_PORT_HTTPS=//'`; \
	export HOST=127.0.0.1 ; \
	export PORT=$${PORT:-$${DEV_PORT_HTTP}} ; \
	export DETAILED_LOGGING=$${DETAILED_LOGGING:-true} ; \
	export LOG_ALL=$${LOG_ALL:-false} ; \
	export LOGLEVEL=$${LOGLEVEL:-info} ; \
	export DUMMY_LOGIN=$${DUMMY_LOGIN:-true} ; \
	export SERVER_SESSION_ACID_FALLBACK=$${SERVER_SESSION_ACID_FALLBACK:-true} ; \
	export SERVER_SESSION_COOKIES_SECURE=$${SERVER_SESSION_COOKIES_SECURE:-false} ; \
	export COOKIES_SECURE=$${COOKIES_SECURE:-false} ; \
	export ALLOW_DEPRECATED=$${ALLOW_DEPRECATED:-true} ; \
	export ENCRYPT_ERRORS=$${ENCRYPT_ERRORS:-false} ; \
	export RIBBON=$${RIBBON:-$${HOST:-localhost}} ; \
	export APPROOT=$${APPROOT:-http://localhost:$${DEV_PORT_HTTP}} ; \
	export AVSPASS=$${AVSPASS:-nopasswordset} ; \
	stack $(STACK_CORES) exec --local-bin-path $$(pwd)/bin --copy-bins -- yesod devel -p "$${DEV_PORT_HTTP}" -q "$${DEV_PORT_HTTPS}"
# HELP(compile-backend): compile backend binaries
--compile-backend:
	stack build $(STACK_CORES) --fast --profile --library-profiling --executable-profiling --flag uniworx:-library-only $(--DEVELOPMENT) --local-bin-path $$(pwd)/bin --copy-bins
# HELP(dependencies-backend): (re-)build backend dependencies
--dependencies-backend: #uniworx.cabal
	chown -R `id -un`:`id -gn` "$(PROJECT_DIR)"; \
	stack build -j2 --only-dependencies
# HELP(lint-backend): lint backend
--lint-backend:
	stack build $(STACK_CORES) --test --fast --flag uniworx:library-only $(--DEVELOPMENT) uniworx:test:hlint
# HELP(test-backend): test backend
--test-backend:
	stack build $(STACK_CORES) --test --coverage --fast --flag uniworx:library-only $(--DEVELOPMENT)
# uniworx.cabal:
# 	stack exec -- hpack --force

# HELP(compile-frontend): compile frontend assets
--compile-frontend: node_modules assets esbuild.config.mjs frontend/src/env.sass
	npm run build
--start-frontend: --compile-frontend;
--dependencies-frontend: node_modules assets;
node_modules: package.json package-lock.json
	npm install --cache .npm --prefer-offline
package-lock.json: package.json
	npm install --cache .npm --prefer-offline
assets: assets/favicons assets/icons;
assets/favicons:
	./utils/faviconize.pl assets/favicon.svg long assets/favicons
assets/icons: node_modules assets/icons-src/fontawesome.json
	./utils/renamer.pl node_modules/@fortawesome/fontawesome-free/svgs/solid assets/icons-src/fontawesome.json assets/icons/fradrive
	./utils/renamer.pl node_modules/@fortawesome/fontawesome-free/svgs/regular assets/icons-src/fontawesome.json assets/icons/fradrive
	-cp assets/icons-src/*.svg assets/icons/fradrive
frontend/src/env.sass:
	echo "\$$path: '$${PROJECT_DIR}'" > frontend/src/env.sass
static: node_modules assets esbuild.config.mjs frontend/src/env.sass
	npm run build
well-known: static;
--lint-frontend: --compile-frontend
	npm run lint
--test-frontend: --compile-frontend
	npm run test

# HELP(compile-uniworxdb): clear and fill database. requires running postgres instance (use "make start-postgres" to start one)
# TODO (db-m-$MIGRATION-backend): apply migration (see src/Model/Migration/Definition.hs for list of available migrations)
--compile-uniworxdb: --compile-backend
	SERVER_SESSION_ACID_FALLBACK=${SERVER_SESSION_ACID_FALLBACK:-true} ; \
	AVSPASS=${AVSPASS:-nopasswordset} ; \
	./bin/uniworxdb $(UNIWORXDB_OPTS)

--shell-ghci:
	stack ghci -- $(SRC)
# --main-is uniworx:exe:uniworx

# HELP(shell-{backend,frontend,memcached,minio,postgres}): enter (bash) shell inside a new container of a given service
--shell-%:
	/bin/bash

# HELP(start-minio): start minio service

.PHONY: status
# HELP: print develop status: running containers, used ports
status:
	@./utils/develop-status.pl -a

.PHONY: log-%
# HELP(log-$(JOB)-$(SERVICE)): inspect output of a given service. The service must be currently running When a service supports multiple running instances in one develop (i.e. backend), you need to specify the exact instance by its associated file (e.g. backend-1, backend-2, etc.), please check the contents of the develop/ directory for a list of running instances.
log-%:
	DEVELOP=develop/`cat develop/.current` ; \
	SEARCH_FILE="$${DEVELOP}/$*" ; \
	if [[ ! -e "$${SEARCH_FILE}" ]] ; then \
	  SEARCH_FILE="$${DEVELOP}/.exited.$*" ; \
	fi ; \
	if [[ -e "$${SEARCH_FILE}" ]] ; then \
	  $(CONTAINER_COMMAND) logs --follow `cat "$${SEARCH_FILE}" | grep CONTAINER_ID= | sed 's/^CONTAINER_ID=//'` ; \
	else \
	  >&2 echo "Cannot show log: No develop file found for '$*'" ; \
	  exit 1 ; \
	fi

.PHONY: enter
# HELP: launch (bash) shell inside a currently running container. Use ./enter shell wrapper for more convenient usage, possibly with tab-completion in the future
enter: --ensure-develop
	$(MAKE) -- --enter

.PHONY: psql
# HELP: enter psql (postgresql) cli inside a currently running database container
psql: ENTRYPOINT=/usr/bin/psql -d uniworx
psql: EXEC_OPTS=--user postgres
psql: --ensure-develop
	$(MAKE) -- --enter CONTAINER_FILE=develop/`cat develop/.current`/start-postgres

--enter:
	CONTAINER_ID=`cat $(CONTAINER_FILE) | grep 'CONTAINER_ID=' | sed 's/CONTAINER_ID=//'` ; \
	$(CONTAINER_COMMAND) exec -it $(EXEC_OPTS) $${CONTAINER_ID} $(if $(ENTRYPOINT),$(ENTRYPOINT),/bin/bash)

.PHONY: stop
# HELP: stop all currently running develop instances
stop:
	rm -rf develop
.PHONY: stop-%
# HELP(stop-SERVICE): stop all currently running develop instances of a given service (i.e. backend,frontend,uniworxdb,hoogle,postgres,...)
# HELP(stop-JOB): stop all currently running develop instances of a given job (i.e. compile,start,test,lint)
stop-compile: CF_PREFIX=compile-
stop-start: CF_PREFIX=start-
stop-test: CF_PREFIX=test-
stop-lint: CF_PREFIX=lint-
stop-%: --stop;
--stop:
	$(SET_DEVELOP)
ifdef CF_PREFIX
	rm -rf $(DEVELOP)/$(CF_PREFIX)*
endif
ifdef SERVICE_VARIANT
	rm -rf $(DEVELOP)/*-$(SERVICE_VARIANT)
endif

.PHONY: new-develop
# HELP: instantiate new development bundle, i.e. create new directory under develop/
new-develop:
	$(NEW_DEVELOP)
	mkdir -p $(DEVELOP)
	$(MAKE) develop/.current
.PHONY: switch-develop
# HELP: switch current develop instance to DEVELOP=...
switch-develop:
	if ! [ -e develop/$(DEVELOP) ]; then \
	  echo "Specified develop $(DEVELOP) does not exist! Not switching." ; \
	  exit 1 ; \
	fi ; \
	echo "$(DEVELOP)" > develop/.current
--ensure-develop:
	if ! [[ -e develop ]]; then \
	  $(MAKE) new-develop; \
	fi
	$(MAKE) develop/.current
	$(SET_DEVELOP)
.PHONY: develop/.current
develop/.current:
	ls -1 develop | tail -n1 > develop/.current

.PHONY: --%
.SUFFIXES: # Delete all default suffixes
