GOPRIVATE:="github.com/tidbcloud/*"
MAKEFILE_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
BUF:=go run github.com/bufbuild/buf/cmd/buf@v1

_COMMIT := $(shell git describe --no-match --always --dirty)
COMMIT  := $(if $(COMMIT),$(COMMIT),$(_COMMIT))

CONTROLLER_GEN:= go run sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.5
IMG ?= $(REGISTRY)/cluster-service-ng:$(COMMIT)
AUTOSCALER_IMG ?= $(REGISTRY)/ng-autoscaler:$(COMMIT)
AUTOSCALER_ENGINE_IMG ?= $(REGISTRY)/ng-autoscaler-engine:$(COMMIT)
EXECUTOR_IMG ?= $(REGISTRY)/ng-executor:$(COMMIT)
TASKCLEANUP_IMG ?= $(REGISTRY)/ng-taskcleanup:$(COMMIT)

LOCAL_BIN = $(CURDIR)/bin

MOCKERY_VERSION = v3.4.0
MOCKERY_BIN = $(LOCAL_BIN)/mockery

GOLANGCI_LINT_VERSION = v2.5.0
GOLANGCI_LINT_BIN := $(LOCAL_BIN)/golangci-lint

GOTESTSUM_VERSION = v1.13.0
GOTESTSUM_BIN = $(LOCAL_BIN)/gotestsum

GITLEAKS_VERSION = v8.21.0
GITLEAKS_BIN = $(LOCAL_BIN)/gitleaks

default: dev

.PHONY: dev
dev: mod fmt manifests proto build test

.PHONY: mod
mod:
	GOPRIVATE=$(GOPRIVATE) go mod tidy
	GOPRIVATE=$(GOPRIVATE) go mod download
	cd ./api && \
		GOPRIVATE=$(GOPRIVATE) go mod tidy && \
		GOPRIVATE=$(GOPRIVATE) go mod download

.PHONY: install-lint
install-lint:
	@echo "Checking for golangci-lint..."
	@if [ ! -x "$(GOLANGCI_LINT_BIN)" ]; then \
		echo "Installing golangci-lint $(GOLANGCI_LINT_VERSION) to $(LOCAL_BIN)..."; \
		GOBIN=$(LOCAL_BIN) go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION); \
	else \
		echo "golangci-lint already installed at $(GOLANGCI_LINT_BIN)"; \
	fi

.PHONY: install-mockery
install-mockery:
	@echo "Checking for mockery..."
	@if [ ! -x "$(MOCKERY_BIN)" ]; then \
		echo "Installing mockery $(MOCKERY_VERSION) to $(MOCKERY_BIN)..."; \
		GOBIN=$(LOCAL_BIN) go install github.com/vektra/mockery/v3@$(MOCKERY_VERSION); \
	else \
		echo "mockery already installed at $(MOCKERY_BIN)"; \
	fi

.PHONY: install-test
install-test:
	@echo "Checking for gotestsum..."
	@if [ ! -x "$(GOTESTSUM_BIN)" ]; then \
		echo "Installing gotestsum @$(GOTESTSUM_VERSION) to $(LOCAL_BIN)..."; \
		GOBIN=$(LOCAL_BIN) go install gotest.tools/gotestsum@$(GOTESTSUM_VERSION); \
	else \
		echo "gotestsum already installed at $(GOTESTSUM_BIN)"; \
	fi

.PHONY: install-gitleaks
install-gitleaks:
	@echo "Checking for gitleaks..."
	@if [ ! -x "$(GITLEAKS_BIN)" ]; then \
		echo "Installing gitleaks $(GITLEAKS_VERSION) to $(LOCAL_BIN)..."; \
		GOBIN=$(LOCAL_BIN) go install github.com/zricethezav/gitleaks/v8@$(GITLEAKS_VERSION); \
	else \
		echo "gitleaks already installed at $(GITLEAKS_BIN)"; \
	fi

.PHONY: fmt
fmt: install-lint
	GOPRIVATE=$(GOPRIVATE) $(GOLANGCI_LINT_BIN) run --config $(MAKEFILE_DIR)/.golangci.yml --fix

.PHONY: lint
lint: install-lint
	GOPRIVATE=$(GOPRIVATE) $(GOLANGCI_LINT_BIN) run --config $(MAKEFILE_DIR)/.golangci.yml --timeout 15m

.PHONY: lint-fix
lint-fix: install-lint
	GOPRIVATE=$(GOPRIVATE) $(GOLANGCI_LINT_BIN) run --config $(MAKEFILE_DIR)/.golangci.yml --fix

# build will build all binary under cmd.
.PHONY: build
build:
	mkdir -p $(MAKEFILE_DIR)/./bin
	CGO_ENABLED=0 GOPRIVATE=$(GOPRIVATE) go build -o $(MAKEFILE_DIR)/./bin ./...

.PHONY: build-server
build-server:
	mkdir -p $(MAKEFILE_DIR)/./bin
	CGO_ENABLED=0 GOPRIVATE=$(GOPRIVATE) go build -o $(MAKEFILE_DIR)/./bin/cluster_ng_server ./cmd/cluster_ng_server/main.go

.PHONY: build-executor
build-executor:
	mkdir -p $(MAKEFILE_DIR)/./bin
	CGO_ENABLED=0 GOPRIVATE=$(GOPRIVATE) go build -o $(MAKEFILE_DIR)/./bin/executor ./cmd/executor/main.go

.PHONY: build-autoscale_stub
build-autoscale_stub:
	mkdir -p $(MAKEFILE_DIR)/./bin
	CGO_ENABLED=0 GOPRIVATE=$(GOPRIVATE) go build -o $(MAKEFILE_DIR)/./bin/autoscale_stub ./cmd/autoscale_stub/main.go

.PHONY: build-autoscale_mock
build-autoscale_mock:
	mkdir -p $(MAKEFILE_DIR)/./bin
	CGO_ENABLED=0 GOPRIVATE=$(GOPRIVATE) go build -o $(MAKEFILE_DIR)/./bin/autoscale_mock ./cmd/autoscale_mock/main.go

.PHONY: build-autoscale_engine
build-autoscale_engine:
	mkdir -p $(MAKEFILE_DIR)/./bin
	CGO_ENABLED=0 GOPRIVATE=$(GOPRIVATE) go build -o $(MAKEFILE_DIR)/./bin/autoscale_engine ./cmd/autoscale_engine/main.go

.PHONY: test
# CGO_ENABLED=1 is required for -race. Some self-hosted runner images set
# CGO_ENABLED=0 in `go env -w`, which makes every test run fail before any test
# executes; the inline env var overrides that file.
test: install-test
	CGO_ENABLED=1 GOPRIVATE=$(GOPRIVATE) $(GOTESTSUM_BIN) --format=testname -- -count=1 -p=4 -race -failfast -shuffle=on -coverprofile=$(MAKEFILE_DIR)/./coverage.pipeline.txt ./pkg/... ./domain/...

.PHONY: browse-coverage
browse-coverage:
	@go tool cover -html=coverage.pipeline.txt -o coverage.html
	@grep -v mocks coverage.html > coverage.filtered.html && mv coverage.filtered.html coverage.html
	@echo "Opening coverage report in browser..."
	@open coverage.html

.PHONY: gitleaks
gitleaks: install-gitleaks
	$(GITLEAKS_BIN) detect --source . -v --redact

.PHONY: generate
generate: proto manifests

.PHONY: proto
proto: lint-proto format-proto generate-proto

.PHONY: generate-proto
generate-proto:
	cd api && $(BUF) dep update && $(BUF) --exclude-path tidbcloud/ generate

.PHONY: format-proto
format-proto:
	cd api && $(BUF) --exclude-path tidbcloud/ format -w

.PHONY: lint-proto
lint-proto:
	cd api && $(BUF) --exclude-path tidbcloud/ lint

.PHONY: manifests
manifests:  ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
	$(CONTROLLER_GEN) rbac:roleName=cluster-service-next-gen-role crd:crdVersions=v1 paths="$(MAKEFILE_DIR)/pkg/storage/etcd/schema/..." output:crd:artifacts:config=$(MAKEFILE_DIR)/deployments/crds output:rbac:artifacts:config=$(MAKEFILE_DIR)/deployments/rbac && $(CONTROLLER_GEN) object paths="$(MAKEFILE_DIR)/pkg/storage/etcd/schema/..."

.PHONY: docker-build
docker-build: build
	docker build --platform=linux/amd64 -q -f Dockerfile -t $(IMG) .

.PHONY: docker-build-ng-autoscaler
docker-build-ng-autoscaler: build-autoscale_stub
	docker build --platform=linux/amd64 -q -f Dockerfile.autoscaler -t $(AUTOSCALER_IMG) .

.PHONY: docker-build-autoscaler-engine
docker-build-autoscaler-engine: build-autoscale_engine
	docker build --platform=linux/amd64 -q -f Dockerfile.autoscaler_engine -t $(AUTOSCALER_ENGINE_IMG) .

.PHONY: docker-build-executor
docker-build-executor: build-executor
	docker build --platform=linux/amd64 -q -f Dockerfile.executor -t $(EXECUTOR_IMG) .

.PHONY: docker-build-taskcleanup
docker-build-taskcleanup:
	docker build --platform=linux/amd64 -q -f Dockerfile.taskcleanup -t $(TASKCLEANUP_IMG) .

.PHONY: docker-push
docker-push: ## Push docker image with the manager.
	docker push $(IMG)

.PHONY: docker-safe-push
docker-safe-push:
	@echo "Checking if $(IMG) exists..."
	@if docker manifest inspect $(IMG) > /dev/null 2>&1; then \
		echo "Image $(IMG) already exists, skipping push"; \
	else \
		echo "Pushing $(IMG)..."; \
		docker push $(IMG); \
	fi

.PHONY: generate-mocks
generate-mocks: install-mockery
	$(MOCKERY_BIN) --config=.mockery.yml

.PHONY: sync-op-v2-crd
sync-op-v2-crd:
	./.github/scripts/sync-tidb-operator-v2-crd.sh

.PHONY: auto-update-cluster-agent
auto-update-cluster-agent:
	sh ./.github/scripts/auto-update-cluster-agent.sh

.PHONY: auto-update-audit-ng
auto-update-audit-ng:
	sh ./.github/scripts/auto-update-audit-ng.sh
