# Makefile für die Entwicklung an Syncova.
#
# Überblick über alle Ziele:  make help

SHELL := /bin/bash

# Verzeichnis für gebaute Binaries.
BIN_DIR := bin

# Compose-Datei der lokalen Entwicklungsumgebung.
COMPOSE_FILE := deployment/docker-compose.yml

# Datei mit der lokalen Konfiguration. Sie wird von "make dev-env" erzeugt.
ENV_FILE := .env

# Version, die in die Binaries eingebrannt wird.
# Ohne Git-Tag wird der Kurz-Hash des Commits verwendet.
BUILD_VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "0.1.0-dev")

# Linker-Flags setzen die Version zur Bauzeit.
GO_LDFLAGS := -X main.buildVersion=$(BUILD_VERSION)

# Zu prüfende Go-Pakete.
#
# node_modules wird ausgeschlossen: einzelne NPM-Pakete bringen eigenen Go-Code
# mit, der sonst in Tests, Lint und Schwachstellenprüfung auftauchen würde.
GO_PACKAGES := $(shell go list ./... 2>/dev/null | grep -v '/node_modules/')

# Verzeichnisse mit eigenem Go-Quelltext (für gofmt, das keine Paketpfade kennt).
GO_SOURCE_DIRS := apps/api apps/agent packages migrations

# Alle Rezepte laufen mit den Variablen aus der .env, sofern vorhanden.
ifneq (,$(wildcard $(ENV_FILE)))
include $(ENV_FILE)
export
endif

.DEFAULT_GOAL := help

.PHONY: help
help: ## Zeigt diese Übersicht
	@echo "Syncova – verfügbare Ziele:"
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "  \033[36m%-18s\033[0m %s\n", $$1, $$2}'

# ---------------------------------------------------------------------------
# Entwicklungsumgebung
# ---------------------------------------------------------------------------

.PHONY: dev-env
dev-env: ## Erzeugt .env mit einem zufälligen Datenbankpasswort
	@if [ -f $(ENV_FILE) ]; then \
		echo "$(ENV_FILE) existiert bereits – es wird nicht überschrieben."; \
		exit 0; \
	fi
	@cp .env.example $(ENV_FILE)
	@# Das Passwort wird lokal zufällig erzeugt, damit es nirgends eingecheckt ist.
	@GENERATED_PASSWORD="$$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)"; \ # secretscan:erlaubt: erfundener Testwert
	sed -i.bak "s|^SYNCOVA_DB_PASSWORD=.*|SYNCOVA_DB_PASSWORD=$$GENERATED_PASSWORD|" $(ENV_FILE); \
	rm -f $(ENV_FILE).bak
	@# Ohne Verschlüsselungsschlüssel startet kein Dienst; er wird deshalb
	@# gleich miterzeugt. openssl liefert genau die geforderten 32 Byte.
	@GENERATED_KEY="$$(openssl rand -base64 32)"; \
	sed -i.bak "s|^SYNCOVA_ENCRYPTION_KEYS=.*|SYNCOVA_ENCRYPTION_KEYS=v1:$$GENERATED_KEY|" $(ENV_FILE); \
	rm -f $(ENV_FILE).bak
	@chmod 600 $(ENV_FILE)
	@echo "$(ENV_FILE) wurde mit zufälligem Datenbankpasswort und Verschlüsselungsschlüssel erzeugt."

.PHONY: dev-up
dev-up: require-env ## Startet PostgreSQL und wartet auf Betriebsbereitschaft
	@docker compose --env-file $(ENV_FILE) -f $(COMPOSE_FILE) up -d --wait
	@echo "PostgreSQL ist bereit."

.PHONY: dev-down
dev-down: ## Stoppt die lokale Umgebung (Daten bleiben erhalten)
	@docker compose -f $(COMPOSE_FILE) down

.PHONY: dev-reset
dev-reset: ## Stoppt die Umgebung und LÖSCHT alle lokalen Datenbankdaten
	@echo "Achtung: Dieser Schritt löscht das lokale Datenbank-Volume."
	@read -p "Fortfahren? [j/N] " CONFIRMATION; \
	if [ "$$CONFIRMATION" = "j" ] || [ "$$CONFIRMATION" = "J" ]; then \
		docker compose -f $(COMPOSE_FILE) down --volumes; \
		echo "Lokale Datenbankdaten wurden gelöscht."; \
	else \
		echo "Abgebrochen."; \
	fi

.PHONY: require-env
require-env: ## Prüft, ob die lokale Konfiguration vorhanden ist
	@if [ ! -f $(ENV_FILE) ]; then \
		echo "Es fehlt die Datei $(ENV_FILE). Bitte zuerst 'make dev-env' ausführen."; \
		exit 1; \
	fi

# ---------------------------------------------------------------------------
# Backend
# ---------------------------------------------------------------------------

.PHONY: build
build: ## Baut alle Go-Binaries nach ./bin
	@mkdir -p $(BIN_DIR)
	@go build -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/syncova-api ./apps/api/cmd/syncova-api
	@go build -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/syncova-migrate ./apps/api/cmd/syncova-migrate
	@go build -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/syncova-admin ./apps/api/cmd/syncova-admin
	@go build -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/syncova-repo ./apps/api/cmd/syncova-repo
	@go build -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/syncova-dr ./apps/api/cmd/syncova-dr
	@go build -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/syncova-bench ./apps/api/cmd/syncova-bench
	@go build -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/syncova-agent ./apps/agent/cmd/syncova-agent
	@go build -ldflags "$(GO_LDFLAGS)" -o $(BIN_DIR)/syncova-proxmox ./apps/api/cmd/syncova-proxmox
	@echo "Binaries gebaut (Version $(BUILD_VERSION))."

.PHONY: run-api
run-api: require-env ## Startet den API-Dienst lokal
	@go run -ldflags "$(GO_LDFLAGS)" ./apps/api/cmd/syncova-api

.PHONY: migrate-up
migrate-up: require-env ## Wendet alle ausstehenden Datenbankmigrationen an
	@go run ./apps/api/cmd/syncova-migrate up

.PHONY: migrate-status
migrate-status: require-env ## Zeigt den aktuellen Migrationsstand
	@go run ./apps/api/cmd/syncova-migrate status

.PHONY: migrate-down
migrate-down: require-env ## Nimmt genau eine Migration zurück
	@go run ./apps/api/cmd/syncova-migrate down

.PHONY: test
test: ## Führt alle Go-Tests mit Race-Detector aus
	@go test -race $(GO_PACKAGES)

.PHONY: test-coverage
test-coverage: ## Führt die Go-Tests aus und schreibt einen Coverage-Bericht
	@go test -race -coverprofile=coverage.out $(GO_PACKAGES)
	@go tool cover -func=coverage.out | tail -1

.PHONY: lint
lint: ## Prüft Formatierung und statische Analyse des Go-Codes
	@echo "==> gofmt"
	@UNFORMATTED_FILES="$$(gofmt -l $(GO_SOURCE_DIRS))"; \
	if [ -n "$$UNFORMATTED_FILES" ]; then \
		echo "Nicht formatierte Dateien gefunden:"; echo "$$UNFORMATTED_FILES"; exit 1; \
	fi
	@echo "==> go vet"
	@go vet $(GO_PACKAGES)
	@echo "==> staticcheck"
	@go run honnef.co/go/tools/cmd/staticcheck@latest $(GO_PACKAGES)

# Die Sicherheitsprüfungen laufen getrennt vom Lint, weil sie das Netz brauchen:
# govulncheck gleicht gegen die Schwachstellendatenbank von Go ab. In einer
# Umgebung ohne Netz soll `make lint` trotzdem durchlaufen.
# Die Plattformprüfung ist keine Formalie.
#
# Bis Phase 5 liess sich der Agent fuer Windows **gar nicht uebersetzen** —
# unix.Statfs gibt es dort nicht. Der Windows-Dienst galt als „geschrieben, aber
# ungeprueft"; tatsaechlich haette er sich nicht einmal bauen lassen. Ein
# Uebersetzungslauf je Zielplattform faengt das in Sekunden.
.PHONY: cross-build
cross-build: ## Prüft die Übersetzbarkeit für alle Zielplattformen
	@echo "==> windows/amd64"
	@GOOS=windows GOARCH=amd64 go build -o /dev/null ./apps/agent/cmd/syncova-agent
	@GOOS=windows GOARCH=amd64 go build -o /dev/null ./apps/api/cmd/syncova-api
	@GOOS=windows GOARCH=amd64 go build -o /dev/null ./apps/api/cmd/syncova-repo
	@echo "==> linux/amd64"
	@GOOS=linux GOARCH=amd64 go build -o /dev/null ./apps/agent/cmd/syncova-agent
	@GOOS=linux GOARCH=amd64 go build -o /dev/null ./apps/api/cmd/syncova-api
	@echo "==> linux/arm64"
	@GOOS=linux GOARCH=arm64 go build -o /dev/null ./apps/agent/cmd/syncova-agent
	@echo "Alle Zielplattformen übersetzen."

.PHONY: security-scan
security-scan: ## Prüft Abhängigkeiten auf bekannte Schwachstellen
	@echo "==> govulncheck"
	@go run golang.org/x/vuln/cmd/govulncheck@latest $(GO_PACKAGES)
	@echo "==> npm audit (Frontend)"
	@cd apps/web && npm audit --audit-level=high

.PHONY: tidy
tidy: ## Bereinigt die Go-Modulabhängigkeiten
	@go mod tidy

.PHONY: generate-key
generate-key: ## Erzeugt einen neuen Verschlüsselungsschlüssel
	@go run ./apps/api/cmd/syncova-admin generate-key

.PHONY: create-admin
create-admin: require-env ## Legt den ersten Administrator an (fragt nach dem Passwort)
	@go run ./apps/api/cmd/syncova-admin create-admin --username $(USERNAME)

# ---------------------------------------------------------------------------
# Frontend
# ---------------------------------------------------------------------------

.PHONY: web-install
web-install: ## Installiert die Frontend-Abhängigkeiten
	@cd apps/web && npm ci

.PHONY: web-dev
web-dev: ## Startet den Frontend-Entwicklungsserver
	@cd apps/web && npm run dev

.PHONY: web-build
web-build: ## Baut das Frontend für die Auslieferung
	@cd apps/web && npm run build

.PHONY: web-test
web-test: ## Führt die Frontend-Tests aus
	@cd apps/web && npm run test

.PHONY: web-lint
web-lint: ## Prüft den Frontend-Code
	@cd apps/web && npm run lint

# ---------------------------------------------------------------------------
# Übergreifend
# ---------------------------------------------------------------------------

.PHONY: secret-scan
secret-scan: ## Durchsucht den Quellbestand nach Geheimnissen
	@go test -count=1 -run TestRepositoryContainsNoSecrets ./packages/platform/secretscan/

.PHONY: release
release: ## Baut das Auslieferungspaket nach ./dist
	@scripts/build-release.sh

.PHONY: check
check: lint cross-build test web-lint web-test ## Führt alle Prüfungen aus (wie in der CI)
	@echo "Alle Prüfungen bestanden."
