.PHONY: build build-dataflow-amd64 \
	check check-idl check-golang \
	lint lint-golang \
	fmt fmt-golang \
	test unit-test ci-unit-test tidy \
	dm-unit-test imp-unit-test agent-unit-test impdm-unit-test \
	generate generate-idl generate-import-into-ui-swagger generate-entity \
	generate-coverage browse-coverage \
	gapi-lint

GO     := GO111MODULE=on go
GOTEST := CGO_ENABLED=1 $(GO) test -p 3 --race
GOBUILD := CGO_ENABLED=0 $(GO) build
CGOBUILD := CGO_ENABLED=1 $(GO) build
GOCOVER := $(GO) tool cover
LDFLAGS += -X "github.com/tidbcloud/dataflow-service/pkg/version.ReleaseVersion=$(shell git describe --tags --dirty --always)"
LDFLAGS += -X "github.com/tidbcloud/dataflow-service/pkg/version.BuildTS=$(shell date -u '+%Y-%m-%d %H:%M:%S')"
LDFLAGS += -X "github.com/tidbcloud/dataflow-service/pkg/version.GitHash=$(shell git rev-parse HEAD)"
LDFLAGS += -X "github.com/tidbcloud/dataflow-service/pkg/version.GitBranch=$(shell git rev-parse --abbrev-ref HEAD)"
TEST_ARTIFACTS_DIR := /tmp/dataflow-service/test-artifacts
TEST_ARTIFACTS_OUT := $(TEST_ARTIFACTS_DIR)/cov.unit_test.out
TEST_ARTIFACTS_FILE_COVER := $(TEST_ARTIFACTS_DIR)/file_coverage.html
TEST_OPTIONS := -timeout 5m -cover -covermode=atomic -coverprofile="$(TEST_ARTIFACTS_OUT)"
FILES := $$(find . -name '*.go' -type f | grep -vE 'mock|vendor|generate|kv_gen|proto|pb\.go|pb\.gw\.go|test\.go')
GENQS := PATH="../../../tools/bin:$(PATH)" go generate entity.go

FAIL_ON_STDOUT := awk '{ print } END { if (NR > 0) { exit 1  }  }'

UNAME := $(shell uname)
BROWSER_OPEN := "unknown"
ifeq ($(UNAME),Darwin)
    BROWSER_OPEN := open -a Safari
endif
ifeq ($(UNAME),Linux)
    BROWSER_OPEN := xdg-open
endif

failpoint-enable: tools/bin/failpoint-ctl
	tools/bin/failpoint-ctl enable

failpoint-disable: tools/bin/failpoint-ctl
	tools/bin/failpoint-ctl disable

build: build-dataflow build-dataflow-agent

build-darwin: build-dataflow-darwin

build-dataflow:
	$(GOBUILD) -ldflags '$(LDFLAGS)' -o dist/dataflow cmd/dataflow/main.go

build-dataflow-personal:
	$(GOBUILD) -gcflags "all=-N -l" -ldflags '$(LDFLAGS)' -o dist/dataflow cmd/dataflow/main.go

build-dataflow-amd64:
	GOARCH=amd64 GOOS=linux $(GOBUILD) -ldflags '$(LDFLAGS)' -o dist/dataflow-amd64 cmd/dataflow/main.go

# there are cgo codes in go files of darwin
build-dataflow-darwin:
	$(CGOBUILD) -ldflags '$(LDFLAGS)' -o dist/dataflow cmd/dataflow/main.go

build-dataflow-amd64-personal:
	GOARCH=amd64 GOOS=linux $(GOBUILD) -gcflags "all=-N -l" -ldflags '$(LDFLAGS)' -o dist/dataflow-amd64 cmd/dataflow/main.go

build-dataflow-agent:
	$(GOBUILD) -ldflags '$(LDFLAGS)' -o dist/dataflow-agent cmd/agent/main.go

build-dataflow-agent-amd64:
	GOARCH=amd64 GOOS=linux $(GOBUILD) -ldflags '$(LDFLAGS)' -o dist/dataflow-agent-amd64 cmd/agent/main.go

# there are cgo codes in go files of darwin
build-dataflow-agent-darwin:
	$(CGOBUILD) -ldflags '$(LDFLAGS)' -o dist/dataflow-agent cmd/agent/main.go

generate: generate-idl generate-entity

generate-idl: tools/bin/buf tools/bin/protoc-gen-validate tools/bin/swagger
	cd idl/idl && PATH='$(shell pwd)/tools/bin:$(PATH)' buf generate -v --path ./agent/agent --template ./dedicated/dedicated_template.yaml
	cd idl/idl && PATH='$(shell pwd)/tools/bin:$(PATH)' buf generate -v --exclude-path ./dedicated/dedicated
	cd idl/idl && PATH='$(shell pwd)/tools/bin:$(PATH)' buf generate -v --path ./dedicated/dedicated --template ./dedicated/dedicated_template.yaml
	cd idl/idl && PATH='$(shell pwd)/tools/bin:$(PATH)' buf generate -v --path ./dataflow/dataflow --template ./dataflow/dataflow_template.yaml
	cd idl/idl && PATH='$(shell pwd)/tools/bin:$(PATH)' buf generate -v --path ./dataflow/replay --template ./dataflow/dataflow_template.yaml
	cd idl/idl && PATH='$(shell pwd)/tools/bin:$(PATH)' buf generate -v --path ./dataflow/adminapi/v1beta1/import.proto --template ./dataflow/adminapi/dataflow_v1beta1.yaml
	cd idl/idl && PATH='$(shell pwd)/tools/bin:$(PATH)' buf generate -v --path ./dataflow/adminapi/v1beta1/backup.proto --template ./dataflow/adminapi/dataflow_v1beta1.yaml
	cd idl/idl && PATH='$(shell pwd)/tools/bin:$(PATH)' buf generate -v --path ./dataflow/openapi/v1beta1/changefeed.proto --template ./dataflow/openapi/changefeed_v1beta1.yaml
	mv idl/pbgen/proto/dataflow/service.swagger.json api/
	mv idl/pbgen/proto/agent/agent.swagger.json api/
	mv idl/pbgen/proto/dataflow/dataflow.swagger.json api/
	#mv idl/pbgen/proto/dm/dm.swagger.json api/
	mv idl/pbgen/proto/dataflow/backup.swagger.json api/
	mv idl/pbgen/proto/dataflow/crrf.swagger.json api/
	mv idl/pbgen/proto/replay/replay.swagger.json api/
	mv idl/pbgen/proto/openapi/v1beta1/changefeed.swagger.json api/changefeed.swagger.internal.json
	cp api/changefeed.swagger.internal.json api/changefeed.swagger.json
	# Sanitize the public OpenAPI spec: the TiCDC cluster metadata API is internal-only and must not be advertised in the public spec (the endpoint stays live; the internal spec keeps documenting it)
	jq -f tools/scripts/sanitize_changefeed_swagger.jq api/changefeed.swagger.json > api/changefeed.swagger.json.tmp && mv api/changefeed.swagger.json.tmp api/changefeed.swagger.json
	mv idl/pbgen/proto/adminapi/v1beta2/import_into.swagger.json api/
	make generate-import-into-ui-swagger
	tools/bin/swagger validate api/service.swagger.json
	tools/bin/swagger validate api/agent.swagger.json
	tools/bin/swagger validate api/replay.swagger.json
	tools/bin/swagger validate api/import_into.ui.swagger.json
	tools/bin/swagger validate api/changefeed.swagger.json
	tools/bin/swagger validate api/changefeed.swagger.internal.json

generate-import-into-ui-swagger:
	bash ./tools/scripts/gen_ui_swagger.sh api/import_into.swagger.json api/import_into.ui.swagger.json /api/v2025/cloud

generate-entity: internal/db/entity/autogenerated_entity.go app/internal/repo/entity/autogenerated_entity.go

generate4test: tools/bin/mockery
	@(bash ./tools/scripts/gen_mock.sh)

internal/db/entity/autogenerated_entity.go: internal/db/entity/entity.go tools/bin/goqueryset
	cd internal/db/entity && $(GENQS)

app/internal/repo/entity/autogenerated_entity.go: app/repo/entity/entity.go tools/bin/goqueryset
	cd app/repo/entity && PATH="../../../../tools/bin:$(PATH)" go generate entity.go

check: lint fmt
	@git --no-pager diff --exit-code | $(FAIL_ON_STDOUT)

check-go-in-ci: lint-idl lint-golang fmt-idl fmt-golang
	@git --no-pager diff --exit-code 2>&1 | $(FAIL_ON_STDOUT)

lint: lint-idl lint-golang

lint-golang: tools/bin/golangci-lint
	cd app && ../tools/bin/golangci-lint run

lint-idl: tools/bin/buf
	cd idl/idl && ../../tools/bin/buf lint

fmt: fmt-idl fmt-golang fmt-sh

fmt-golang: tools/bin/golangci-lint tools/bin/gofumpt
	@echo "golangci-lint run --fix"
	cd app && ../tools/bin/golangci-lint run --fix --timeout 10m0s
	@echo "gofmt (simplify)"
	cd app && ../tools/bin/gofumpt -l -w $(FILES) 2>&1 | $(FAIL_ON_STDOUT)

fmt-idl: tools/bin/buf
	tools/bin/buf format -w --exit-code idl/idl/

fmt-sh: tools/bin/shfmt
	tools/bin/shfmt -d -w .

test: unit-test

ci-unit-test: tools/bin/gotestsum tools/bin/failpoint-ctl
	mkdir -p "$(TEST_ARTIFACTS_DIR)"
	make failpoint-enable
	CGO_ENABLED=1 tools/bin/gotestsum --junitfile "$(TEST_ARTIFACTS_DIR)/junit-report.xml" -- \
	-v $(TEST_OPTIONS) ./... \
	|| { make failpoint-disable ; exit 1; }
	make failpoint-disable

unit-test: tools/bin/failpoint-ctl
	mkdir -p "$(TEST_ARTIFACTS_DIR)"
	make failpoint-enable
	$(GOTEST) $(TEST_OPTIONS) ./... \
	|| { make failpoint-disable ; exit 1; }
	make failpoint-disable

imp-unit-test: tools/bin/failpoint-ctl
	mkdir -p "$(TEST_ARTIFACTS_DIR)"
	make failpoint-enable
	$(GOTEST) $(TEST_OPTIONS) \
		./internal/service/import/... \
		./internal/model/import/... \
		./internal/openapi/service/import/... \
	|| { make failpoint-disable ; exit 1; }
	make failpoint-disable

dm-unit-test: tools/bin/failpoint-ctl
	mkdir -p "$(TEST_ARTIFACTS_DIR)"
	make failpoint-enable
	$(GOTEST) $(TEST_OPTIONS) \
		./internal/service/dm/... \
		./internal/model/dm/... \
	|| { make failpoint-disable ; exit 1; }
	make failpoint-disable

agent-unit-test: tools/bin/failpoint-ctl
	mkdir -p "$(TEST_ARTIFACTS_DIR)"
	make failpoint-enable
	$(GOTEST) $(TEST_OPTIONS) \
		./pkg/agent/... \
	|| { make failpoint-disable ; exit 1; }
	make failpoint-disable

impdm-unit-test: tools/bin/failpoint-ctl
	mkdir -p "$(TEST_ARTIFACTS_DIR)"
	make failpoint-enable
	$(GOTEST) $(TEST_OPTIONS) \
		./internal/service/import/... \
		./internal/model/import/... \
		./internal/openapi/service/import/... \
		./internal/service/dm/... \
		./internal/model/dm/... \
		./pkg/agent/... \
	|| { make failpoint-disable ; exit 1; }
	make failpoint-disable


APP_DIR_TEST = .test
APP_PACKAGE := ./app/...
APP_COVERAGE_FAIL_UNDER := 70
APP_FILE_ALL_COVERAGE = $(APP_DIR_TEST)/coverage_all.txt
APP_TOTAL_COVERAGE = $(APP_DIR_TEST)/coverage_total.txt
APP_TEST_FLAGS :=  -race -failfast -p=1 -covermode=atomic -timeout 5m

app-unit-test-local:
	go test $(APP_PACKAGE)

app-unit-test: tools/bin/gotestsum tools/bin/gocovmerge
	mkdir -p $(APP_DIR_TEST)
	@echo running test
	@# skip mock package , bash scripts is too hacky for newbee > <:
	@for package in $$(go list $(APP_PACKAGE)); do \
        if [ `echo $$(echo $$package | tr / -) | grep -E 'mock' | wc -l` -gt 0 ] ; then \
            echo "skip $$package"; \
        elif [ `echo $$(echo $$package | tr / -) | grep -E 'domain' | wc -l` -gt 0 ]; then \
            echo "skip not need test domain $$package"; \
        else \
            coverfile="$(APP_DIR_TEST)/$$(echo $$package | tr / -).cover"; \
            tools/bin/gotestsum -- $(TESAPP_TEST_FLAGST_FLAGS) -coverprofile="$$coverfile" "$$package" -coverpkg="$$package" || exit 1; \
		fi \
	done

app-check-coverage: app-unit-test
	@echo merge all coverage files into one
	@tools/bin/gocovmerge $(APP_DIR_TEST)/*.cover > $(APP_FILE_ALL_COVERAGE)

	#  generate coverage report
	$(GO) tool cover -func $(APP_FILE_ALL_COVERAGE) | tee $(APP_TOTAL_COVERAGE)

	@echo show coverage
	@total_coverage=`tail -1 $(APP_TOTAL_COVERAGE) | cut -d\) -f2 | tr -d '[:space:]|%' | cut -d. -f1` && echo "total coverate is" $$total_coverage

	@echo test coverage
	test `tail -1 $(APP_TOTAL_COVERAGE) | cut -d\) -f2 | tr -d '[:space:]|%' | cut -d. -f1` -ge $(APP_COVERAGE_FAIL_UNDER)

generate-coverage:
	make failpoint-enable
	$(GOCOVER) -html=$(TEST_ARTIFACTS_OUT) -o $(TEST_ARTIFACTS_FILE_COVER)
	make failpoint-disable

browse-coverage: generate-coverage
	$(BROWSER_OPEN) $(TEST_ARTIFACTS_FILE_COVER)

show-total-coverage:
	make failpoint-enable
	$(GOCOVER) -func $(TEST_ARTIFACTS_OUT) | fgrep "total:" | awk '{print $$NF}'
	make failpoint-disable

tidy:
	$(GO) mod tidy -e

# tools
tools/bin/gotestsum: tools/go.mod
	echo "build gotestsum"
	make -C ./tools gotestsum

tools/bin/golangci-lint: tools/go.mod
	echo "build golangci-lint"
	make -C ./tools golangci-lint

tools/bin/goqueryset: tools/go.mod
	echo "build goqueryset"
	make -C ./tools goqueryset

tools/bin/gofumpt: tools/go.mod
	echo "build gofumpt"
	make -C ./tools gofumpt

tools/bin/buf:
	echo "build buf"
	make -C ./tools buf

tools/bin/failpoint-ctl:
	echo "build failpoint-ctl"
	make -C ./tools failpoint-ctl

tools/bin/protoc-gen-validate:
	echo "build protoc-gen-validate"
	make -C ./tools protoc-gen-validate

tools/bin/swagger: tools/go.mod
	echo "build swagger"
	make -C ./tools swagger

tools/bin/gocovmerge: tools/go.mod
	echo "build gocovmerge"
	make -C ./tools gocovmerge

tools/bin/mockery: tools/go.mod
	echo "build mockery"
	make -C ./tools mockery

tools/bin/shfmt: tools/go.mod
	echo "build shfmt"
	make -C ./tools shfmt

import-openapi-test:
	echo -e "run import openapi test: \nmake import-openapi-test API_AK API_SK SERVERLESS_CLUSTER EKS_CLUSTER GKE_CLUSTER \nCLUSTER should be projectId:clusterId"
	./test/import/run.sh --env $(ENV) --api_ak $(API_AK) --api_sk $(API_SK) --serverless_cluster $(SERVERLESS_CLUSTER) --eks_cluster $(EKS_CLUSTER) --gke_cluster $(GKE_CLUSTER)

gapi-lint:
	cd idl/idl/dataflow; \
	set -e ;\
	TMP=$$(mktemp -d) ;\
	buf export . --output $$TMP ;\
	api-linter --config=adminapi/.api-linter.yaml -I $$TMP "adminapi/v1beta1/import.proto" ;\
	api-linter --config=adminapi/.api-linter.yaml -I $$TMP "adminapi/v1beta1/backup.proto" ;\
	api-linter  --config=openapi/.api-linter.yaml -I $$TMP "openapi/v1beta1/changefeed.proto" ;\
	rm -rf $$TMP;

run-e2e:
	@BRANCH=$${BRANCH:-"master"}; \
	SERVICE_PLAN=$${SERVICE_PLAN:-"dedicated"}; \
	ASYNC_DEPS=$${ASYNC_DEPS:-"true"}; \
	DOMAIN=$${DOMAIN:-"cdc"}; \
	API_MODE=$${API_MODE:-"consoleapi"}; \
	TEST_ENV=$${TEST_ENV:-"dev"}; \
	CASE_GROUP=$${CASE_GROUP:-"smoke"}; \
	CASES=$${CASES:-""}; \
	ORG_ID=$${ORG_ID:-""}; \
	PROJECT_ID=$${PROJECT_ID:-""}; \
	DEDICATED_CLUSTER_IDS=$${DEDICATED_CLUSTER_IDS:-""}; \
	if [ -z "$$DEDICATED_CLUSTER_IDS" ]; then DEDICATED_CLUSTER_IDS=$${CLUSTER_IDS:-""}; fi; \
	PREMIUM_CLUSTER_IDS=$${PREMIUM_CLUSTER_IDS:-""}; \
	SERVERLESS_CLUSTER_IDS=$${SERVERLESS_CLUSTER_IDS:-""}; \
	AWS_REGIONS=$${AWS_REGIONS:-""}; \
	GCP_REGIONS=$${GCP_REGIONS:-""}; \
	AZURE_REGIONS=$${AZURE_REGIONS:-""}; \
	ALICLOUD_REGIONS=$${ALICLOUD_REGIONS:-""}; \
	gh workflow run e2e-test.yaml \
		--ref $$BRANCH \
		-f service_plan=$$SERVICE_PLAN \
		-f async_dependency_provisioning=$$ASYNC_DEPS \
		-f executor=ci \
		-f domain=$$DOMAIN \
		-f api_mode=$$API_MODE \
		-f env=$$TEST_ENV \
		-f case_group="$$CASE_GROUP" \
		-f cases="$$CASES" \
		-f org_id="$$ORG_ID" \
		-f project_id="$$PROJECT_ID" \
		-f dedicated_cluster_ids="$$DEDICATED_CLUSTER_IDS" \
		-f premium_cluster_ids="$$PREMIUM_CLUSTER_IDS" \
		-f serverless_cluster_ids="$$SERVERLESS_CLUSTER_IDS" \
		-f auth_token="$$AUTH_TOKEN" \
		-f aws_regions="$$AWS_REGIONS" \
		-f gcp_regions="$$GCP_REGIONS" \
		-f azure_regions="$$AZURE_REGIONS" \
		-f alicloud_regions="$$ALICLOUD_REGIONS"

.PHONY: run-premium-e2e
run-premium-e2e:
	@$(MAKE) run-e2e SERVICE_PLAN=premium

.PHONY: run-essential-e2e
run-essential-e2e:
	@$(MAKE) run-e2e SERVICE_PLAN=essential-v1
