UV ?= uv
VENV ?= .venv
BIN := $(VENV)/bin
PY_VERSION ?= 3.12
HOST ?= 127.0.0.1
PORT ?= 8000
# Which torch build the dev venv gets (ADR-0007): `cpu` (PyTorch CPU wheels; the
# regular PyPI build on macOS) or `cuda`. Every sync passes it, because `uv sync`
# removes extras it isn't given and would swap torch for PyPI's CUDA build.
ACCEL ?= cpu

# Image build knobs (ADR-0001). IMAGE/TAG name the image; LAYA_FORK, when set to
# a `git+…@ref` spec, installs laya from a fork instead of the pinned release.
IMAGE ?= whatdo
TAG ?= dev
DOCKER ?= docker
LAYA_FORK ?=
FORK_BUILD_ARG := $(if $(LAYA_FORK),--build-arg LAYA_FORK="$(LAYA_FORK)",)

# Load-test knobs (#12). Point BASE_URL at a running deployment; set API_KEY if
# it runs with auth enabled. Passed through to k6 as environment variables.
K6 ?= k6
BASE_URL ?= http://localhost:8000
API_KEY ?=
K6_ENV := -e BASE_URL=$(BASE_URL) $(if $(API_KEY),-e API_KEY=$(API_KEY),)

.PHONY: setup lock lint fmt typecheck test test-otel test-slow run \
	build-cpu build-cuda dist docs docs-serve load-test

## Create the dev virtualenv (.venv) from uv.lock: base deps (incl. laya +
## torch for ACCEL) + dev group + the editable package. Reproducible; installs
## nothing that isn't in the lock. On a CUDA box: make setup ACCEL=cuda.
setup:
	$(UV) sync --extra $(ACCEL)
	git config core.hooksPath .githooks
	@echo "Dev environment ready. Git hooks -> .githooks (pre-push runs the fast suite)."

## Refresh uv.lock from pyproject, then export per-accelerator pinned requirements
## for non-uv installs (base runtime, cpu, cuda). uv.lock is the source of truth.
lock:
	$(UV) lock
	$(UV) export --no-dev --no-emit-project --no-hashes -o requirements.txt
	$(UV) export --no-dev --no-emit-project --no-hashes --extra cpu -o requirements-cpu.txt
	$(UV) export --no-dev --no-emit-project --no-hashes --extra cuda -o requirements-cuda.txt

## Lint + format check (no changes).
lint:
	$(BIN)/ruff check .
	$(BIN)/ruff format --check .

## Auto-format and auto-fix.
fmt:
	$(BIN)/ruff format .
	$(BIN)/ruff check --fix .

## Static type checking (strict on the package).
typecheck:
	$(BIN)/mypy

## Fast test suite (excludes GPU/real-model tests).
test:
	$(BIN)/pytest -m "not slow"

## Sync with the [otel] extra, then run the suite so the OTEL-on tests execute.
test-otel:
	$(UV) sync --extra otel --extra $(ACCEL)
	$(BIN)/pytest -m "not slow"

## Slow suite: real Laya engine on real checkpoints. Requires a GPU; run locally.
test-slow:
	$(BIN)/pytest -m slow

## Run the app locally with autoreload. Serves the real Laya engine (the first
## run downloads the checkpoint); WHATDO_MODEL__ENGINE=fake make run for a
## model-free server when only testing the HTTP layer.
run:
	$(BIN)/uvicorn whatdo.app:app --host $(HOST) --port $(PORT) --reload

## Build the trim CPU image. Pass LAYA_FORK=git+<url>@<ref> to use a laya fork.
build-cpu:
	$(DOCKER) build --build-arg ACCEL=cpu $(FORK_BUILD_ARG) -t $(IMAGE):cpu-$(TAG) .

## Build the CUDA image (builds on CPU-only hosts; running inference needs a GPU).
build-cuda:
	$(DOCKER) build --build-arg ACCEL=cuda $(FORK_BUILD_ARG) -t $(IMAGE):cuda-$(TAG) .

## Build the sdist + wheel into dist/ (version derived by setuptools-scm).
dist:
	rm -rf dist
	$(UV) build
## Build the documentation site (strict — matches CI). Syncs the docs group first
## so mkdocs/mkdocstrings and the package are available.
docs:
	$(UV) sync --group docs --extra $(ACCEL)
	$(BIN)/mkdocs build --strict

## Serve the docs locally with live reload.
docs-serve:
	$(UV) sync --group docs --extra $(ACCEL)
	$(BIN)/mkdocs serve

## Run the k6 load tests against a running deployment (needs k6; not gated in CI).
## Point at a real-model server: make load-test BASE_URL=... [API_KEY=...]
load-test:
	$(K6) run $(K6_ENV) tests/load/smoke.js
	$(K6) run $(K6_ENV) tests/load/load.js
