# Copyright 2022 PingCAP, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

BINARY_DIR = ${CURDIR}/build/bin
TIDBCLOUD_REGIONAL_SERVER_BINARY = ${BINARY_DIR}/tidbcloud-regional-server
INSTALL_PREFIX = ${PREFIX}/regional-server
GENERATE_TARGET_DIR = $(CURDIR)/proto
COMMIT := $(shell git rev-parse --short HEAD)
IMG ?= $(REGISTRY)/tidbcloud-regional-server:$(COMMIT)

include Makefile.common

.PHONY: all clean test gotest gotool help build
all: build

# 1. build binary
build: regional_server
	@#echo "build regional server start."
	@#make regional_server
	@#@echo "build regional successfully."

docker-build: build
	mkdir -p helm-charts
	cp -r ../helm-charts helm-charts
	docker build --platform=linux/amd64 -q -f Dockerfile -t ${IMG} .
	rm -rf helm-charts

docker-build-ft: build-ft
	docker build --platform=linux/amd64 -q -f Dockerfile.ft -t ${IMG} .

.PHONY: docker-push
docker-push: ## Push docker image with the manager.
	docker push ${IMG}

regional_server:
	@echo "build regional-server start."
	$(GOBUILD) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o ${TIDBCLOUD_REGIONAL_SERVER_BINARY} main.go
	@echo "build regional-server successfully."

#2. R&D to test the code themselves for compliance before submitting it
devselfcheck:
	#cat resource/prchecklist.md
	#make gotool
	@echo "start self check."
	make check_fmt
	make check_goword
	make check_static
	make check_unconvert
	make check_lint
	make check_vet
	make test
	@echo "self check complete."

gotool:
	@echo "build compilation toolchain start."
	make build_revive
	make build_goword
	make build_unconvert
	make build_failpoint_ctl
	make build_errdoc_gen
	make build_golangci_lint
	make build_vfsgendev
	@echo "build compilation toolchain successfully."

#Get and compile the tools required in the project
build_revive: build/go.mod
	cd build; \
	$(GO) build -o ${BINARY_DIR}/revive github.com/mgechev/revive

build_goword: build/go.mod
	cd build; \
	$(GO) build -o ${BINARY_DIR}/goword github.com/chzchzchz/goword

build_unconvert: build/go.mod
	cd build; \
	$(GO) build -o ${BINARY_DIR}/unconvert github.com/mdempsky/unconvert

build_failpoint_ctl: build/go.mod
	cd build; \
	$(GO) build -o ${BINARY_DIR}/failpoint-ctl github.com/pingcap/failpoint/failpoint-ctl

build_errdoc_gen: build/go.mod
	cd build; \
	$(GO) build -o ${BINARY_DIR}/errdoc-gen github.com/pingcap/errors/errdoc-gen

build_golangci_lint:
	curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b ${BINARY_DIR} v1.62.0

build_vfsgendev: build/go.mod
	cd build; \
	$(GO) build -o ${BINARY_DIR}/vfsgendev github.com/shurcooL/vfsgen/cmd/vfsgendev

build_megacheck: build/go.mod
	cd build; \
	$(GO) build -o ${BINARY_DIR}/megacheck honnef.co/go/build/cmd/megacheck

check_fmt:
	@echo "gofmt (simplify)"
	#@gofmt -s -l -w $(FILES) 2>&1 | $(FAIL_ON_STDOUT)

check_goword:
	@echo "goword check, files: ${FILES}"
	#${BINARY_DIR}/goword $(FILES) 2>&1 | $(FAIL_ON_STDOUT)

check_static:
	@echo "code static check, files: $($(PACKAGE_LIST))"
	#${BINARY_DIR}/golangci-lint run -v $$($(PACKAGE_DIRECTORIES))

check_unconvert:
	@echo "unconvert check, files: $($(PACKAGE_LIST))"
	#@GO111MODULE=on ${BINARY_DIR}/unconvert $(UNCONVERT_PACKAGES)

check_lint:
	@echo "linting check"
	#@${BINARY_DIR}/revive -formatter friendly -config build_helper/revive.toml $(FILES_WITHOUT_BR)

check_vet:
	@echo "vet check"
	#$(GO) vet -all $(PACKAGES_WITHOUT_BR) 2>&1 | $(FAIL_ON_STDOUT)

install:
	mkdir -p ${INSTALL_PREFIX}
	mkdir -p ${INSTALL_PREFIX}/bin
	mkdir -p ${INSTALL_PREFIX}/etc
	mkdir -p ${INSTALL_PREFIX}/logs
	mkdir -p ${INSTALL_PREFIX}/scripts
	mkdir -p ${INSTALL_PREFIX}/docs
	cp ${TIDBCLOUD_REGIONAL_SERVER_BINARY} ${INSTALL_PREFIX}/bin

uninstall:
	@echo "uninstall: remove all files in $(INSTALL_PREFIX)"
	@if [ -d ${INSTALL_PREFIX} ] ; then rm ${INSTALL_PREFIX} ; fi

clean:
	@if [ -f ${TIDBCLOUD_REGIONAL_SERVER_BINARY} ] ; then rm ${TIDBCLOUD_REGIONAL_SERVER_BINARY} ; fi
	@if [ -f ${MOCK_METRICS_SERVER_BINARY} ] ; then rm ${MOCK_METRICS_SERVER_BINARY} ; fi
	@if [ -f ${BINARY_DIR}/revive ] ; then rm ${BINARY_DIR}/revive ; fi
	@if [ -f ${BINARY_DIR}/goword ] ; then rm ${BINARY_DIR}/goword ; fi
	@if [ -f ${BINARY_DIR}/unconvert ] ; then rm ${BINARY_DIR}/unconvert ; fi
	@if [ -f ${BINARY_DIR}/failpoint-ctl ] ; then rm ${BINARY_DIR}/failpoint-ctl; fi
	@if [ -f ${BINARY_DIR}/vfsgendev ] ; then rm ${BINARY_DIR}/vfsgendev; fi
	@if [ -f ${BINARY_DIR}/golangci-lint ] ; then rm ${BINARY_DIR}/golangci-lint; fi
	@if [ -f ${BINARY_DIR}/errdoc-gen ] ; then rm ${BINARY_DIR}/errdoc-gen; fi
	##@if [ -d ${GENERATE_TARGET_DIR}/clusterservices ] ; then rm -rf ${GENERATE_TARGET_DIR}/clusterservices; fi
	@if [ -d ${CURDIR}/test ] ; then rm -rf ${CURDIR}/test; fi

help:
	@echo "make build, build binary for all servers"
	@echo "make install, install binary to target "
	@echo "make uninstall, uninstall binary from target "
	@echo "make devselfcheck, dev commit code prechecked."
	@echo "make test, test all case"
	@echo "make upload_coverage, upload coverage information"
	@echo "make reviewable before push code to github"

upload_coverage:
#	GO111MODULE=off go get github.com/wadey/gocovmerge
#	gocovmerge "$(TEST_DIR)"/cov.* | grep -vE ".*.pb.go" > "$(TEST_DIR)/all_cov.out"
	grep -vE ".*.pb.go" "$(TEST_DIR)/cov.unit.out" > "$(TEST_DIR)/unit_cov.out"
ifeq ("$(JenkinsCI)", "1")
#	GO111MODULE=off go get github.com/mattn/goveralls
#	@goveralls -coverprofile=$(TEST_DIR)/all_cov.out -service=jenkins-ci -repotoken $(COVERALLS_TOKEN)
	curl -s https://codecov.io/bash > $(CODECOV_BASH)
	bash $(CODECOV_BASH) -f $(TEST_DIR)/unit_cov.out -t $(CODECOV_TOKEN)
else
	go tool cover -html "$(TEST_DIR)/all_cov.out" -o "$(TEST_DIR)/all_cov.html"
	go tool cover -html "$(TEST_DIR)/unit_cov.out" -o "$(TEST_DIR)/unit_cov.html"
	go tool cover -func="$(TEST_DIR)/unit_cov.out"
endif

# don't run it locally, only for CI, use `make test` instead
ci_unit_test:
	$(GO) install github.com/jstemmer/go-junit-report@latest
	$(GO) install gotest.tools/gotestsum@latest
	$(GO) install github.com/boumenot/gocover-cobertura@latest
	@-(gotestsum --junitfile ./unit-tests.xml -- -p 3 -coverprofile=./cover.out ${PACKAGES})
	gocover-cobertura < cover.out > coverage.xml

ci_unit_test_without_coverage:
	go test -p 3 ${PACKAGES}

test: build lint
	mkdir -p "$(TEST_DIR)"
	$(GO) install gotest.tools/gotestsum@latest
	@-(gotestsum --junitfile "$(TEST_DIR)/unit-tests.xml" --packages=${PACKAGES})
	-go test -v ${PACKAGES} -coverprofile="$(TEST_DIR)/cover.out"
	go tool cover -html "$(TEST_DIR)/cover.out" -o "$(TEST_DIR)/cover.html"
	@echo "check ut results in $(TEST_DIR)/unit-tests.xml"
	@echo "check ut coverage by opening $(TEST_DIR)/cover.html using browser"

build-ft:
	go test -c -tags=intergration -o ${BINARY_DIR}/ft.test ./test/ft/...

#race: failpoint-enable
#   @export log_level=debug; \
#	$(GOTEST) -timeout 20m -race $(PACKAGES) || { $(FAILPOINT_DISABLE); exit 1; }
#	@$(FAILPOINT_DISABLE)
#
#leak: failpoint-enable
#	@export log_level=debug; \
#	$(GOTEST) -tags leak $(PACKAGES) || { $(FAILPOINT_DISABLE); exit 1; }
#	@$(FAILPOINT_DISABLE)
#

failpoint-enable: build_failpoint_ctl
# Converting gofail failpoints...
	@$(FAILPOINT_ENABLE)

failpoint-disable: build_failpoint_ctl
# Restoring gofail failpoints...
	@$(FAILPOINT_DISABLE)

lint: mock
	$(GO) install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.9.0
	golangci-lint run --fix -v $$($(LINT_PACKAGES)) --config .golangci.yml
	git diff --exit-code

# don't run it locally, only for CI, use `make gosec` instead
ci_gosec:
	$(GO) install github.com/securego/gosec/v2/cmd/gosec@latest
	gosec -fmt=junit-xml -out=gosec-results.xml -stdout -verbose=text -exclude-dir="agent-control-server/*|data-collector/*|vm-bench/*" -exclude=G103,G104,G204,G304,G307,G401,G404,G501,G505,G601 ./...

gosec:
	mkdir -p "$(TEST_DIR)"
	$(GO) install github.com/securego/gosec/v2/cmd/gosec@latest
	gosec -fmt=junit-xml -out="$(TEST_DIR)/gosec-results.xml" -stdout -verbose=text -exclude-dir="agent-control-server/*|data-collector/*|vm-bench/*" -exclude=G103,G104,G204,G304,G307,G401,G404,G501,G505,G601 ./...
	echo "check gosec result by opening $(TEST_DIR)/gosec-results.xml"

cyclo:
	$(GO) install github.com/fzipp/gocyclo/cmd/gocyclo@latest
	gocyclo -over 20 -ignore "_test|agent-control-server/replaced/|test/" ./

add_test_file:
	build/ci/add_test_file.sh

mock:
	$(GO) install go.uber.org/mock/mockgen@v0.4.0
	mockgen -destination ./test/resourcemanager/mockvpc/mock_vpc.go -package mockvpc -source ./pkg/resourcemanager/vpc/vpc.go
	mockgen -destination ./test/resourcemanager/mockappbox/mock_appbox.go -package mockappbox -source ./pkg/resourcemanager/appbox/appbox.go
	mockgen -destination ./test/resourcemanager/mockapproute/mock_approute.go -package mockapproute -source ./pkg/resourcemanager/approute/approute.go
	mockgen -destination ./test/resourcemanager/mocko11yroute/mock_o11yroute.go -package mocko11yroute -source ./pkg/resourcemanager/o11yroute/o11yroute.go
	mockgen -destination ./test/resourcemanager/mockinternet/mock_internet.go -package mockinternet -source ./pkg/resourcemanager/internet/internet.go
	mockgen -destination ./test/resourcemanager/mockaccount/mock_account.go -package mockaccount -source ./pkg/resourcemanager/account/account.go
	mockgen -destination ./test/resourcemanager/mockaccount/mock_sharedbucket.go -package mockaccount -source ./pkg/resourcemanager/account/shared_bucket.go
	mockgen -destination ./test/resourcemanager/mockvpcpeering/mock_vpcpeering.go -package mockvpcpeering -source ./pkg/resourcemanager/vpcpeering/vpcpeering.go
	mockgen -destination ./test/resourcemanager/mockprivatelinkservice/mock_privatelinkservice.go -package mockprivatelinkservice -source ./pkg/resourcemanager/privatelinkservice/privatelinkservice.go
	mockgen -destination ./test/service/mockaccount/mock_account.go -package mockaccount -source ./pkg/service/account/accountservice.go
	mockgen -destination ./test/service/mockaccount/mock_sharedbucket.go -package mockaccount -source ./pkg/service/account/shared_bucket.go
	mockgen -destination ./test/kubemock/kubeclient.go -package kubemock sigs.k8s.io/controller-runtime/pkg/client Client,Reader
	mockgen -destination ./test/service/mockclustercli/client.go -package mockclustercli -source ./pkg/common/clustercli/interface.go
	mockgen -destination ./test/mockazure/aks_client.go -package mockazure -source ./pkg/common/azure/aks_client.go
	mockgen -destination ./test/mockazure/privatelinkservice_client.go -package mockazure -source ./pkg/common/azure/privatelinkservice_client.go
	mockgen -destination ./test/service/mockec2client/client.go -package mockec2client -source ./pkg/common/ec2client/interface.go

swag:
	$(GO) install github.com/swaggo/swag/cmd/swag@v1.16.2
	swag init -g main.go -o api/swagger

reviewable: generate manifests test
	go mod tidy
	git diff --exit-code

##@ ResourceWorker Development

.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
	$(CONTROLLER_GEN) rbac:roleName=dedicated-regional-server-manager-role crd webhook paths="./..." output:crd:artifacts:config=deployments/crd/bases output:rbac:artifacts:config=deployments/rbac

.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
	$(CONTROLLER_GEN)  object:headerFile="hack/boilerplate.go.txt" paths="./..."
##@ ResourceWorker Build Dependencies
.PHONY: install_crd
install_crd: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
	$(KUSTOMIZE) build deployments/crd | kubectl apply -f -

.PHONY: uninstall_crd
uninstall_crd: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
	$(KUSTOMIZE) build deployments/crd  | kubectl delete --ignore-not-found=$(ignore-not-found) -f -

## Location to install dependencies to
LOCALBIN ?= $(shell pwd)/bin
$(LOCALBIN):
	mkdir -p $(LOCALBIN)

## Tool Binaries
KUBECTL ?= kubectl
KUSTOMIZE ?= $(LOCALBIN)/kustomize
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest

## Tool Versions
KUSTOMIZE_VERSION ?= v5.2.1
CONTROLLER_TOOLS_VERSION ?= v0.17.2

.PHONY: kustomize
kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading.
$(KUSTOMIZE): $(LOCALBIN)
	@if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \
		echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \
		rm -rf $(LOCALBIN)/kustomize; \
	fi
	test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION)

.PHONY: controller-gen
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten.
$(CONTROLLER_GEN): $(LOCALBIN)
	test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \
	GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)

.PHONY: envtest
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
$(ENVTEST): $(LOCALBIN)
	GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest

.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
	cd deployments/manager/$(PROVIDER) && $(KUSTOMIZE) edit set image controller=${IMG}
	$(KUSTOMIZE) build deployments/manager/$(PROVIDER) | $(KUBECTL) -n regional-server-system apply -f -

GINKGO = $(LOCALBIN)/ginkgo
GINKGO_VERSION ?= v2.13.2
FT_CONFIG ?= $(shell pwd)/test/ft/ft-config.yaml
FT_NODES ?= 1

.PHONY: ginkgo
ginkgo: $(GINKGO) ## Download ginkgo locally if necessary.
$(GINKGO): $(LOCALBIN)
	test -s $(LOCALBIN)/ginkgo && $(LOCALBIN)/ginkgo version | grep -q $(GINKGO_VERSION) || \
	GOBIN=$(LOCALBIN) go install github.com/onsi/ginkgo/v2/ginkgo@$(GINKGO_VERSION)

ft: ginkgo
	$(GINKGO) --tags intergration --poll-progress-after=3000s --timeout=2h --nodes=$(FT_NODES) --focus=$(FT_FOCUS) --label-filter=$(FT_LABELS) --skip=$(FT_SKIP) -v ./test/ft/... -- --ft-config=$(FT_CONFIG)
