# Copyright (c) Microsoft Corporation.
# Licensed under the PostgreSQL License.

# pg_durable Makefile

# PostgreSQL major version for pgrx (override with: make ... PG_VERSION=pg18)
PG_VERSION ?= pg17
CARGO ?= cargo
EXTRA_FEATURES ?=
ACR_REGISTRY ?= myregistry.azurecr.io
ACR_IMAGE ?= pg_durable

.PHONY: all build package install uninstall test test-unit test-e2e test-regress pg-clean docker-build docker-push pg-install pgxn-zip

# Default target
all: package

# Build the extension
build:
	cargo build

# Build release artifacts for source installation. This runs without elevated
# privileges; `install` only copies the resulting package tree.
package:
	@if test -e "$(PGRX_PACKAGE_DIR)" \
	    && test "$(PGRX_PACKAGE_DIR)" != "$(DEFAULT_PGRX_PACKAGE_DIR)" \
	    && test ! -f "$(PGRX_PACKAGE_MARKER)" \
	    && { test ! -d "$(PGRX_PACKAGE_DIR)" \
	        || test -n "$$(ls -A "$(PGRX_PACKAGE_DIR)" 2>/dev/null)"; }; then \
	    echo "refusing to replace unowned package directory: $(PGRX_PACKAGE_DIR)" >&2; \
	    exit 1; \
	fi
	rm -rf "$(PGRX_PACKAGE_DIR)"
	$(CARGO) pgrx package --pg-config "$(PG_CONFIG)" \
	    --out-dir "$(PGRX_PACKAGE_DIR)" \
	    --no-default-features --features "$(strip pg$(PG_MAJOR) $(EXTRA_FEATURES))"
	@touch "$(PGRX_PACKAGE_MARKER)"

# Run all tests (unit + E2E)
test:
	./scripts/test.sh --all

# Run only pgrx unit tests
test-unit:
	./scripts/test.sh --unit

# Run only E2E tests (Docker-based)
test-e2e:
	./scripts/test.sh --e2e

# Build Docker image
docker-build:
	docker build --platform linux/amd64 -t pg_durable:latest .

# Build and push to ACR
docker-push: docker-build
	docker tag pg_durable:latest $(ACR_REGISTRY)/$(ACR_IMAGE):latest
	REGISTRY_NAME="$(ACR_REGISTRY)"; az acr login --name "$${REGISTRY_NAME%%.*}"
	docker push $(ACR_REGISTRY)/$(ACR_IMAGE):latest

# Run local development server
run:
	cargo pgrx run pg17

# Clean build artifacts (renamed to avoid PGXS conflict)
pg-clean:
	cargo clean
	rm -rf target/
	rm -f META.json $(DISTNAME)-$(DISTVERSION).zip

# Install extension locally (renamed to avoid PGXS conflict)
pg-install:
	cargo pgrx install --features http-allow-test-domains

# Run pg_regress tests (convenience target)
# Override version: make test-regress PG_VERSION=pg18
test-regress:
	@echo "Resetting PostgreSQL..."
	./scripts/pg-reset.sh $(subst pg,,$(PG_VERSION))
	@echo "Starting PostgreSQL with PGDATABASE=contrib_regression..."
	PGDATABASE=contrib_regression ./scripts/pg-start.sh --pg-version $(subst pg,,$(PG_VERSION))
	@echo "Running pg_regress tests..."
	PGHOST=$(HOME)/.pgrx PGUSER=postgres PG_CONFIG=$$(cargo pgrx info pg-config $(PG_VERSION)) $(MAKE) -e installcheck

# Help
help:
	@echo "pg_durable Makefile targets:"
	@echo ""
	@echo "  all           - Build release artifacts for source installation"
	@echo "  build         - Build the extension in debug mode"
	@echo "  package       - Build release artifacts for source installation"
	@echo "  test          - Run all tests (unit + E2E)"
	@echo "  test-unit     - Run pgrx unit tests only"
	@echo "  test-e2e      - Run E2E tests only (Docker)"
	@echo "  test-regress  - Run pg_regress tests (resets and starts PostgreSQL)"
	@echo "  installcheck  - Run pg_regress tests (requires PostgreSQL running, via PGXS)"
	@echo "  docker-build  - Build Docker image"
	@echo "  docker-push   - Build and push to ACR"
	@echo "  run           - Start local pgrx dev server"
	@echo "  pg-clean      - Clean build artifacts"
	@echo "  install       - Install prebuilt artifacts for the selected PostgreSQL"
	@echo "  uninstall     - Remove installed artifacts for the selected PostgreSQL"
	@echo "  pg-install    - Install extension locally"
	@echo "  pgxn-zip      - Build the PGXN release bundle (META.json + zip)"

# ============================================================================
# PGXN packaging
# ============================================================================
# The distribution version is read from Cargo.toml so the PGXN metadata can
# never drift from the crate version. META.json is generated, not committed.
DISTNAME    = pg_durable
DISTVERSION = $(shell sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)

# Render the PGXN metadata, stamping in the Cargo.toml version. This reuses the
# same @CARGO_VERSION@ token that pgrx substitutes into pg_durable.control.
META.json: META.json.in Cargo.toml
	sed 's/@CARGO_VERSION@/$(DISTVERSION)/g' $< > $@

# Build the PGXN release bundle. git archive ships only committed files, so the
# generated META.json is added explicitly.
pgxn-zip: META.json
	git archive --format zip --prefix $(DISTNAME)-$(DISTVERSION)/ \
	    --add-file META.json -o $(DISTNAME)-$(DISTVERSION).zip HEAD

# ============================================================================
# pg_regress (PGXS) configuration
# ============================================================================
EXTENSION = pg_durable

REGRESS = 00_init simple sequence variables parallel conditional

REQUESTED_GOALS := $(if $(MAKECMDGOALS),$(MAKECMDGOALS),all)
PG_CONFIG_GOALS := all package install uninstall installcheck

ifneq ($(filter $(PG_CONFIG_GOALS),$(REQUESTED_GOALS)),)
ifndef PG_CONFIG
PG_CONFIG := $(shell $(CARGO) pgrx info pg-config $(PG_VERSION) 2>/dev/null)
ifeq ($(PG_CONFIG),)
PG_CONFIG := $(shell command -v pg_config 2>/dev/null)
endif
endif

ifeq ($(PG_CONFIG),)
$(error PG_CONFIG is not set and pg_config could not be found via cargo-pgrx or PATH)
endif

PG_MAJOR := $(shell "$(PG_CONFIG)" --version 2>/dev/null | sed -nE 's/^PostgreSQL ([0-9]+).*/\1/p')
ifeq ($(filter $(PG_MAJOR),17 18),)
$(error unsupported PostgreSQL major '$(PG_MAJOR)'; pg_durable supports PostgreSQL 17 and 18)
endif
endif

ifneq ($(filter install uninstall,$(REQUESTED_GOALS)),)
ifneq ($(filter installcheck,$(REQUESTED_GOALS)),)
$(error run 'make install' or 'make uninstall' and 'make installcheck' as separate commands)
endif
endif

# PGXS is used only for the regression-test entry point. Keeping it out of
# normal builds prevents its `install` recipe from conflicting with ours.
ifneq ($(filter installcheck,$(REQUESTED_GOALS)),)
ifndef PGXS
PGXS := $(shell "$(PG_CONFIG)" --pgxs)
endif
include $(PGXS)
endif

# ============================================================================
# Source installation
# ============================================================================
DEFAULT_PGRX_PACKAGE_DIR = $(CURDIR)/target/release/pg_durable-pg$(PG_MAJOR)
PGRX_PACKAGE_DIR ?= $(DEFAULT_PGRX_PACKAGE_DIR)
PGRX_PACKAGE_MARKER = $(PGRX_PACKAGE_DIR).pg_durable-owned
PG_DLSUFFIX ?= $(if $(filter Darwin,$(shell uname -s)),.dylib,.so)

ifeq ($(filter installcheck,$(REQUESTED_GOALS)),)

PG_PKGLIBDIR = $(shell "$(PG_CONFIG)" --pkglibdir)
PG_EXTENSION_DIR = $(shell "$(PG_CONFIG)" --sharedir)/extension
PACKAGE_LIBRARY = $(PGRX_PACKAGE_DIR)$(PG_PKGLIBDIR)/pg_durable$(PG_DLSUFFIX)
PACKAGE_EXTENSION_DIR = $(PGRX_PACKAGE_DIR)$(PG_EXTENSION_DIR)
# Same directories relative to the package root, for auditing the packaged tree.
PG_PKGLIBDIR_REL = $(patsubst /%,%,$(PG_PKGLIBDIR))
PG_EXTENSION_DIR_REL = $(patsubst /%,%,$(PG_EXTENSION_DIR))

install:
	@set -eu; \
	package_library="$(PACKAGE_LIBRARY)"; \
	package_extension_dir="$(PACKAGE_EXTENSION_DIR)"; \
	test -f "$$package_library" || { echo "missing packaged library: $$package_library; run 'make package' first" >&2; exit 1; }; \
	test -f "$$package_extension_dir/pg_durable.control" || { echo "missing packaged control file; run 'make package' first" >&2; exit 1; }; \
	set -- "$$package_extension_dir"/pg_durable--*.sql; \
	test -f "$$1" || { echo "missing packaged SQL files; run 'make package' first" >&2; exit 1; }; \
	unexpected="$$(cd "$(PGRX_PACKAGE_DIR)" && find . -type f \
	    ! -path "./$(PG_PKGLIBDIR_REL)/pg_durable$(PG_DLSUFFIX)" \
	    ! -path "./$(PG_EXTENSION_DIR_REL)/pg_durable.control" \
	    ! -path "./$(PG_EXTENSION_DIR_REL)/pg_durable--*.sql")"; \
	test -z "$$unexpected" || { \
	    echo "packaged tree contains files this target does not install:" >&2; \
	    echo "$$unexpected" >&2; \
	    echo "the Debian package ships the whole tree, so a source install would silently differ from it; extend the install and uninstall recipes or exclude these files" >&2; \
	    exit 1; }; \
	install -d -m 0755 "$(DESTDIR)$(PG_PKGLIBDIR)" "$(DESTDIR)$(PG_EXTENSION_DIR)"; \
	install -m 0755 "$$package_library" "$(DESTDIR)$(PG_PKGLIBDIR)/pg_durable$(PG_DLSUFFIX)"; \
	install -m 0644 "$$package_extension_dir/pg_durable.control" "$$@" "$(DESTDIR)$(PG_EXTENSION_DIR)/"

# `pgxn uninstall` runs this target directly, without building first, so it must
# not depend on a package tree. It removes only what `install` places, and is
# idempotent so a partial installation can always be cleaned up.
uninstall:
	@set -eu; \
	extension_dir="$(DESTDIR)$(PG_EXTENSION_DIR)"; \
	library="$(DESTDIR)$(PG_PKGLIBDIR)/pg_durable$(PG_DLSUFFIX)"; \
	removed=0; \
	if test -e "$$library"; then rm -f "$$library"; removed=1; fi; \
	if test -e "$$extension_dir/pg_durable.control"; then rm -f "$$extension_dir/pg_durable.control"; removed=1; fi; \
	for sql in "$$extension_dir"/pg_durable--*.sql; do \
	    test -e "$$sql" || continue; \
	    rm -f "$$sql"; \
	    removed=1; \
	done; \
	test "$$removed" -eq 1 || echo "pg_durable is not installed for $(PG_CONFIG); nothing to remove"

endif
