.PHONY: all build clean test test-all test-ci test-integration-go test-unit-go test-connection generate-test-config schema-install broker-start broker-stop install deps docker-up docker-down docker-build release help

# Build variables
BINARY_NAME=pgsql-broker
BIN_DIR=bin
CMD_DIR=cmd/broker
GO=go
GOFLAGS=-v
LDFLAGS=-w -s

# Auto-detect container runtime (Docker or Podman)
CONTAINER_RUNTIME := $(shell \
	if command -v podman > /dev/null 2>&1; then \
		echo "podman"; \
	elif command -v docker > /dev/null 2>&1; then \
		echo "docker"; \
	else \
		echo "none"; \
	fi)

# Detect compose command
COMPOSE_CMD := $(shell \
	if [ "$(CONTAINER_RUNTIME)" = "podman" ]; then \
		echo "podman-compose"; \
	elif command -v docker-compose > /dev/null 2>&1; then \
		echo "docker-compose"; \
	else \
		echo "docker compose"; \
	fi)



# Test database connection info. Override in CI to point at a dynamically
# assigned Postgres (e.g. a services: block port), avoiding a fixed host port
# that can collide with other jobs on a shared runner.
TEST_DB_HOST ?= 127.0.0.1
TEST_DB_PORT ?= 5433
TEST_CONFIG := $(BIN_DIR)/broker.test.runtime.yaml

# Version information
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
BUILD_TIME=$(shell date -u '+%Y-%m-%d_%H:%M:%S')
COMMIT=$(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")

# Inject version info
LDFLAGS += -X 'main.Version=$(VERSION)' -X 'main.BuildTime=$(BUILD_TIME)' -X 'main.Commit=$(COMMIT)'

all: clean deps build ## Build everything

build: ## Build the broker binary
	@echo "Building $(BINARY_NAME)..."
	@mkdir -p $(BIN_DIR)
	$(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(BIN_DIR)/$(BINARY_NAME) ./$(CMD_DIR)
	@echo "Built: $(BIN_DIR)/$(BINARY_NAME)"

clean: ## Remove build artifacts
	@echo "Cleaning..."
	@rm -rf $(BIN_DIR)
	@$(GO) clean
	@echo "Clean complete"

deps: ## Download dependencies
	@echo "Downloading dependencies..."
	@$(GO) mod download
	@$(GO) mod tidy
	@echo "Dependencies ready"

# Main test target to run everything
test-local-unit: deps ## Run local unit tests
	@echo "Running local unit tests..."
	@$(GO) test -v -race -cover $(shell $(GO) list ./... | grep -v /tests/integration)

test-all: test-teardown test-setup test-connection schema-install broker-start test-local-unit test-integration-go broker-stop test-teardown ## Run all unit and integration tests (starts its own Postgres via docker-compose)

test-ci: test-connection schema-install broker-start test-local-unit test-integration-go broker-stop ## Run all unit and integration tests against an externally-provided Postgres (CI services: block)

test-connection: deps ## Test database connection with retry
	@echo "Testing database connection (host=$(TEST_DB_HOST) port=$(TEST_DB_PORT))..."
	@TEST_DB_HOST=$(TEST_DB_HOST) TEST_DB_PORT=$(TEST_DB_PORT) $(GO) test -v -run '^TestConnection$$' ./tests/integration/...

generate-test-config: ## (internal) render broker.test.yaml with TEST_DB_HOST/TEST_DB_PORT
	@mkdir -p $(BIN_DIR)
	@sed -e "s/^    host: .*/    host: $(TEST_DB_HOST)/" -e "s/^    port: .*/    port: $(TEST_DB_PORT)/" broker.test.yaml > $(TEST_CONFIG)

schema-install: build generate-test-config ## Install database schema using the broker CLI
	@echo "Installing database schema..."
	@$(BIN_DIR)/$(BINARY_NAME) install --config $(TEST_CONFIG)

test-setup: build ## Start test environment (docker-compose/podman-compose)
	@echo "Starting test environment (using $(COMPOSE_CMD))..."
	@if [ "$(CONTAINER_RUNTIME)" = "none" ]; then \
		echo "Error: Neither Docker nor Podman is installed"; \
		exit 1; \
	fi
	@$(COMPOSE_CMD) -f tests/docker-compose.yml up -d
	@echo "Waiting for PostgreSQL to be ready..."
	@for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do \
		if $(COMPOSE_CMD) -f tests/docker-compose.yml exec -T postgres pg_isready -U user > /dev/null 2>&1; then \
			echo "PostgreSQL is ready"; exit 0; \
		fi; \
		sleep 2; \
	done; \
	echo "ERROR: PostgreSQL did not become ready in time"; \
	$(COMPOSE_CMD) -f tests/docker-compose.yml logs postgres; \
	exit 1

test-teardown: ## Stop test environment (docker-compose/podman-compose)
	@echo "Stopping test environment (using $(COMPOSE_CMD))..."
	@if [ "$(CONTAINER_RUNTIME)" = "none" ]; then \
		echo "Neither Docker nor Podman is installed, skipping teardown"; \
	else \
		$(COMPOSE_CMD) -f tests/docker-compose.yml down -v --rmi all || true; \
	fi
	@sleep 5 # Give the container runtime time to release resources

broker-start: build generate-test-config ## Start the broker in the background
	@echo "Starting broker..."
	@setsid $(BIN_DIR)/$(BINARY_NAME) start --config $(TEST_CONFIG) > broker.log 2>&1 < /dev/null & echo $$! > broker.pid
	@sleep 5 # Give the broker a moment to start

broker-stop: ## Stop the broker
	@echo "Stopping broker..."
	@if [ -f broker.pid ]; then \
		kill -15 -- -$$(cat broker.pid); \
		rm broker.pid; \
	else \
		echo "broker.pid not found, broker might not be running."; \
	fi

test-integration-go: ## Run Go integration tests
	@echo "Running Go integration tests (host=$(TEST_DB_HOST) port=$(TEST_DB_PORT))..."
	@TEST_DB_HOST=$(TEST_DB_HOST) TEST_DB_PORT=$(TEST_DB_PORT) $(GO) test -v ./tests/integration/...

install: build ## Install the binary to GOPATH/bin
	@echo "Installing to GOPATH/bin..."
	@$(GO) install $(GOFLAGS) -ldflags "$(LDFLAGS)" ./$(CMD_DIR)
	@echo "Installed: $(BINARY_NAME)"

run: build ## Build and run the broker
	@echo "Running $(BINARY_NAME)..."
	@$(BIN_DIR)/$(BINARY_NAME)

fmt: ## Format the code
	@echo "Formatting code..."
	@$(GO) fmt ./...
	@echo "Formatting complete"

vet: ## Run go vet
	@echo "Running go vet..."
	@$(GO) vet ./...
	@echo "Vet complete"

lint: ## Run golangci-lint (if installed)
	@if command -v golangci-lint >/dev/null 2>&1; then \
		 echo "Running golangci-lint..."; \
		 golangci-lint run ./...; \
	else \
		 echo "golangci-lint not installed, skipping"; \
	fi

sql-install: build ## Install SQL tables and procedures using broker CLI
	@echo "Installing SQL schema..."
	@$(BIN_DIR)/$(BINARY_NAME) install

sql-install-manual: ## Install SQL tables and procedures manually via psql
	@echo "Installing SQL schema manually..."
	@if [ -z "$$PGDATABASE" ]; then \
		echo "Error: PGDATABASE environment variable not set"; \
		exit 1; \
	fi
	@psql -f pkg/broker/install/sql/tables/00_install.sql
	@psql -f pkg/broker/install/sql/procedures/00_install.sql
	@echo "SQL schema installed"

docker-up: ## Start PostgreSQL test database
	@echo "Starting PostgreSQL test database (using $(CONTAINER_RUNTIME))..."
	@if [ "$(CONTAINER_RUNTIME)" = "none" ]; then \
		echo "Error: Neither Docker nor Podman is installed"; \
		exit 1; \
	fi
	@if [ "$(CONTAINER_RUNTIME)" = "podman" ]; then \
		podman run -d --name pgsql-broker-test-postgres \
			-e POSTGRES_USER=user \
			-e POSTGRES_PASSWORD=password \
			-e POSTGRES_DB=broker_test \
			-p 5433:5432 \
			postgres:13 2>/dev/null || echo "Container already running"; \
	else \
		cd tests && $(COMPOSE_CMD) up -d; \
	fi
	@echo "Waiting for PostgreSQL to be ready..."
	@sleep 3
	@echo "PostgreSQL is running on port 5433"
	@echo "Connection: postgres://user:password@localhost:5433/broker_test"

docker-down: ## Stop PostgreSQL test database
	@echo "Stopping PostgreSQL test database (using $(CONTAINER_RUNTIME))..."
	@if [ "$(CONTAINER_RUNTIME)" = "podman" ]; then \
		podman stop pgsql-broker-test-postgres 2>/dev/null || true; \
		podman rm pgsql-broker-test-postgres 2>/dev/null || true; \
	else \
		cd tests && $(COMPOSE_CMD) down; \
	fi
	@echo "PostgreSQL stopped"

docker-build: ## Build the pgsql-broker runtime image
	@if [ "$(CONTAINER_RUNTIME)" = "none" ]; then echo "Error: Neither Docker nor Podman is installed"; exit 1; fi
	@$(CONTAINER_RUNTIME) build \
		--build-arg VERSION=$(VERSION) \
		--build-arg COMMIT=$(COMMIT) \
		--build-arg BUILD_TIME=$(BUILD_TIME) \
		-t pgsql-broker:$(VERSION) -t pgsql-broker:latest \
		-f Dockerfile .

release: ## Create and push a new release tag (auto-increments patch version)
	@echo "Creating new release..."
	@latest_tag=$$(git describe --tags --abbrev=0 2>/dev/null || echo ""); \
	if [ -z "$$latest_tag" ]; then \
		version="v1.0.0"; \
		echo "No existing tags found. Creating first release: $$version"; \
		commit_logs=$$(git log --pretty=format:"- %s" --no-merges); \
	else \
		echo "Latest tag: $$latest_tag"; \
		version_number=$${latest_tag#v}; \
		IFS='.' read -r major minor patch <<< "$$version_number"; \
		patch=$$((patch + 1)); \
		version="v$$major.$$minor.$$patch"; \
		echo "Creating new release: $$version"; \
		commit_logs=$$(git log "$${latest_tag}..HEAD" --pretty=format:"- %s" --no-merges); \
	fi; \
	if [ -z "$$commit_logs" ]; then \
		tag_message="Release $$version"; \
	else \
		tag_message="Release $$version\n\n$$commit_logs"; \
	fi; \
	git tag -a "$$version" -m "$$tag_message"; \
	git push origin "$$version"; \
	echo "Tag $$version created and pushed to remote repository."


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