# Headscale Makefile
# Modern Makefile following best practices

# Version calculation
VERSION ?= $(shell git describe --always --tags --dirty)

# Build configuration
GOOS ?= $(shell uname | tr '[:upper:]' '[:lower:]')
ifeq ($(filter $(GOOS), openbsd netbsd solaris plan9), )
	PIE_FLAGS = -buildmode=pie
endif

# Tool availability check with nix warning
define check_tool
	@command -v $(1) >/dev/null 2>&1 || { \
		echo "Warning: $(1) not found. Run 'nix develop' to ensure all dependencies are available."; \
		exit 1; \
	}
endef

# Source file collections using shell find for better performance
GO_SOURCES := $(shell find . -name '*.go' -not -path './gen/*' -not -path './vendor/*')
PROTO_SOURCES := $(shell find . -name '*.proto' -not -path './gen/*' -not -path './vendor/*')
DOC_SOURCES := $(shell find . \( -name '*.md' -o -name '*.yaml' -o -name '*.yml' -o -name '*.ts' -o -name '*.js' -o -name '*.html' -o -name '*.css' -o -name '*.scss' -o -name '*.sass' \) -not -path './gen/*' -not -path './vendor/*' -not -path './node_modules/*')

# Default target
.PHONY: all
all: lint test build

# Dependency checking
.PHONY: check-deps
check-deps:
	$(call check_tool,go)
	$(call check_tool,golangci-lint)
	$(call check_tool,gofumpt)
	$(call check_tool,prettier)
	$(call check_tool,clang-format)
	$(call check_tool,buf)

# Build targets
.PHONY: build
build: check-deps $(GO_SOURCES) go.mod go.sum
	@echo "Building headscale..."
	go build $(PIE_FLAGS) -ldflags "-X main.version=$(VERSION)" -o headscale ./cmd/headscale

# Test targets
.PHONY: test
test: check-deps $(GO_SOURCES) go.mod go.sum
	@echo "Running Go tests..."
	go test -race ./...


# Formatting targets
.PHONY: fmt
fmt: fmt-go fmt-prettier fmt-proto

.PHONY: fmt-go
fmt-go: check-deps $(GO_SOURCES)
	@echo "Formatting Go code..."
	gofumpt -l -w .
	golangci-lint run --fix

.PHONY: fmt-prettier
fmt-prettier: check-deps $(DOC_SOURCES)
	@echo "Formatting documentation and config files..."
	prettier --write '**/*.{ts,js,md,yaml,yml,sass,css,scss,html}'
	prettier --write --print-width 80 --prose-wrap always CHANGELOG.md

.PHONY: fmt-proto
fmt-proto: check-deps $(PROTO_SOURCES)
	@echo "Formatting Protocol Buffer files..."
	clang-format -i $(PROTO_SOURCES)

# Linting targets
.PHONY: lint
lint: lint-go lint-proto

.PHONY: lint-go
lint-go: check-deps $(GO_SOURCES) go.mod go.sum
	@echo "Linting Go code..."
	golangci-lint run --timeout 10m

.PHONY: lint-proto
lint-proto: check-deps $(PROTO_SOURCES)
	@echo "Linting Protocol Buffer files..."
	cd proto/ && buf lint

# Code generation
.PHONY: generate
generate: check-deps
	@echo "Generating code..."
	go generate ./...

# Clean targets
.PHONY: clean
clean:
	rm -rf headscale gen

# Development workflow
.PHONY: dev
dev: fmt lint test build

# Help target
.PHONY: help
help:
	@echo "Headscale Development Makefile"
	@echo ""
	@echo "Main targets:"
	@echo "  all          - Run lint, test, and build (default)"
	@echo "  build        - Build headscale binary"
	@echo "  test         - Run Go tests"
	@echo "  fmt          - Format all code (Go, docs, proto)"
	@echo "  lint         - Lint all code (Go, proto)"
	@echo "  generate     - Generate code from Protocol Buffers"
	@echo "  dev          - Full development workflow (fmt + lint + test + build)"
	@echo "  clean        - Clean build artifacts"
	@echo ""
	@echo "Specific targets:"
	@echo "  fmt-go       - Format Go code only"
	@echo "  fmt-prettier - Format documentation only" 
	@echo "  fmt-proto    - Format Protocol Buffer files only"
	@echo "  lint-go      - Lint Go code only"
	@echo "  lint-proto   - Lint Protocol Buffer files only"
	@echo ""
	@echo "Dependencies:"
	@echo "  check-deps   - Verify required tools are available"
	@echo ""
	@echo "Note: If not running in a nix shell, ensure dependencies are available:"
	@echo "  nix develop"