# Django-Ray Makefile
# Core development commands for django-ray
#
# For Kubernetes deployment: see mk/k8s.mk or use `make -f mk/k8s.mk <target>`
# For load testing: see mk/loadtest.mk
# For Docker: see mk/docker.mk

.PHONY: all install configure-git commit-check commit-title-check commit-policy-test format fix lint typecheck audit-dependencies test test-xdist test-unit test-integration test-postgres test-testproject test-cov test-suite-inventory coverage-debt check ci build clean help
.PHONY: migrate runserver shell makemigrations createsuperuser
.PHONY: worker worker-sync worker-local worker-all
.PHONY: docs-build docs-build-strict docs-serve
.PHONY: linux-test-catalogue linux-test-stage linux-test-aggregate

# Include optional modules (comment out if not needed)
-include mk/docker.mk
-include mk/k8s.mk
-include mk/tls.mk
-include mk/loadtest.mk

COVERAGE_GLOBAL_MIN ?= 95
COVERAGE_WORKER_MIN ?= 90
COVERAGE_RAY_JOB_MIN ?= 90
COVERAGE_TESTPROJECT_MIN ?= 80
COVERAGE_DEBT_OUTPUT_DIR ?= artifacts/coverage-debt
COVERAGE_DEBT_SOURCE_COMMIT ?= $(shell git rev-parse HEAD)
COVERAGE_DEBT_DEFAULT_TIMEOUT_SECONDS ?= 1200
COVERAGE_DEBT_LOCAL_RAY_TIMEOUT_SECONDS ?= 900
TEST_SUITE_INVENTORY_OUTPUT_DIR ?= artifacts/test-suite-inventory
TEST_XDIST_WORKERS ?= 4
LINUX_TEST_STAGE ?=
LINUX_TEST_OUTPUT ?=
LINUX_TEST_EVIDENCE ?=
LINUX_TEST_SOURCE_MANIFEST ?=

# =============================================================================
# Development
# =============================================================================

# Default target - run non-mutating checks and tests
all: check test

# Source-owned assertions only; the caller admits resources and runs each stage.
linux-test-catalogue:
	python scripts/linux_test_plan.py catalogue \
		$(if $(LINUX_TEST_SOURCE_MANIFEST),--source-manifest "$(LINUX_TEST_SOURCE_MANIFEST)")

linux-test-stage:
	python scripts/linux_test_plan.py run --stage "$(LINUX_TEST_STAGE)" \
		--output-dir "$(LINUX_TEST_OUTPUT)" \
		$(if $(LINUX_TEST_SOURCE_MANIFEST),--source-manifest "$(LINUX_TEST_SOURCE_MANIFEST)")

linux-test-aggregate:
	python scripts/linux_test_plan.py aggregate --evidence-dir "$(LINUX_TEST_EVIDENCE)" \
		--output-dir "$(LINUX_TEST_OUTPUT)" \
		$(if $(LINUX_TEST_SOURCE_MANIFEST),--source-manifest "$(LINUX_TEST_SOURCE_MANIFEST)")

# Install dependencies
install:
	uv sync
	npm ci --ignore-scripts
	$(MAKE) configure-git

# Configure this worktree to use the tracked commit template and hook.
configure-git:
	git config extensions.worktreeConfig true
	git config --worktree commit.template "$(CURDIR)/.gitmessage"
	git config --worktree core.commentChar ";"
	git config --worktree core.hooksPath "$(CURDIR)/.githooks"

# Validate every retained commit after the base and through the head.
COMMIT_BASE ?= origin/main
COMMIT_HEAD ?= HEAD
commit-check:
	npm run --silent commitlint -- \
		--from "$(COMMIT_BASE)" \
		--to "$(COMMIT_HEAD)" \
		--git-log-args="--no-merges"

# Validate a PR title from the PR_TITLE environment variable.
commit-title-check:
	@node -e "const title = process.env.PR_TITLE; if (!title) { console.error('PR_TITLE is required.'); process.exit(2); } process.stdout.write(title + '\n');" | npm run --silent commitlint:title

# Exercise the repository-owned commit policy fixtures.
commit-policy-test:
	npm test --silent

# Format code with Ruff
format:
	ruff format .

# Apply formatting and safe lint fixes
fix:
	ruff format .
	ruff check . --fix

# Lint code with Ruff without modifying files
lint:
	ruff check .

# Type check with ty
typecheck:
	ty check

# Audit only the exact locked runtime graph against current PyPI advisories
audit-dependencies:
	python scripts/audit_runtime_dependencies.py

# Run all tests
test:
	python scripts/require_linux.py
	pytest

# Run the default-resource local subset with ordinary pytest-xdist.
test-xdist:
	python scripts/require_linux.py
	pytest -n $(TEST_XDIST_WORKERS) --max-worker-restart=0 \
		-m "not real_ray and not live_cluster and not postgresql"

# Run unit tests only
test-unit:
	python scripts/require_linux.py
	pytest tests/unit/ -v

# Run integration tests only
test-integration:
	python scripts/require_linux.py
	pytest tests/integration/ -v

# Exercise database coordination against a real PostgreSQL server.
test-postgres:
	python scripts/require_linux.py
	python -m pytest \
		tests/integration/test_postgresql_coordination.py \
		tests/integration/test_sample_admission.py \
		tests/integration/test_transactional_enqueue.py \
		tests/integration/test_postgresql_workflow_progress_storage.py \
		tests/integration/test_postgresql_workflow_progress_reads.py \
		tests/integration/test_postgresql_polling.py \
		tests/integration/test_postgresql_metrics.py \
		tests/integration/test_protocol_coordination.py \
		tests/integration/test_ray_target_coordination.py \
		tests/integration/test_ray_target_routing_coordination.py \
		tests/integration/test_ray_worker_target_capabilities.py \
		tests/integration/test_protocol_status.py \
		tests/integration/test_execution_protocol_schema_migration.py \
		tests/integration/test_ray_target_persistence_migration.py \
		tests/integration/test_ray_task_target_binding_migration.py \
		tests/integration/test_ray_target_route_migration.py \
		tests/integration/test_ray_worker_target_capability_migration.py \
		tests/integration/test_ray_task_target_execution_evidence_migration.py \
		tests/integration/test_priority_migration.py \
		tests/integration/test_queue_expiration_migration.py \
		tests/integration/test_request_reference_schema_migration.py \
		tests/integration/test_task_id_uniqueness_migration.py \
		tests/integration/test_workflow_run_allocation_migration.py \
		tests/unit/test_application_offline_settings.py \
		-m postgresql -vv --durations=20

# Validate the bundled sample project's user-facing boundary
test-testproject:
	python scripts/require_linux.py
	python testproject/manage.py check
	pytest tests/integration/test_api.py \
		tests/integration/test_sample_admission.py \
		tests/unit/test_sample_workload_limits.py \
		tests/integration/test_testproject_admin_theme.py \
		tests/integration/test_workflow_progress_api.py \
		tests/unit/test_sample_security.py \
		tests/unit/test_ray_data_golden_path.py \
		tests/unit/test_testproject_workflows.py \
		tests/unit/test_workflow_reporting_benchmark_command.py \
		-m "not postgresql" \
		--cov=testproject.api \
		--cov=testproject.views \
		--cov=testproject.urls \
		--cov-report=term \
		--cov-fail-under=$(COVERAGE_TESTPROJECT_MIN)

# Run tests with coverage
test-cov:
	python scripts/require_linux.py
	pytest -m "not live_cluster" --cov=src --cov-report=html --cov-report=term --cov-fail-under=$(COVERAGE_GLOBAL_MIN)
	coverage report --include="src/django_ray/management/commands/django_ray_worker.py" --fail-under=$(COVERAGE_WORKER_MIN)
	coverage report --include="src/django_ray/runner/ray_job.py" --fail-under=$(COVERAGE_RAY_JOB_MIN)

# Collect exact execution-contract and CI-lane counts without running tests.
test-suite-inventory:
	python scripts/test_suite_inventory.py collect \
		--json-output "$(TEST_SUITE_INVENTORY_OUTPUT_DIR)/test-suite-inventory.json" \
		--markdown-output "$(TEST_SUITE_INVENTORY_OUTPUT_DIR)/test-suite-inventory.md"

# Produce deterministic line-coverage debt evidence from isolated resource phases.
coverage-debt:
	python scripts/require_linux.py
	python scripts/coverage_debt.py prepare-output --output-dir "$(COVERAGE_DEBT_OUTPUT_DIR)"
	python scripts/coverage_debt.py run-phases \
		--output-dir "$(COVERAGE_DEBT_OUTPUT_DIR)" \
		--default-timeout-seconds $(COVERAGE_DEBT_DEFAULT_TIMEOUT_SECONDS) \
		--local-ray-timeout-seconds $(COVERAGE_DEBT_LOCAL_RAY_TIMEOUT_SECONDS)
	coverage report --include="src/django_ray/management/commands/django_ray_worker.py" --fail-under=$(COVERAGE_WORKER_MIN)
	coverage report --include="src/django_ray/runner/ray_job.py" --fail-under=$(COVERAGE_RAY_JOB_MIN)
	coverage report --fail-under=$(COVERAGE_GLOBAL_MIN)
	coverage json --rcfile=pyproject.toml --pretty-print -o "$(COVERAGE_DEBT_OUTPUT_DIR)/coverage.py.json"
	python scripts/coverage_debt.py render \
		--coverage-json "$(COVERAGE_DEBT_OUTPUT_DIR)/coverage.py.json" \
		--classifications .github/coverage-debt-classifications.json \
		--pyproject pyproject.toml \
		--source-commit "$(COVERAGE_DEBT_SOURCE_COMMIT)" \
		--json-output "$(COVERAGE_DEBT_OUTPUT_DIR)/coverage-debt.json" \
		--markdown-output "$(COVERAGE_DEBT_OUTPUT_DIR)/coverage-debt.md"

# Run formatting, lint, and type checks without modifying files
check:
	$(MAKE) commit-policy-test
	ruff format --check .
	ruff check .
	ty check

# CI check - current-interpreter equivalents of required CI jobs, without modifications.
# Invoke as `uv run make ci` so Ray inherits one uv-managed environment.
ci:
	python scripts/require_linux.py
	$(MAKE) commit-policy-test
	ruff format --check .
	ruff check .
	ty check
	python scripts/audit_runtime_dependencies.py
	pytest -m "not live_cluster" --cov=src --cov-report=xml --cov-report=term --cov-fail-under=$(COVERAGE_GLOBAL_MIN)
	coverage report --include="src/django_ray/management/commands/django_ray_worker.py" --fail-under=$(COVERAGE_WORKER_MIN)
	coverage report --include="src/django_ray/runner/ray_job.py" --fail-under=$(COVERAGE_RAY_JOB_MIN)
	$(MAKE) test-testproject
	zensical build --strict --clean
	uv build
	@echo "All CI checks passed!"

# Build the package
build:
	uv build

# Build docs
docs-build:
	uv run zensical build

# Build docs in strict mode (CI)
docs-build-strict:
	uv run python scripts/validate_release.py --development --allow-release-candidate
	uv run zensical build --strict --clean

# Serve docs locally at http://127.0.0.1:8000
docs-serve:
	uv run zensical serve --dev-addr 127.0.0.1:8000

# =============================================================================
# Django (testproject)
# =============================================================================

migrate:
	cd testproject && python manage.py migrate

runserver:
	cd testproject && python manage.py runserver

shell:
	cd testproject && python manage.py shell

makemigrations:
	cd testproject && python manage.py makemigrations

createsuperuser:
	cd testproject && python manage.py createsuperuser

# =============================================================================
# Worker
# =============================================================================

# Start worker (default: Ray Job API mode)
worker:
	cd testproject && python manage.py django_ray_worker --queue=default

# Start worker in sync mode (no Ray, for testing)
worker-sync:
	cd testproject && python manage.py django_ray_worker --queue=default --sync

# Start worker with local Ray (recommended for development)
worker-local:
	cd testproject && python manage.py django_ray_worker --queue=default --local

# Start worker processing all queues (development)
worker-all:
	cd testproject && python manage.py django_ray_worker --all-queues --local

# Connect to Ray cluster
worker-cluster:
	cd testproject && python manage.py django_ray_worker --queue=default --cluster=ray://localhost:10001

# =============================================================================
# Utilities
# =============================================================================

# Clean up cache and build files
clean:
	rm -rf .pytest_cache .ruff_cache htmlcov .coverage dist build *.egg-info src/*.egg-info db.sqlite3
	find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true

# Show help
help:
	@echo "Django-Ray Development Commands"
	@echo ""
	@echo "Setup:"
	@echo "  install        - Install Python and commit-policy dependencies"
	@echo "  configure-git  - Install the tracked commit template and hook"
	@echo "  migrate        - Run Django migrations"
	@echo ""
	@echo "Development:"
	@echo "  format         - Format code with Ruff"
	@echo "  fix            - Format code and apply safe Ruff fixes"
	@echo "  lint           - Lint code with Ruff (no modifications)"
	@echo "  typecheck      - Type check with ty"
	@echo "  audit-dependencies - Audit the exact locked runtime dependency graph"
	@echo "  check          - Test policy; check formatting, lint, and types"
	@echo "  commit-policy-test - Exercise commit message policy fixtures"
	@echo ""
	@echo "Testing:"
	@echo "  test           - Run all tests"
	@echo "  test-xdist     - Run the default-resource subset with configurable xdist workers"
	@echo "  test-unit      - Run unit tests only"
	@echo "  test-integration - Run integration tests only"
	@echo "  test-postgres  - Run PostgreSQL coordination tests"
	@echo "  test-testproject - Validate the bundled sample project"
	@echo "  test-cov       - Run tests with coverage"
	@echo "  test-suite-inventory - Classify collected tests by execution contract"
	@echo "  coverage-debt  - Build exact JSON and Markdown line-coverage debt reports"
	@echo "  k8s-final-gate-preflight - Validate a guarded local KubeRay gate without mutations"
	@echo "  k8s-final-gate - Run the guarded local KubeRay final integration gate"
	@echo "  docs-build     - Build Zensical site"
	@echo "  docs-build-strict - Build Zensical site (strict mode)"
	@echo "  docs-serve     - Serve docs locally at http://127.0.0.1:8000"
	@echo ""
	@echo "Django:"
	@echo "  runserver      - Start Django dev server"
	@echo "  shell          - Open Django shell"
	@echo "  makemigrations - Create migrations"
	@echo "  createsuperuser - Create admin user"
	@echo ""
	@echo "Worker:"
	@echo "  worker         - Start worker (Ray Job API)"
	@echo "  worker-local   - Start worker (local Ray) [recommended]"
	@echo "  worker-sync    - Start worker (no Ray, for testing)"
	@echo "  worker-all     - Process all queues (local Ray)"
	@echo "  worker-cluster - Connect to ray://localhost:10001"
	@echo ""
	@echo "CI/CD:"
	@echo "  commit-check   - Validate commits from COMMIT_BASE through COMMIT_HEAD"
	@echo "  commit-title-check - Validate the PR_TITLE environment variable"
	@echo "  all            - Run non-mutating checks and tests"
	@echo "  ci             - Run Linux current-interpreter CI, coverage, docs, and build"
	@echo "  build          - Build the package"
	@echo "  clean          - Clean cache and build files"
	@echo ""
	@echo "Additional modules (if included):"
	@echo "  Docker:     make docker-up, docker-smoke, docker-down"
	@echo "  Kubernetes (local evaluation): make k8s-deploy, k8s-urls, k8s-status, k8s-delete"
	@echo "  Load test:  make loadtest-demo, loadtest, loadtest-headless"
	@echo ""
	@echo "For full k8s commands: make -f mk/k8s.mk help"

