VENV := .venv
PY   := $(VENV)/bin/python
PIP  := $(VENV)/bin/pip

# Resolve black config from the repo root (one directory up).
# This avoids duplicating line-length / target-version between server and CLI.
ROOT_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..)

# setuptools_scm version, resolved on the host from the installed _version.py.
# Injected via SETUPTOOLS_SCM_PRETEND_VERSION_FOR_* so the sdist build skips the
# VCS file-finder (which root=".." would otherwise use to pull in the whole
# monorepo), and so in-container builds (no git / no repo-root mount) get it too.
DIST_ENV := SETUPTOOLS_SCM_PRETEND_VERSION_FOR_LR_LUMENTEST_CLI
SCM_VERSION = $(shell $(PY) -c "import lumentest._version as v; print(v.version)" 2>/dev/null)

# Only the sdist and wheel are valid PyPI distributions; dist/ also holds the
# PyInstaller one-file binary and the linux-arm64/ build tree, so a bare
# dist/* glob trips twine on files it was never meant to see.
DIST_FILES := dist/*.whl dist/*.tar.gz

PYPI_REPOSITORY ?= https://upload.pypi.org/legacy/
TEST_PYPI_REPOSITORY ?= https://test.pypi.org/legacy/

# The PyPI token lives in the releasing developer's shell, exported by hand
# before running `publish`/`publish-test` — the same arrangement lr-gladiator
# and lr-qrm use. No CI job holds it, so a release reaches PyPI only when a
# person runs the target.
#
# setuptools_scm derives SCM_VERSION from the `lumentest-*` tag: any commit
# past the tag carries a `.devN+g<sha>` suffix. PyPI rejects a local (`+…`)
# segment outright, and a `.dev` upload permanently spends a version number
# the index can never reuse — so both upload targets refuse anything but a
# clean tag.
CHECK_RELEASE_VERSION = case "$(SCM_VERSION)" in *dev*|*+*|"") echo "error: refusing to upload $(SCM_VERSION) — build from a clean 'lumentest-X.Y.Z' tag"; exit 2 ;; esac

.DEFAULT_GOAL := install

.PHONY: install dev test test-only format dist check publish publish-test shell clean package build-linux-docker build-linux-arm64-docker build-windows-docker

$(VENV)/bin/activate:
	python3.12 -m venv $(VENV)
	$(PIP) install --upgrade pip

install: $(VENV)/bin/activate
	$(PIP) install .

dev: $(VENV)/bin/activate
	$(PIP) install -e ".[dev,package]"

# `test` is the whole job: sync the venv, then run pytest. `test-only` is the
# pytest half on its own, for a driver that has already done the `dev` pass and
# wants to run several sub-trees at once -- the editable installs `dev` performs
# write in-tree build artefacts, so they must not overlap; pytest runs may.
test: dev test-only

test-only:
	$(VENV)/bin/pytest \
		--cov=lumentest \
		--cov-report=term-missing \
		--cov-report=html:htmlcov \
		--junit-xml=test-results.xml \
		tests/

format: dev
	$(VENV)/bin/black --config $(ROOT_DIR)/pyproject.toml src/ tests/

# Clearing the previous sdist and wheel is what keeps `$(DIST_FILES)` meaning
# "this build": every artefact accumulates under its own version, and an upload
# globbing the directory would carry a stale version along with the current one.
# The PyInstaller binary and the arm64 tree in dist/ are left alone.
dist: dev
	rm -f dist/*.whl dist/*.tar.gz
	$(DIST_ENV)="$(SCM_VERSION)" $(PY) -m build

check: dist
	$(VENV)/bin/twine check $(DIST_FILES)

# Both upload targets pass credentials through TWINE_USERNAME/TWINE_PASSWORD,
# which twine reads natively, and silence the upload line with `@`. `-u`/`-p`
# would put the token in the echoed recipe line and in the upload process's
# argv, where `ps` can read it — visible in a developer's scrollback and, since
# this target also runs as a CI job, in a captured job log.
publish-test: check
	@$(CHECK_RELEASE_VERSION)
	@if [ -z "$$TEST_PYPI_TOKEN" ]; then echo "error: TEST_PYPI_TOKEN is required — create one at https://test.pypi.org/manage/account/token/"; exit 2; fi
	@TWINE_USERNAME=__token__ TWINE_PASSWORD="$$TEST_PYPI_TOKEN" $(VENV)/bin/twine upload --repository-url $(TEST_PYPI_REPOSITORY) $(DIST_FILES)

publish: check
	@$(CHECK_RELEASE_VERSION)
	@if [ -z "$$PYPI_TOKEN" ]; then echo "error: PYPI_TOKEN is required — create one at https://pypi.org/manage/account/token/"; exit 2; fi
	@TWINE_USERNAME=__token__ TWINE_PASSWORD="$$PYPI_TOKEN" $(VENV)/bin/twine upload --repository-url $(PYPI_REPOSITORY) $(DIST_FILES)

shell:
	@echo "source $(VENV)/bin/activate"

clean:
	rm -rf $(VENV) dist/ build/ htmlcov/ test-results.xml .coverage
	find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null; true
	find . -name "*.pyc" -delete 2>/dev/null; true
	find . -name "*.egg-info" -type d -exec rm -rf {} + 2>/dev/null; true

package: dev
	$(VENV)/bin/pyinstaller lumentest.spec

# ubuntu:24.04 caps the glibc floor at 2.39 by construction.
build-linux-docker: dev
	docker run --rm \
		-e $(DIST_ENV)="$(SCM_VERSION)" \
		-e DEBIAN_FRONTEND=noninteractive \
		-v "$(CURDIR):/src" \
		-w /src \
		ubuntu:24.04 \
		bash -c "apt-get update -qq && \
		apt-get install -y --no-install-recommends python3 python3-venv python3-pip binutils && \
		python3 -m venv /build-venv && \
		/build-venv/bin/pip install --no-cache-dir --upgrade pip setuptools wheel && \
		/build-venv/bin/pip install --no-cache-dir -e ".[package]" && \
		/build-venv/bin/pyinstaller lumentest.spec; \
		rc=\$$?; chown -R $(shell id -u):$(shell id -g) build dist src/*.egg-info 2>/dev/null; exit \$$rc"

build-linux-arm64-docker: dev
	docker run --rm \
		--platform linux/arm64 \
		-e $(DIST_ENV)="$(SCM_VERSION)" \
		-e DEBIAN_FRONTEND=noninteractive \
		-v "$(CURDIR):/src" \
		-w /src \
		ubuntu:24.04 \
		bash -c "apt-get update -qq && \
		apt-get install -y --no-install-recommends python3 python3-venv python3-pip binutils && \
		python3 -m venv /build-venv && \
		/build-venv/bin/pip install --no-cache-dir --upgrade pip setuptools wheel && \
		/build-venv/bin/pip install --no-cache-dir -e ".[package]" && \
		/build-venv/bin/pyinstaller lumentest.spec --distpath dist/linux-arm64 --workpath build/linux-arm64; \
		rc=\$$?; chown -R $(shell id -u):$(shell id -g) build dist src/*.egg-info 2>/dev/null; exit \$$rc"

build-windows-docker: dev
	docker run --rm \
		--entrypoint bash \
		-e $(DIST_ENV)="$(SCM_VERSION)" \
		-v "$(CURDIR):/src" \
		-w /src \
		batonogov/pyinstaller-windows:v5.0.0 \
		-c "pip install --no-cache-dir --upgrade -e . && pyinstaller lumentest.spec; \
		rc=\$$?; chown -R $(shell id -u):$(shell id -g) build dist src/*.egg-info 2>/dev/null; exit \$$rc"
