ROOT := github.com/tidbcloud/aws-shared-provider
IMG_PREFIX := aws-shared-provider

# Setting SHELL to bash allows bash commands to be executed by recipes.
# This is a requirement for 'setup-envtest.sh' in the test target.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec

REPO ?= gcr.io/pingcap-public
VERSION ?= "latest"

KUBECONFIG ?= $(HOME)/.kube/config
INFRA_KUBECONFIG ?= $(HOME)/.kube/config

TARGETS ?= manager tidb-gateway gateway-controller datacleaner vpa-agent tidb-worker tidb-http-gateway audit-log ops-service meta-server
KUBECTL ?= kubectl

.PHONY: build

all: build

##@ General

# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk commands is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php

help: ## Display this help.
	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

##@ Development

reviewable: build lint unit-test ## Make a PR ready for review.

lint: golangci-lint generate
	$(GOLANGCI_LINT) run ./...

unit-test: generate ## Run tests.
	mkdir -p coverage
	go test -v `go list ./... |grep -v tests` -coverprofile coverage/ut.out

race-test: generate ## Run race tests.
	go test -race -v `go list ./... |grep -v tests`

GENHTML=genhtml  # part of lcov
coverage: gcov2lcov ## generate coverage html under coverage/ after running tests
	$(GCOV2LCOV) -infile=coverage/ut.out -outfile=coverage/ut.out.lcov
	$(GENHTML) -o coverage/ut_html coverage/ut.out.lcov

e2e: pulumi ginkgo crd
	VAR_IMG_HUB=ecr ./hack/tests/e2e.sh

cleanup-e2e: pulumi
	VAR_IMG_HUB=ecr ./hack/tests/e2e.sh

build-cache: generate
	./hack/build/cache.sh $(TARGETS)

build: generate
	./hack/build/go.sh $(TARGETS)

build-in-docker: generate
	./hack/build/in_docker.sh $(TARGETS)

# NOTE(liubo02): now make push can build and push image
docker-build: build-in-docker
	./hack/image/build.sh $(TARGETS)

docker-push:
	echo "Deprecated: Please use make push to build and push image directly"
	./hack/push-image.sh $(TARGETS)

# TODO(liubo02): add a cmd to build and push to kind
push: build-in-docker
	./hack/image/build.sh --push $(TARGETS)

version: git-semver
	$(GIT_SEMVER)

KUSTOMIZE = $(shell pwd)/bin/kustomize
kustomize: ## Download kustomize locally if necessary.
	$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)

GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint
golangci-lint:
	$(call go-get-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.8.0)

KIND = $(shell pwd)/bin/kind
kind:
	$(call go-get-tool,$(KIND),sigs.k8s.io/kind/cmd/kind@v0.11.1)

PULUMI = $(shell pwd)/bin/pulumi
pulumi:
	BIN=$(PULUMI) ./tools/pulumi/install.sh
	cd ./cd && npm install

GINKGO = $(shell pwd)/bin/ginkgo
ginkgo:
	$(call go-get-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo@v2.21.0)

GCOV2LCOV = $(shell pwd)/bin/gcov2lcov
gcov2lcov:
	$(call go-get-tool, $(GCOV2LCOV),github.com/jandelgado/gcov2lcov@v1.0.5)

GIT_SEMVER = $(shell pwd)/bin/git-semver
git-semver:
	$(call go-get-tool, $(GIT_SEMVER),github.com/mdomke/git-semver/v6@v6.1.1)

CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
controller-gen:
	$(call go-get-tool, $(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.4)

crd: controller-gen ## CustomResourceDefinition objects.
	$(CONTROLLER_GEN) crd:allowDangerousTypes=true paths="./apis/serverless/..." output:crd:artifacts:config=cd/manifests/manager/crd/
	$(CONTROLLER_GEN) crd:allowDangerousTypes=true paths="./apis/gateway/..." output:crd:artifacts:config=cd/manifests/tidb-gateway/crd/

PROTOC = $(shell pwd)/bin/protoc
protoc:
	BIN=$(PROTOC) ./tools/protoc/install.sh

PROTOC_GEN_GO = $(shell pwd)/bin/protoc-gen-go
protoc-gen-go:
	$(call go-get-tool, $(PROTOC_GEN_GO),google.golang.org/protobuf/cmd/protoc-gen-go@v1.30.0)

PROTOC_GEN_GO_GRPC = $(shell pwd)/bin/protoc-gen-go-grpc
protoc-gen-go-grpc:
	$(call go-get-tool, $(PROTOC_GEN_GO_GRPC),google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3.0)

generate: controller-gen protoc protoc-gen-go protoc-gen-go-grpc
	## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
	$(CONTROLLER_GEN) object:headerFile="hack/template/boilerplate.go.txt" paths="./apis/..."
	## Generate code for manager GRPC.
	$(PROTOC) --plugin=protoc-gen-go=$(PROTOC_GEN_GO) --plugin=protoc-gen-go-grpc=$(PROTOC_GEN_GO_GRPC) \
	--go_out=. --go-grpc_out=. cmd/manager/protos/*.proto
	## Generate code for tidb worker scaler service GRPC.
	$(PROTOC) --plugin=protoc-gen-go=$(PROTOC_GEN_GO) --plugin=protoc-gen-go-grpc=$(PROTOC_GEN_GO_GRPC) \
	--go_out=. --go-grpc_out=. pkg/tidbworker/proto/*.proto

# go-get-tool will 'go get' any package $2 and install it to $1.
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
define go-get-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
CGO_ENABLED=0 GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef

fmt-modified: ## Format only modified Go files (compared to HEAD) with better error handling
	@echo "🔍 Finding modified Go files..."
	@modified_files=$$(git diff --name-only --diff-filter=AM HEAD | grep '\.go$$' || true); \
	untracked_files=$$(git ls-files --others --exclude-standard | grep '\.go$$' || true); \
	all_files="$$modified_files $$untracked_files"; \
	if [ -n "$$(echo "$$all_files" | tr ' ' '\n' | grep -v '^$$' | sort -u)" ]; then \
		echo "📁 Files to format:"; \
		echo "$$all_files" | tr ' ' '\n' | grep -v '^$$' | sort -u | sed 's/^/  - /'; \
		echo "📝 Formatting files..."; \
		echo "$$all_files" | tr ' ' '\n' | grep -v '^$$' | sort -u | xargs -I {} gofmt -w -l {}; \
		formatted_count=$$(echo "$$all_files" | tr ' ' '\n' | grep -v '^$$' | sort -u | xargs -I {} gofmt -l {} | wc -l); \
		if [ "$$formatted_count" -gt 0 ]; then \
			echo "✅ Formatted $$formatted_count files:"; \
			echo "$$all_files" | tr ' ' '\n' | grep -v '^$$' | sort -u | xargs -I {} gofmt -l {} | sed 's/^/  - /'; \
		else \
			echo "✨ All files were already properly formatted"; \
		fi; \
	else \
		echo "ℹ️  No modified or untracked Go files found"; \
	fi

fmt-staged: ## Format only staged Go files (git add'd files)
	@echo "🔍 Finding staged Go files..."
	@staged_files=$$(git diff --cached --name-only --diff-filter=AM | grep '\.go$$' || true); \
	if [ -n "$$staged_files" ]; then \
		echo "📁 Staged files to format:"; \
		echo "$$staged_files" | sed 's/^/  - /'; \
		echo "📝 Formatting staged files..."; \
		echo "$$staged_files" | xargs -I {} gofmt -w -l {}; \
		formatted_count=$$(echo "$$staged_files" | xargs -I {} gofmt -l {} | wc -l); \
		if [ "$$formatted_count" -gt 0 ]; then \
			echo "✅ Formatted $$formatted_count staged files:"; \
			echo "$$staged_files" | xargs -I {} gofmt -l {} | sed 's/^/  - /'; \
			echo "🔄 Please re-stage the formatted files with: git add -u"; \
		else \
			echo "✨ All staged files were already properly formatted"; \
		fi; \
	else \
		echo "ℹ️  No staged Go files found"; \
	fi

fmt-all: ## Format all Go files in the project
	@echo "📝 Formatting all Go files..."
	@find . -name '*.go' -not -path './vendor/*' -not -path './.git/*' -not -path './bin/*' | xargs gofmt -w -l
	@echo "✅ All Go files formatted"

setup-hooks: ## Install Git hooks for automatic Go formatting
	@./hack/setup-git-hooks.sh

fmt-check: ## Check if Go files need formatting (CI-friendly)
	@echo "🔍 Checking Go code formatting..."
	@unformatted=$$(find . -name '*.go' -not -path './vendor/*' -not -path './.git/*' -not -path './bin/*' | xargs gofmt -l); \
	if [ -n "$$unformatted" ]; then \
		echo "❌ Unformatted Go files found:"; \
		echo "$$unformatted" | sed 's/^/  - /'; \
		echo ""; \
		echo "📝 Run 'make fmt-all' to fix formatting"; \
		exit 1; \
	else \
		echo "✅ All Go files are properly formatted"; \
	fi
