.PHONY: build build-release build-all-features test test-rust check check-full check-crate fmt fmt-check clippy audit outdated deny \
	lint-python \
	clean clean-generated \
	build-python test-python test-python-upstream test-python-upstream-full \
	test-parity-phase-a test-parity-phase-b test-parity-phase-c test-parity-phase-d \
	test-parity-phase-e test-parity-phase-f test-parity-phase-g test-parity-phases \
	sparkless-parity all bench-window

# Use stable toolchain when no default is configured (override with RUSTUP_TOOLCHAIN=nightly etc.)
export RUSTUP_TOOLCHAIN ?= stable
# Use real Cargo home to avoid sandbox cache corruption (e.g. Cursor IDE).
# Force override (:=) so Cursor/sandbox CARGO_HOME does not take precedence.
export CARGO_HOME := $(HOME)/.cargo

# Build
build:
	cargo build

build-release:
	cargo build --release

# Build with all optional features (sql, delta) so feature-gated code is compiled and checked
build-all-features:
	cargo build --workspace --all-features

# Run Rust tests only (default features)
test-rust:
	cargo test --workspace

# Run Rust tests with all features (sql, delta) - used by check
test-rust-all-features:
	cargo test --workspace --all-features

# Run all tests (Rust only)
test: test-rust

# Run all Rust checks. Clean first so old binaries don't accumulate (each run is a full rebuild).
# Then run fmt-check, audit, deny, and clippy in parallel (they are independent); then tests
# (tests reuse clippy's build). Use -j4 to overlap the four jobs.
check: clean
	@$(MAKE) -j4 fmt-check audit deny clippy
	$(MAKE) test-rust-all-features
	@echo "All checks passed"

# Remove all build artifacts (target/). Use when target/ grows large from repeated
# check-full runs (incremental + debug + all-features + all-targets). Next build will be a full rebuild.
clean:
	cargo clean

# Remove local/generated non-Rust outputs (best-effort).
clean-generated:
	rm -rf tmp spark-warehouse site docs/_build _build

# Run checks for a single crate only (faster when editing one crate).
# Usage: make check-crate CRATE=spark-sql-parser
# Runs: fmt-check, audit, deny, then clippy and test for that package only.
check-crate:
	@if [ -z "$(CRATE)" ]; then echo "Usage: make check-crate CRATE=<package-name>"; exit 1; fi
	$(MAKE) fmt-check
	$(MAKE) audit
	$(MAKE) deny
	cargo clippy -p $(CRATE) --all-features --all-targets -- -D warnings
	cargo test -p $(CRATE) --all-features
	@echo "check-crate ($(CRATE)): format, clippy, audit, deny, tests passed"

# Python lint and type-check: ruff format (check), ruff check, mypy. No Java/PySpark required.
# Use .venv/bin/ruff and .venv/bin/mypy when present so check-full works without ruff/mypy on PATH.
# Mypy must match release workflow: same config (python/pyproject.toml) and target (sparkless package).
lint-python:
	@RUFF=$$(test -f .venv/bin/ruff && echo .venv/bin/ruff || echo ruff); \
	MYPY=$$(test -f .venv/bin/mypy && echo .venv/bin/mypy || echo mypy); \
	$$RUFF format --check . && $$RUFF check . && \
	$$MYPY --config-file python/pyproject.toml python/sparkless

# Backwards-compatible alias for full check suite (Rust + Python lint).
# With CRATE set, runs checks for that package only (faster when editing one crate).
# Usage: make check-full  OR  make check-full CRATE=spark-sql-parser
check-full:
	@if [ -n "$(CRATE)" ]; then \
	  $(MAKE) check-crate CRATE=$(CRATE); \
	  echo "check-full (crate $(CRATE)): format, clippy, audit, deny, tests"; \
	else \
	  $(MAKE) check; \
	  $(MAKE) lint-python; \
	  echo "check-full: format, clippy, audit, deny, tests, ruff, mypy"; \
	fi

# Format code
fmt:
	cargo fmt
	@echo "Formatted"

# Check format without modifying (workspace is formatted as a whole from root; includes python crate).
fmt-check:
	cargo fmt --check

# Lint with Clippy. Use --workspace to match CI and include Python Rust code (sparkless-native).
clippy:
	cargo clippy --workspace --all-features --all-targets -- -D warnings

# Security: scan for known vulnerabilities
audit:
	cargo audit

# List outdated dependencies
outdated:
	cargo outdated

# Advisory, bans, sources checks (skip licenses - needs per-crate clarifications). Workspace-aware.
deny:
	cargo deny check advisories bans sources

# Run parity fixtures for a specific phase (A–G). See docs/PARITY_STATUS.md.
test-parity-phase-a:
	PARITY_PHASE=a cargo test pyspark_parity_fixtures

test-parity-phase-b:
	PARITY_PHASE=b cargo test pyspark_parity_fixtures

test-parity-phase-c:
	PARITY_PHASE=c cargo test pyspark_parity_fixtures

test-parity-phase-d:
	PARITY_PHASE=d cargo test pyspark_parity_fixtures

test-parity-phase-e:
	PARITY_PHASE=e cargo test pyspark_parity_fixtures

test-parity-phase-f:
	PARITY_PHASE=f cargo test pyspark_parity_fixtures

test-parity-phase-g:
	PARITY_PHASE=g cargo test pyspark_parity_fixtures

test-parity-phases:
	cargo test --workspace --all-features pyspark_parity_fixtures

# Convert Sparkless fixtures (when SPARKLESS_EXPECTED_OUTPUTS is set), regenerate
# expected results from PySpark, then run Rust parity tests.
sparkless-parity:
	@if [ -z "$$SPARKLESS_EXPECTED_OUTPUTS" ]; then \
	  echo "SPARKLESS_EXPECTED_OUTPUTS is not set; see docs/CONVERTER_STATUS.md"; \
	  exit 1; \
	fi
	python tests/convert_sparkless_fixtures.py --batch "$$SPARKLESS_EXPECTED_OUTPUTS" tests/fixtures --output-subdir converted --dedupe
	python tests/regenerate_expected_from_pyspark.py tests/fixtures/converted
	cargo test pyspark_parity_fixtures

# Python package (sparkless 4.0.0)
build-python:
	cd python && maturin build

# Run Python smoke test (requires maturin develop or pip install ./python; uses .venv if present)
test-python:
	@PYTHON=$$(test -f .venv/bin/python && echo .venv/bin/python || echo python); \
	$$PYTHON -c "from sparkless.sql import SparkSession; from sparkless.sql.functions import col, lit_i64; s = SparkSession.builder.app_name('test').get_or_create(); df = s.create_dataframe([(1, 2, 'a')], ['x', 'y', 'z']); assert df.count() == 1; print('sparkless OK')"

# Unified Python tests: fast subset (excludes delta and integration by default)
test-python-upstream:
	@PYTHON=$$(test -f .venv/bin/python && echo .venv/bin/python || echo python); \
	$$PYTHON -m pytest tests -m "not delta and not integration" -n 10 -v --tb=short

# Unified Python tests: full suite (includes delta, integration; best-effort)
test-python-upstream-full:
	@PYTHON=$$(test -f .venv/bin/python && echo .venv/bin/python || echo python); \
	$$PYTHON -m pytest tests -n 10 -v --tb=short

# Window-like groupBy+join benchmark (issue #1430). Requires sparkless in .venv or PATH.
bench-window:
	@PYTHON=$$(test -f .venv/bin/python && echo .venv/bin/python || echo python); \
	$$PYTHON scripts/run_window_benchmark.py --rows 100000 --groups 1000 --repetitions 5

# Run everything: format, lint, security, deny, tests
all: check
	@echo "All updates and checks complete"

