# Operational Makefile for services adopting spaps-server-quickstart
# Battle-tested patterns from consumer_server

PYTHON ?= python3.12
POETRY ?= poetry
REGISTRY_IMAGE ?= ghcr.io/example/service
GIT_SHA ?= $(shell git rev-parse --short HEAD)
DEPLOY_HOST ?= your-prod-host
DEPLOY_USER ?= root

# Pytest parallelism
PYTEST_WORKERS_MAX ?= 4
PYTEST_WORKERS ?= $(shell $(POETRY) run python -c "import sys; print(min($(PYTEST_WORKERS_MAX), max(1, __import__('os').cpu_count() or 1)))" 2>/dev/null || echo 1)

.PHONY: install lint format typecheck \
	pytest pytest-full pytest-init pytest-cov test \
	prepush \
	docker-build docker-push deploy \
	local-up local-up-watch local-up-prod-fresh local-down \
	worker beat

# ---------------------------------------------------------------------------
# Setup
# ---------------------------------------------------------------------------

install:
	$(POETRY) install

ensure-venv:
	@test -d .venv || (echo "No .venv found. Run 'make install' first." && exit 1)

# ---------------------------------------------------------------------------
# Code quality
# ---------------------------------------------------------------------------

format:
	$(POETRY) run ruff format src tests
	$(POETRY) run ruff check src tests --select I --fix

lint:
	$(POETRY) run ruff check src tests
	$(POETRY) run mypy src

typecheck:
	$(POETRY) run mypy src

# ---------------------------------------------------------------------------
# Testing
# ---------------------------------------------------------------------------

# Testmon-aware: only run tests affected by recent changes.
# Falls back to full suite if testmon unavailable.
pytest:
	SPAPS_ENV=test $(POETRY) run pytest \
		--testmon \
		-n $(PYTEST_WORKERS) \
		-q --tb=short 2>/dev/null \
	|| SPAPS_ENV=test $(POETRY) run pytest -q --tb=short

# Full suite with parallelism (ignores testmon)
pytest-full:
	SPAPS_ENV=test $(POETRY) run pytest \
		-n $(PYTEST_WORKERS) \
		-q --tb=short

# Rebuild testmon database from scratch
pytest-init:
	SPAPS_ENV=test $(POETRY) run pytest \
		--testmon-nocollect \
		-n $(PYTEST_WORKERS) \
		-q --tb=short

# Coverage report
pytest-cov:
	SPAPS_ENV=test $(POETRY) run pytest \
		--cov --cov-report=term-missing --cov-report=html \
		-q --tb=short

# CI-friendly: lint + typecheck + testmon test
test: lint typecheck pytest

# Pre-push validation (stricter: format + full suite)
prepush: format lint typecheck pytest-full

# ---------------------------------------------------------------------------
# Docker
# ---------------------------------------------------------------------------

docker-build:
	docker build --pull -t $(REGISTRY_IMAGE):$(GIT_SHA) -f Dockerfile.prod .

docker-push:
	docker push $(REGISTRY_IMAGE):$(GIT_SHA)
	docker push $(REGISTRY_IMAGE):latest

deploy:
	REGISTRY_IMAGE=$(REGISTRY_IMAGE) IMAGE_TAG=$(GIT_SHA) \
	GHCR_DEPLOY_USER=$(GHCR_DEPLOY_USER) GHCR_DEPLOY_TOKEN=$(GHCR_DEPLOY_TOKEN) \
	bash deploy/deploy.sh $(DEPLOY_HOST) $(DEPLOY_USER)

# ---------------------------------------------------------------------------
# Local development
# ---------------------------------------------------------------------------

# Start local dev stack (fetches prod DB on first run, caches thereafter)
local-up:
	./scripts/dev/local-debug-loop.sh

# Start with log streaming (watch mode)
local-up-watch:
	LOCAL_WATCH=1 ./scripts/dev/local-debug-loop.sh

# Force fresh production DB fetch
local-up-prod-fresh:
	$(shell echo $${SERVICE_NAME:-SERVICE}_PROD_DB_FRESH)=1 ./scripts/dev/local-debug-loop.sh

# Tear down local stack
local-down:
	docker compose down --volumes --remove-orphans 2>/dev/null || docker-compose down --volumes --remove-orphans

# ---------------------------------------------------------------------------
# Celery
# ---------------------------------------------------------------------------

worker:
	$(POETRY) run celery -A $(shell basename $(CURDIR)).celery_app worker --loglevel=info

beat:
	$(POETRY) run celery -A $(shell basename $(CURDIR)).celery_app beat --loglevel=info
