# SPDX-License-Identifier: Apache-2.0
#
# Entry point for building, testing and releasing. Run `make help`.

# ---------------------------------------------------------------------------
# Central names (ADR 0018). Override on the command line to rebrand, e.g.
#   make release PRODUCT=acme-ddi DISPLAY_NAME="ACME DDI"
# ---------------------------------------------------------------------------
PRODUCT        ?= ddi-suite
DISPLAY_NAME   ?= DDI-Suite
BIN_SERVER     ?= ddi-server
BIN_AGENT_DNS  ?= ddi-agent-dns
BIN_AGENT_DHCP ?= ddi-agent-dhcp

# Licensing trust anchor embedded into the server (ADR 0016). Both values are
# public; they are embedded so that configuration cannot redirect licensing.
KEYGEN_ACCOUNT_ID ?= 9de54794-8e94-4250-83f6-87e3d5686a0b
KEYGEN_PUBLIC_KEY ?= 40495568fa239e44736e28949ae2ae93f2c9b6e6bc6d917f4c8cc0eb629e3c62

MODULE     := git.beugsiud.de/beug.k/DDI
VERSION    ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
COMMIT     ?= $(shell git rev-parse HEAD 2>/dev/null || echo unknown)
BUILD_DATE ?= $(shell git log -1 --format=%cI 2>/dev/null)

PLATFORMS ?= linux/amd64 linux/arm64
BIN_DIR   ?= bin
DIST_DIR  ?= dist

COMPOSE_DEV := docker compose -f deploy/compose/compose.dev.yaml

export PRODUCT DISPLAY_NAME BIN_SERVER BIN_AGENT_DNS BIN_AGENT_DHCP VERSION COMMIT BUILD_DATE
export KEYGEN_ACCOUNT_ID KEYGEN_PUBLIC_KEY
export DDI_DISPLAY_NAME := $(DISPLAY_NAME)
export CGO_ENABLED := 0

# Values are single-quoted so that names may contain spaces.
GO_LDFLAGS := -s -w \
	-X '$(MODULE)/internal/buildinfo.Version=$(VERSION)' \
	-X '$(MODULE)/internal/buildinfo.Commit=$(COMMIT)' \
	-X '$(MODULE)/internal/buildinfo.Date=$(BUILD_DATE)' \
	-X '$(MODULE)/internal/brand.Product=$(PRODUCT)' \
	-X '$(MODULE)/internal/brand.DisplayName=$(DISPLAY_NAME)' \
	-X '$(MODULE)/internal/brand.ServerBinary=$(BIN_SERVER)' \
	-X '$(MODULE)/internal/brand.AgentDNSBinary=$(BIN_AGENT_DNS)' \
	-X '$(MODULE)/internal/brand.AgentDHCPBinary=$(BIN_AGENT_DHCP)' \
	-X '$(MODULE)/internal/brand.ConfigDir=/etc/$(PRODUCT)' \
	-X '$(MODULE)/internal/brand.StateDir=/var/lib/$(PRODUCT)' \
	-X '$(MODULE)/internal/brand.LogDir=/var/log/$(PRODUCT)' \
	-X '$(MODULE)/server/internal/license.EmbeddedAccountID=$(KEYGEN_ACCOUNT_ID)' \
	-X '$(MODULE)/server/internal/license.EmbeddedPublicKey=$(KEYGEN_PUBLIC_KEY)'

SHELL_SCRIPTS := $(shell git ls-files '*.sh')

.DEFAULT_GOAL := help

## help: list targets
.PHONY: help
help:
	@awk -F': ' '/^## /{ sub(/^## /, ""); printf "  %-16s %s\n", $$1, $$2 }' $(MAKEFILE_LIST)

# --------------------------------------------------------------------------- build

## build: build web UI and all binaries for the host platform into bin/
.PHONY: build
build: web build-go

## build-go: build the Go binaries only (uses whatever is in web/dist)
.PHONY: build-go
build-go:
	go build -trimpath -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/$(BIN_SERVER) ./server/cmd/ddi-server
	go build -trimpath -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/$(BIN_AGENT_DNS) ./agent/cmd/ddi-agent-dns
	go build -trimpath -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/$(BIN_AGENT_DHCP) ./agent/cmd/ddi-agent-dhcp

## web-deps: install frontend dependencies exactly as locked
.PHONY: web-deps
web-deps:
	cd web && npm ci --no-fund --no-audit

## web: build the frontend into web/dist (embedded by the server)
.PHONY: web
web: web-deps
	cd web && npm run build

## generate: regenerate protobuf code and the TypeScript API types
.PHONY: generate
generate:
	buf generate
	cd web && npm run gen:api

## check-generated: fail if generated code is out of date
.PHONY: check-generated
check-generated: generate
	git diff --exit-code -- proto/gen web/src/api/schema.d.ts

# --------------------------------------------------------------------------- quality

## fmt: format Go, protobuf and frontend code
.PHONY: fmt
fmt:
	golangci-lint fmt ./...
	buf format -w
	cd web && npm run format

## lint: run all linters
.PHONY: lint
lint: lint-go lint-proto lint-web lint-shell lint-spdx

.PHONY: lint-go
lint-go:
	go vet ./...
	golangci-lint run ./...

.PHONY: lint-proto
lint-proto:
	buf lint
	buf format --diff --exit-code

.PHONY: lint-web
lint-web:
	cd web && npm run lint && npm run format:check && npm run typecheck

.PHONY: lint-shell
lint-shell:
	shellcheck $(SHELL_SCRIPTS)

.PHONY: lint-spdx
lint-spdx:
	scripts/check-spdx.sh

## test: run Go and frontend unit tests
.PHONY: test
test: test-go test-web

.PHONY: test-go
test-go:
	CGO_ENABLED=1 go test -race -count=1 -coverprofile=coverage.txt ./...

.PHONY: test-web
test-web:
	cd web && npm test

## test-e2e: API change -> revision -> agents -> BIND/Kea in containers (needs docker)
.PHONY: test-e2e
test-e2e: build
	test/e2e/run.sh

## test-integration: generated configs and agent roles against real BIND and Kea (needs docker)
.PHONY: test-integration
test-integration:
	go test -tags integration -count=1 -v -timeout 20m \
		./server/internal/configgen/integration/ ./agent/role/integration/

## vuln: scan dependencies for known vulnerabilities
.PHONY: vuln
vuln:
	go tool govulncheck ./...
	cd web && npm audit --audit-level=high

## ci: everything the CI checks, in one go
.PHONY: ci
ci: lint check-generated test vuln build

# --------------------------------------------------------------------------- release

## release: cross-compile tarballs and SHA256SUMS into dist/
.PHONY: release
release: web
	scripts/build-release.sh -o $(DIST_DIR) -p "$(PLATFORMS)"

# --------------------------------------------------------------------------- development

## dev-up: start PostgreSQL, BIND and Kea for development
.PHONY: dev-up
dev-up:
	$(COMPOSE_DEV) up -d --build --wait

## dev-down: stop the development environment (keeps volumes)
.PHONY: dev-down
dev-down:
	$(COMPOSE_DEV) down

## dev-logs: follow logs of the development environment
.PHONY: dev-logs
dev-logs:
	$(COMPOSE_DEV) logs -f

## dev-smoke: verify the development environment answers (needs dev-up)
.PHONY: dev-smoke
dev-smoke:
	deploy/compose/smoke-test.sh

DEV_CONFIG := deploy/config/server.dev.yaml

local/master.key:
	go run ./server/cmd/ddi-server gen-master-key -out $@

## run-server: run the server on http://127.0.0.1:8080 against the dev database (make dev-up)
.PHONY: run-server
run-server: local/master.key
	DDI_CONFIG_FILE=$(DEV_CONFIG) go run ./server/cmd/ddi-server serve

## dev-admin: create the first administrator in the dev database
.PHONY: dev-admin
dev-admin: local/master.key
	DDI_CONFIG_FILE=$(DEV_CONFIG) go run ./server/cmd/ddi-server bootstrap-admin

## clean: remove build output
.PHONY: clean
clean:
	rm -rf $(BIN_DIR) $(DIST_DIR) coverage.txt
	find web/dist -mindepth 1 ! -name .gitkeep -delete 2>/dev/null || true
