.PHONY: help setup worktree-setup quickstart demo demo-rag dev worker langfuse-env observability-langfuse observability-langfuse-down test lint format check check-core check-full check-minimal smoke-examples perf-test clean diagrams demo-gif

LANGFUSE_ENV_FILE := _env/langfuse.env
MINIMAL_UV_ENV := /tmp/fastapi-agent-blueprint-minimal-venv

## Show available commands
help:
	@echo "Usage: make <command>"
	@echo ""
	@awk '/^## /{desc=substr($$0,4)} /^[a-zA-Z_-]+:/{if(desc){printf "  \033[36m%-32s\033[0m %s\n", $$1, desc; desc=""}}' $(MAKEFILE_LIST)

.DEFAULT_GOAL := help

## Setup development environment (includes admin + aws extras for full dev coverage)
setup:
	uv venv && uv sync --group dev --extra admin --extra aws && uv run pre-commit install && uv run pre-commit install --hook-type commit-msg

# Idempotent and tool-independent: worktree managers (Paseo, Orca) point their
# post-create hook here, and it also works when run by hand. Tools that read
# .worktreeinclude (Claude Code, Conductor, Roo Code, CodeBuddy) copy the env
# files themselves; the extras still need this target. The copy list below
# mirrors .worktreeinclude — keep the two in sync.
## Bootstrap a fresh git worktree (copy gitignored env files + install extras)
worktree-setup:
	@main=$$(git worktree list --porcelain | sed -n '1s/^worktree //p'); \
	here=$$(git rev-parse --show-toplevel); \
	if [ -n "$$main" ] && [ "$$main" != "$$here" ]; then \
		for f in local.env quickstart.env; do \
			if [ ! -f "_env/$$f" ] && [ -f "$$main/_env/$$f" ]; then \
				cp "$$main/_env/$$f" "_env/$$f" && echo "→ copied _env/$$f from $$main"; \
			fi; \
		done; \
	fi
	@echo "→ Syncing dependencies (dev group + admin/aws extras)"
	uv sync --group dev --extra admin --extra aws

## Zero-config quickstart: SQLite + InMemory broker, no external infra
quickstart:
	@if [ ! -f _env/quickstart.env ]; then \
		echo "→ Creating _env/quickstart.env from template"; \
		cp _env/quickstart.env.example _env/quickstart.env; \
	fi
	@echo "→ Syncing dependencies (includes admin extra for the dashboard)"
	@# `uv sync --extra admin` installs exactly that, so every *other* extra —
	@# aws, otel, pydantic-ai, sqs, rabbitmq — is uninstalled. Right for a
	@# first-time user, a trap inside a dev checkout: `pytest tests/` then aborts
	@# during collection (two modules import `aioboto3`) and `uv run pyright`
	@# reports 47 unresolved imports. The `dev` group itself survives, because uv
	@# syncs default groups regardless of `--extra`.
	@#
	@# Detected rather than announced to everyone: `aioboto3` comes only from the
	@# aws extra, so if it was importable before the sync and is gone after, this
	@# was a development environment.
	@had_extras=$$(ls -d .venv/lib/python*/site-packages/aioboto3 2>/dev/null | head -1); \
	uv sync --extra admin; \
	if [ -n "$$had_extras" ] && [ ! -d "$$had_extras" ]; then \
		echo ""; \
		echo "!  This was a development environment. The sync above uninstalled the"; \
		echo "!  aws / otel / pydantic-ai / sqs / rabbitmq extras, which quickstart"; \
		echo "!  does not need. Until you restore them, 'pytest tests/' aborts during"; \
		echo "!  collection and 'uv run pyright' reports unresolved imports."; \
		echo "!  Restore with:  make setup"; \
		echo ""; \
	fi
	@echo "→ Starting FastAPI server on http://127.0.0.1:8001"
	@echo "  API docs:   http://127.0.0.1:8001/docs"
	@echo "  Admin:      http://127.0.0.1:8001/admin (admin / admin)"
	@echo "  Run demo:   make demo  (in another terminal)"
	@uv run python run_server_local.py --env quickstart

## Hit the running quickstart server with sample user requests
demo:
	@bash scripts/demo.sh

## End-to-end RAG showcase: seed 3 docs, list, run a query (needs quickstart server)
demo-rag:
	@bash scripts/demo-rag.sh

## Render architecture-diagrams.md Mermaid blocks to SVG (needs npx)
diagrams:
	@bash scripts/render-diagrams.sh

## Re-record docs/assets/cast/demo.gif from demo.tape (needs vhs + ffmpeg)
demo-gif:
	@bash scripts/record-demo-gif.sh

## Start local development (postgres + server)
dev:
	docker compose -f docker-compose.local.yml up -d postgres && \
	sleep 2 && \
	uv run alembic upgrade head && \
	uv run python run_server_local.py --env local

## Start worker locally (cross-process broker required — set BROKER_TYPE=rabbitmq|sqs)
worker:
	uv run python run_worker_local.py --env local

## Start Taskiq scheduler locally — separate process from the worker; enqueues
## @broker.task schedule labels. Skip in deployments that prefer external cron.
scheduler:
	uv run python run_scheduler_local.py --env local

## Create local Langfuse env file with random secrets
langfuse-env: $(LANGFUSE_ENV_FILE)

$(LANGFUSE_ENV_FILE):
	@echo "→ Creating $(LANGFUSE_ENV_FILE) with local random secrets"
	@set -e; \
		mkdir -p "$(dir $(LANGFUSE_ENV_FILE))"; \
		tmp="$(LANGFUSE_ENV_FILE).tmp"; \
		trap 'rm -f "$$tmp"' 0; \
		rm -f "$$tmp"; \
		umask 077; \
		bash scripts/create-langfuse-env.sh > "$$tmp"; \
		chmod 600 "$$tmp"; \
		mv "$$tmp" "$(LANGFUSE_ENV_FILE)"

## Start the opt-in Langfuse observability stack
observability-langfuse: $(LANGFUSE_ENV_FILE)
	docker compose --env-file $(LANGFUSE_ENV_FILE) -f docker-compose.langfuse.yml up -d

## Stop the opt-in Langfuse observability stack
observability-langfuse-down: $(LANGFUSE_ENV_FILE)
	docker compose --env-file $(LANGFUSE_ENV_FILE) -f docker-compose.langfuse.yml down

## Run all tests (SQLite in-memory by default)
test:
	uv run pytest tests/ -v

## Run all tests against the local docker PostgreSQL (test_db -> postgresql)
test-pg:
	docker compose -f docker-compose.local.yml up -d postgres && \
	sleep 2 && \
	set -a && . _env/local.env && set +a && \
	TEST_DB_ENGINE=postgresql \
	TEST_DB_USER=postgres \
	TEST_DB_PASSWORD=postgres \
	TEST_DB_HOST=localhost \
	TEST_DB_PORT=5432 \
	TEST_DB_NAME=postgres \
	uv run pytest tests/ -v

## Run DynamoDB integration tests against local dynamodb-local
test-dynamo:
	docker compose -f docker-compose.local.yml up -d dynamodb-local && \
	sleep 2 && \
	set -a && . _env/local.env && set +a && \
	uv run pytest tests/integration/_core/infrastructure/persistence/nosql/dynamodb/ -v

## Run tests with coverage
test-cov:
	uv run pytest tests/ -v --cov=src --cov-report=term-missing

# Locust performance-test defaults — override per run, e.g.
#   make perf-test PERF_USERS=50 PERF_RUN_TIME=2m
PERF_HOST ?= http://127.0.0.1:8001
PERF_USERS ?= 10
PERF_SPAWN_RATE ?= 2
PERF_RUN_TIME ?= 30s

# Admin CRUD scenario needs LOCUST_ADMIN_USERNAME / LOCUST_ADMIN_PASSWORD —
# see docs/operations/performance-locust.md. Numbers are illustrative only.
## Headless Locust perf run against a local server (start `make quickstart` first)
perf-test:
	uv run locust \
		-f tests/perf/locustfile.py \
		--headless \
		--host $(PERF_HOST) \
		--users $(PERF_USERS) \
		--spawn-rate $(PERF_SPAWN_RATE) \
		--run-time $(PERF_RUN_TIME) \
		--stop-timeout 10

## Run linter
lint:
	uv run ruff check src/

## Run formatter
format:
	uv run ruff format src/

## Run fast local checks (lint + format check + core tests)
check-core:
	uv run ruff check src/ && \
	uv run ruff format --check src/ && \
	uv run pytest tests/ -v \
		--ignore=tests/unit/_core/infrastructure/persistence/nosql \
		--ignore=tests/integration

## Alias for fast local checks
check: check-core

## Run CI-parity checks (requires admin + aws extras and dynamodb-local)
check-full:
	uv sync --group dev --extra admin --extra aws && \
	docker compose -f docker-compose.local.yml up -d dynamodb-local && \
	sleep 2 && \
	uv run ruff check src/ && \
	uv run ruff format --check src/ && \
	uv run pytest tests/ -v

## Run no-extra minimal-install regression in an isolated uv environment
check-minimal:
	UV_PROJECT_ENVIRONMENT=$(MINIMAL_UV_ENV) uv sync --group dev && \
	UV_PROJECT_ENVIRONMENT=$(MINIMAL_UV_ENV) uv run pytest tests/integration/_core/test_minimal_install.py -v

## Copy-flow smoke: every example must boot after cp-to-src (#260).
## CI's test job already runs this via `pytest tests/`; this target is the
## fast local loop (make check-core excludes tests/integration).
smoke-examples:
	uv run pytest tests/integration/examples -v

## Run pre-commit on all files
pre-commit:
	uv run pre-commit run --all-files

## Run database migrations
migrate:
	uv run alembic upgrade head

## Generate new migration
migration:
	@read -p "Migration message: " msg; \
	uv run alembic revision --autogenerate -m "$$msg"

## Clean up
clean:
	find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null; \
	find . -type d -name .pytest_cache -exec rm -rf {} + 2>/dev/null; \
	find . -type d -name .ruff_cache -exec rm -rf {} + 2>/dev/null; \
	rm -rf .coverage htmlcov/
