.PHONY: help lint format type-check security test pre-push pre-push-fast clean test-tck docstring-coverage test-network test-perf test-perf-xs test-perf-slow test-perf-large coverage coverage-quick coverage-report coverage-diff coverage-strict check-coverage check-patch-coverage test-durations test-analytics docs-serve docs-build docs-clean

help:  ## Show this help message
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

lint:  ## Run ruff linter
	uv run ruff check .

format:  ## Format code with ruff
	uv run ruff format .

format-check:  ## Check code formatting
	uv run ruff format --check .

type-check:  ## Run mypy type checker
	uv run mypy src/graphforge --strict-optional --show-error-codes

security:  ## Run Bandit security scanner
	uv run bandit -c pyproject.toml -r src/

docstring-coverage:  ## Check docstring coverage (90% minimum)
	uv run interrogate src/graphforge --fail-under 90 --quiet

test:  ## Run all tests in parallel (excludes snap/network downloads)
	uv run pytest tests/ -n $${PYTEST_WORKERS:-4} -m "not snap"

test-unit:  ## Run unit tests in parallel
	uv run pytest tests/unit -n $${PYTEST_WORKERS:-4}

test-integration:  ## Run integration tests in parallel (excludes snap/network downloads)
	uv run pytest tests/integration -n $${PYTEST_WORKERS:-4} -m "not snap"

test-network:  ## Run external network/download tests (snap datasets; requires internet)
	uv run pytest tests/ -m "snap" -v

test-perf-xs:  ## Run perf QA for XS/S datasets only (karate + ego-facebook, ~3 MB total)
	uv run pytest tests/perf/ -m "perf and not perf_large and not perf_slow" -k "xs or s_face" -v -s

test-perf:  ## Run perf QA through M-tier (Amazon 334k nodes, ~15 MB); writes baseline JSON
	uv run pytest tests/perf/ -m "perf and not perf_large and not perf_slow" -v -s
	@echo ""
	@echo "Baseline written to benchmarks/real_dataset_baseline_v0.3.9.json"

test-perf-slow:  ## Run slow two-hop full-expansion benchmarks (opt-in; minutes on S/M, hours on L)
	uv run pytest tests/perf/ -m "perf_slow" -v -s

test-perf-large:  ## Run L/XL tiers (livejournal 500 MB, cit-patents 270 MB, orkut 1.7 GB); needs 16 GB RAM
	uv run pytest tests/perf/ -m "perf and perf_large" -v -s --timeout=3600

test-tck:  ## Run TCK compliance tests in parallel
	uv run pytest tests/tck/ -n $${PYTEST_WORKERS:-4} -v

coverage:  ## Run tests with coverage and validate thresholds (sharded to avoid OOM)
	@echo "━━━ Coverage shard 1/2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	uv run coverage erase
	uv run pytest tests/unit tests/integration -m "not snap" \
		--splits 2 --group 1 \
		-n $${PYTEST_WORKERS:-4} \
		--cov=src --cov-branch \
		--cov-report= \
		--cov-append
	@echo "━━━ Coverage shard 2/2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	uv run pytest tests/unit tests/integration -m "not snap" \
		--splits 2 --group 2 \
		-n $${PYTEST_WORKERS:-4} \
		--cov=src --cov-branch \
		--cov-report=term-missing \
		--cov-report=xml \
		--cov-append
	@echo "━━━ Threshold checks ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@uv run coverage report --fail-under=85 || \
		(echo "❌ Total coverage below 85% threshold" && exit 1)
	@echo "✅ Total coverage ≥ 85%"
	@DIFF_OUT=$$(git diff --name-only origin/main... 2>&1) || \
		{ echo "❌ git diff failed — check that origin/main is accessible"; exit 1; }; \
	CHANGED_FILES=$$(printf '%s\n' "$$DIFF_OUT" | grep '^src/.*\.py$$' || true); \
	if [ -z "$$CHANGED_FILES" ]; then \
		echo "ℹ️  No source files changed — skipping patch coverage check"; \
	else \
		echo "Changed files:"; \
		echo "$$CHANGED_FILES" | sed 's/^/  - /'; \
		INCLUDE_PATTERN=$$(echo "$$CHANGED_FILES" | tr '\n' ',' | sed 's/,$$//'); \
		uv run coverage report --include="$$INCLUDE_PATTERN" --fail-under=90 || \
			(echo "❌ Patch coverage below 90% for changed files" && \
			 echo "   Run 'make coverage-report' to see detailed coverage" && \
			 exit 1); \
		echo "✅ Patch coverage ≥ 90%"; \
	fi

coverage-quick:  ## Quick coverage on unit tests only (for iterative development)
	@echo "Running unit tests with coverage..."
	uv run pytest tests/unit \
		-n $${PYTEST_WORKERS:-4} \
		--cov=src --cov-branch \
		--cov-report=term-missing \
		--cov-report=xml

test-durations:  ## Generate .test_durations for pytest-split shard balancing
	uv run pytest tests/unit tests/integration -m "not snap" \
		--store-durations --durations-path=.test_durations -q

test-analytics:  ## Run tests with analytics output (JUnit XML)
	@echo "Running tests with analytics output..."
	uv run pytest tests/unit tests/integration \
		--junitxml=test-results-local.xml \
		-v

check-coverage:  ## Validate total coverage meets 85% threshold (uses existing .coverage data)
	@echo "Checking coverage thresholds..."
	@uv run coverage report --fail-under=85 || \
		(echo "❌ Coverage below 85% threshold" && exit 1)
	@echo "✅ Coverage meets threshold"

coverage-strict:  ## Strict 90% coverage check for new features
	@echo "Checking strict coverage (90%)..."
	@uv run coverage report --fail-under=90 || \
		(echo "❌ Coverage below 90% - consider adding more tests" && exit 1)
	@echo "✅ Coverage meets strict threshold"

coverage-report:  ## Generate HTML coverage report and open in browser
	@echo "Generating HTML coverage report..."
	uv run coverage html
	@echo "Opening coverage report in browser..."
	@open htmlcov/index.html || xdg-open htmlcov/index.html || \
		echo "Coverage report generated at htmlcov/index.html"

coverage-diff:  ## Show coverage for changed files only
	@echo "Showing coverage for changed files..."
	@CHANGED_FILES=$$(git diff --name-only origin/main... | grep '\.py$$' || true); \
	if [ -z "$$CHANGED_FILES" ]; then \
		echo "ℹ️  No Python files changed"; \
	else \
		INCLUDE_PATTERN=$$(echo "$$CHANGED_FILES" | tr '\n' ',' | sed 's/,$$//'); \
		uv run coverage report --include="$$INCLUDE_PATTERN"; \
	fi

check-patch-coverage:  ## Validate patch coverage for changed files (90% threshold, uses existing .coverage data)
	@echo "Checking patch coverage for changed files..."
	@DIFF_OUT=$$(git diff --name-only origin/main... 2>&1) || \
		{ echo "❌ git diff failed — check that origin/main is accessible"; exit 1; }; \
	CHANGED_FILES=$$(printf '%s\n' "$$DIFF_OUT" | grep '^src/.*\.py$$' || true); \
	if [ -z "$$CHANGED_FILES" ]; then \
		echo "ℹ️  No source files changed - skipping patch coverage check"; \
	else \
		echo "Changed files:"; \
		echo "$$CHANGED_FILES" | sed 's/^/  - /'; \
		INCLUDE_PATTERN=$$(echo "$$CHANGED_FILES" | tr '\n' ',' | sed 's/,$$//'); \
		uv run coverage report --include="$$INCLUDE_PATTERN" --fail-under=90 || \
			(echo "❌ Patch coverage below 90% for changed files" && \
			 echo "   Run 'make coverage-report' to see detailed coverage" && \
			 exit 1); \
		echo "✅ Patch coverage meets 90% threshold"; \
	fi

pre-push-fast:  ## Run fast checks only — format, lint, type, security, docstrings (no coverage, ~30s)
	@echo "━━━ Format check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@$(MAKE) format-check
	@echo "━━━ Lint ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@$(MAKE) lint
	@echo "━━━ Type check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@$(MAKE) type-check
	@echo "━━━ Security ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@$(MAKE) security
	@echo "━━━ Docstring coverage ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@$(MAKE) docstring-coverage
	@echo "✅ Fast checks passed! Run 'make pre-push' to include coverage."

pre-push:  ## Run all pre-push checks — fast checks + coverage with thresholds (mirrors CI)
	@$(MAKE) pre-push-fast
	@echo "━━━ Coverage + thresholds ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@$(MAKE) coverage
	@echo "✅ All pre-push checks passed!"

clean:  ## Clean up cache files
	find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
	find . -type d -name .pytest_cache -exec rm -rf {} + 2>/dev/null || true
	find . -type d -name .mypy_cache -exec rm -rf {} + 2>/dev/null || true
	find . -type f -name "*.pyc" -delete 2>/dev/null || true
	rm -f test-results*.xml coverage.xml 2>/dev/null || true
	rm -rf htmlcov/ 2>/dev/null || true

docs-serve:  ## Serve docs locally with live reload (http://127.0.0.1:8000)
	uv run --group docs mkdocs serve

docs-build:  ## Build docs site to site/ (strict mode)
	uv run --group docs mkdocs build --strict

docs-clean:  ## Remove built docs
	rm -rf site/
