CLI_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(sort $(subst :,\:,$(CLI_ARGS))):;@:)

PRIMARY_GOAL := $(firstword $(MAKECMDGOALS))
ifeq ($(PRIMARY_GOAL),)
    PRIMARY_GOAL := help
endif

include docker/.env

# Current user ID and group ID except MacOS where it conflicts with Docker abilities
ifeq ($(shell uname), Darwin)
    export UID=1000
    export GID=1000
else
    export UID=$(shell id -u)
    export GID=$(shell id -g)
endif

export COMPOSE_PROJECT_NAME=${STACK_NAME}
DOCKER_COMPOSE_DEV := docker compose -f docker/compose.yml -f docker/dev/compose.yml
DOCKER_COMPOSE_TEST := docker compose -f docker/compose.yml -f docker/test/compose.yml

#
# Development
#

ifeq ($(PRIMARY_GOAL),build)
build: ## Build docker images.
	$(DOCKER_COMPOSE_DEV) build $(CLI_ARGS)
endif

ifeq ($(PRIMARY_GOAL),up)
up: ## Up the dev environment.
	@set -eu; \
	if $(DOCKER_COMPOSE_DEV) up -d --wait --wait-timeout 60 --remove-orphans; then \
		port="$$( $(DOCKER_COMPOSE_DEV) port app 80 )"; \
		host_port="$${port##*:}"; \
		url="http://localhost$$( [ "$$host_port" = '80' ] || printf ':%s' "$$host_port" )"; \
		printf '🚀 Started server at %s\n' "$$url"; \
	else \
		echo '❌ Failed to start server.' >&2; \
		container_id="$$( $(DOCKER_COMPOSE_DEV) ps -a -q app 2>/dev/null || true )"; \
			if [ -n "$$container_id" ]; then \
				state="$$(docker inspect -f '{{.State.Status}}' "$$container_id" 2>/dev/null || true)"; \
				health="$$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{end}}' "$$container_id" 2>/dev/null || true)"; \
				exit_code="$$(docker inspect -f '{{.State.ExitCode}}' "$$container_id" 2>/dev/null || true)"; \
				error="$$(docker inspect -f '{{.State.Error}}' "$$container_id" 2>/dev/null || true)"; \
				if [ -n "$$error" ]; then \
					exit 1; \
				fi; \
				[ -n "$$health" ] && echo "Container health: $$health" >&2; \
				[ -n "$$state" ] && echo "Container state: $$state" >&2; \
				[ -n "$$exit_code" ] && echo "Container exit code: $$exit_code" >&2; \
				echo 'Recent logs:' >&2; \
				$(DOCKER_COMPOSE_DEV) logs --tail=50 app >&2 || true; \
			fi; \
		exit 1; \
	fi
endif

ifeq ($(PRIMARY_GOAL),open)
open: ## Open the running app in the default browser.
	@set -eu; \
	if ! port="$$( $(DOCKER_COMPOSE_DEV) port app 80 2>/dev/null )" || [ -z "$$port" ]; then \
		echo 'Start server with `make up` first.' >&2; \
		exit 0; \
	fi; \
	host_port="$${port##*:}"; \
	url="http://localhost$$( [ "$$host_port" = '80' ] || printf ':%s' "$$host_port" )"; \
	opener="$$(command -v xdg-open || command -v open || command -v wslview || true)"; \
	if [ -z "$$opener" ]; then \
		echo "Could not open $$url." >&2; \
		exit 0; \
	fi; \
	"$$opener" "$$url" >/dev/null 2>&1 &
endif

ifeq ($(PRIMARY_GOAL),down)
down: ## Down the dev environment.
	$(DOCKER_COMPOSE_DEV) down --remove-orphans
endif

ifeq ($(PRIMARY_GOAL),stop)
stop: ## Stop the dev environment.
	$(DOCKER_COMPOSE_DEV) stop
endif

ifeq ($(PRIMARY_GOAL),clear)
clear: ## Remove development docker containers and volumes.
	$(DOCKER_COMPOSE_DEV) down --volumes --remove-orphans
endif

ifeq ($(PRIMARY_GOAL),shell)
shell: ## Get into container shell.
	$(DOCKER_COMPOSE_DEV) exec app /bin/bash
endif

#
# Tools
#

ifeq ($(PRIMARY_GOAL),yii)
yii: ## Execute Yii command.
	$(DOCKER_COMPOSE_DEV) run --rm app ./yii $(CLI_ARGS)
.PHONY: yii
endif

ifeq ($(PRIMARY_GOAL),composer)
composer: ## Run Composer.
	$(DOCKER_COMPOSE_DEV) run --rm app composer $(CLI_ARGS)
endif

ifeq ($(PRIMARY_GOAL),rector)
rector: ## Run Rector.
	$(DOCKER_COMPOSE_DEV) run --rm app ./vendor/bin/rector $(CLI_ARGS)
endif

ifeq ($(PRIMARY_GOAL),cs-fix)
cs-fix: ## Run PHP CS Fixer.
	$(DOCKER_COMPOSE_DEV) run --rm app ./vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff
endif

#
# Tests and analysis
#

ifeq ($(PRIMARY_GOAL),test)
test: ## Run tests.
	$(DOCKER_COMPOSE_TEST) run --rm app ./vendor/bin/codecept run $(CLI_ARGS)
endif

ifeq ($(PRIMARY_GOAL),test-coverage)
test-coverage: ## Run tests with coverage.
	$(DOCKER_COMPOSE_TEST) run --rm app ./vendor/bin/codecept run --coverage --coverage-html --disable-coverage-php
endif

ifeq ($(PRIMARY_GOAL),codecept)
codecept: ## Run Codeception.
	$(DOCKER_COMPOSE_TEST) run --rm app ./vendor/bin/codecept $(CLI_ARGS)
endif

ifeq ($(PRIMARY_GOAL),psalm)
psalm: ## Run Psalm.
	$(DOCKER_COMPOSE_DEV) run --rm app ./vendor/bin/psalm $(CLI_ARGS)
endif

ifeq ($(PRIMARY_GOAL),composer-dependency-analyser)
composer-dependency-analyser: ## Run Composer Dependency Analyser.
	$(DOCKER_COMPOSE_DEV) run --rm app ./vendor/bin/composer-dependency-analyser --config=composer-dependency-analyser.php $(CLI_ARGS)
endif

#
# Production
#

ifeq ($(PRIMARY_GOAL),prod-build)
prod-build: ## Build an image.
	docker build --file docker/Dockerfile --target prod --pull -t ${IMAGE}:${IMAGE_TAG} .
endif

ifeq ($(PRIMARY_GOAL),prod-push)
prod-push: ## Push image to repository.
	docker push ${IMAGE}:${IMAGE_TAG}
endif

ifeq ($(PRIMARY_GOAL),prod-deploy)
prod-deploy: ## Deploy to production.
	set -euo pipefail \
	docker -H ${PROD_SSH} stack deploy --prune --detach=false --with-registry-auth -c docker/compose.yml -c docker/prod/compose.yml ${STACK_NAME} 2>&1 | tee deploy.log \
	if grep -qiE 'rollback:|update rolled back|service update paused' deploy.log then \
		FAILED_TASK_ID="$(grep -oiE 'task[[:space:]]+[a-z0-9]+' deploy.log | head -n 1 | awk '{print $2}')" \
		if [ -n "${FAILED_TASK_ID}" ]; then \
			echo "Docker Swarm update failed. Failed task ID: ${FAILED_TASK_ID}" \
			echo "--- docker service logs (${FAILED_TASK_ID}) ---" \
			docker -H ${PROD_SSH} service logs --timestamps --tail 500 "${FAILED_TASK_ID}" || true \
		else \
			echo 'Docker Swarm update failed. Failed task ID: not found in deploy output.' \
		fi \
		exit 1 \
	fi
endif

#
# Other
#

ifeq ($(PRIMARY_GOAL),help)
help: ## This help.
	@awk 'BEGIN { printf "\nUsage:\n  make \033[36m<target>\033[0m\n" } \
	/^#$$/ { blank = 1; next } \
	blank && /^# [a-zA-Z]/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 3); blank = 0; next } \
	/^[a-zA-Z_-]+:([^=]|$$)/ { \
		split($$0, parts, "##"); \
		target = parts[1]; sub(/:.*/, "", target); \
		desc = parts[2]; \
		gsub(/^[[:space:]]+|[[:space:]]+$$/, "", desc); \
		printf "  \033[36m%-25s\033[0m %s\n", target, desc; \
		blank = 0; \
	}' $(MAKEFILE_LIST)
endif
