.PHONY: help lint lint-go dupl-go test-go test-go-postgres test-go-all verify-go test-go-migrations fmt-go gen-openapi-go gen-openapi-ts gen-openapi gen-proto-go gen-asyncapi-event-contracts validate-asyncapi

help:
	@echo "Targets:"
	@echo "  make lint-go   - golangci-lint for active Go packages, excluding deprecated/**"
	@echo "  make dupl-go   - fail on duplicated Go code (dupl -t 50)"
	@echo "  make test-go   - hermetic go test for active Go packages, excluding PostgreSQL integration"
	@echo "  make test-go-postgres - run explicit PostgreSQL repository integration tests"
	@echo "  make test-go-all - run Go unit/component tests and explicit PostgreSQL integration tests"
	@echo "  make verify-go - alias for test-go-all"
	@echo "  make test-go-migrations - run migration guard tests for active service goose files"
	@echo "  make fmt-go    - gofmt -w on tracked .go files"
	@echo "  make gen-openapi-go [SVC=services/external/integration-gateway] - generate Go transport code from OpenAPI"
	@echo "  make gen-openapi-ts [APP=services/staff/web-console] - generate TS API client from OpenAPI"
	@echo "  make gen-openapi - run Go+TS OpenAPI generators for default services"
	@echo "  make gen-proto-go - generate Go gRPC contracts from active proto/**/*.proto"
	@echo "  make gen-asyncapi-event-contracts - generate Go event constants and payloads from AsyncAPI"
	@echo "  make validate-asyncapi [SVC=access-manager|SPEC=specs/asyncapi/access-manager.v1.yaml] - validate AsyncAPI contract"
	@echo "  make lint      - run all linters"

lint: lint-go dupl-go

lint-go:
	@packages="$$(for root in services cmd libs; do \
		if [ -d "$$root" ]; then printf './%s/... ' "$$root"; fi; \
	done)"; \
	if [ -z "$$packages" ]; then \
		echo "lint-go: no active Go packages"; \
	else \
		golangci-lint run $$packages; \
	fi; \
	for module in $$(find libs -name go.mod -not -path '*/vendor/*' -exec dirname {} \; 2>/dev/null | sort); do \
		echo "lint-go: $$module"; \
		(cd "$$module" && golangci-lint run ./...); \
	done

dupl-go:
	@tmp="$$(mktemp)"; \
	candidates="$$(mktemp)"; \
	roots="$$(for root in services libs; do if [ -d "$$root" ]; then printf '%s\n' "$$root"; fi; done)"; \
	if [ -n "$$roots" ]; then \
		printf '%s\n' "$$roots" | xargs rg --files -g '*.go' -g '!**/*_test.go' -g '!**/generated/**' -g '!**/*.gen.go' > "$$candidates"; \
	fi; \
	if [ ! -s "$$candidates" ]; then \
		rm -f "$$tmp" "$$candidates"; \
		exit 0; \
	fi; \
	dupl -t 50 -plumbing -files < "$$candidates" > "$$tmp"; \
	if [ -s "$$tmp" ]; then \
		cat "$$tmp"; \
		echo "dupl-go: duplicates found (threshold=50)"; \
		rm -f "$$tmp" "$$candidates"; \
		exit 1; \
	fi; \
	rm -f "$$tmp" "$$candidates"

test-go:
	@unit_env="env \
		-u KODEX_ACCESS_MANAGER_TEST_DATABASE_DSN \
		-u KODEX_EVENTLOG_TEST_DATABASE_DSN \
		-u KODEX_PROJECT_CATALOG_TEST_DATABASE_DSN \
		-u KODEX_PACKAGE_HUB_TEST_DATABASE_DSN \
		-u KODEX_PROVIDER_HUB_TEST_DATABASE_DSN"; \
	packages="$$(for root in services cmd proto/gen/go libs; do \
		if [ -d "$$root" ]; then go list ./$$root/...; fi; \
	done)"; \
	if [ -z "$$packages" ]; then \
		echo "test-go: no active Go packages"; \
	else \
		$$unit_env go test $$packages; \
	fi; \
	for module in $$(find libs -name go.mod -not -path '*/vendor/*' -exec dirname {} \; 2>/dev/null | sort); do \
		echo "test-go: $$module"; \
		(cd "$$module" && $$unit_env go test ./...); \
	done

test-go-postgres:
	@./scripts/test-go-postgres.sh

test-go-all: test-go test-go-postgres

verify-go: test-go-all

test-go-migrations:
	@migration_files="$$(find services -path '*/cmd/cli/migrations/*.sql' -not -path '*/deprecated/*' -print 2>/dev/null | sort)"; \
	packages="$$(go list ./services/... 2>/dev/null | grep '/cmd/cli/migrations' || true)"; \
	if [ -n "$$migration_files" ] && [ -z "$$packages" ]; then \
		echo "test-go-migrations: active SQL migrations exist, but no migration guard packages were found"; \
		printf '%s\n' "$$migration_files"; \
		exit 1; \
	fi; \
	if [ -z "$$packages" ]; then \
		echo "test-go-migrations: no active migration packages"; \
		exit 0; \
	fi; \
	go test $$packages

fmt-go:
	@files="$$(git ls-files '*.go'; git ls-files --others --exclude-standard '*.go')"; \
	files="$$(printf '%s\n' "$$files" | grep -v '^deprecated/' || true)"; \
	if [ -z "$$files" ]; then \
		echo "fmt-go: no active Go files"; \
		exit 0; \
	fi; \
	printf '%s\n' "$$files" | xargs -r gofmt -w

gen-openapi-go:
	@svc="$${SVC:-services/external/integration-gateway}"; \
	spec="$$svc/api/server/api.yaml"; \
	cfg="tools/codegen/openapi/$$(basename "$$svc").oapi-codegen.yaml"; \
	out="$$svc/internal/transport/http/generated/openapi.gen.go"; \
	case "$$svc" in \
		services/external/integration-gateway) spec="specs/openapi/integration-gateway.v1.yaml" ;; \
		services/staff/staff-gateway) spec="specs/openapi/staff-gateway.v1.yaml" ;; \
		services/external/api-gateway|services/external/telegram-interaction-adapter) ;; \
		*) echo "gen-openapi-go: unsupported SVC=$$svc"; exit 1 ;; \
	esac; \
	test -f "$$spec"; \
	test -f "$$cfg"; \
	mkdir -p "$$(dirname "$$out")"; \
	go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.5.0 -config "$$cfg" "$$spec" > "$$out"

gen-openapi-ts:
	@app="$${APP:-services/staff/web-console}"; \
	if [ "$$app" != "services/staff/web-console" ]; then \
		echo "gen-openapi-ts: unsupported APP=$$app (currently only services/staff/web-console)"; \
		exit 1; \
	fi; \
	npm --prefix "$$app" run gen:openapi

gen-openapi:
	@$(MAKE) gen-openapi-go SVC=services/external/integration-gateway
	@$(MAKE) gen-openapi-go SVC=services/staff/staff-gateway
	@if [ -d services/staff/web-console ]; then $(MAKE) gen-openapi-ts; else echo "gen-openapi-ts: services/staff/web-console is not present"; fi

gen-proto-go:
	@mkdir -p proto/gen/go; \
	protos="$$(find proto -name '*.proto' -print 2>/dev/null | sort)"; \
	if [ -z "$$protos" ]; then \
		echo "gen-proto-go: no proto files"; \
		exit 0; \
	fi; \
	protoc -I proto \
		--go_out=proto/gen/go --go_opt=paths=source_relative \
		--go-grpc_out=proto/gen/go --go-grpc_opt=paths=source_relative \
		$$protos

gen-asyncapi-event-contracts:
	@go run ./cmd/asyncapi-event-contracts

validate-asyncapi:
	@spec="$${SPEC:-}"; \
	if [ -z "$$spec" ]; then \
		svc="$${SVC:-access-manager}"; \
		spec="specs/asyncapi/$$svc.v1.yaml"; \
	fi; \
	test -f "$$spec"; \
	npx --yes @asyncapi/cli validate "$$spec"
