.PHONY: build clean test fmt vet lint help gen-proto gen-proto-go gen-proto-py mod-tidy mod-vendor build-conch-init-initramfs

# project name
PROJECT_NAME := Conch

# Go commands
GOCMD := go
GOBUILD := $(GOCMD) build
GOCLEAN := $(GOCMD) clean
GOTEST := $(GOCMD) test
GOGET := $(GOCMD) get
GOMOD := $(GOCMD) mod

# binary output directory
BIN_DIR := bin
VERSION_PKG := github.com/openeuler/Conch/internal/version
VERSION_TAG ?= $(shell git describe --tags --abbrev=0 2>/dev/null || echo unknown)
GIT_COMMIT ?= $(shell git rev-parse --short=8 HEAD 2>/dev/null || echo unknown)
VERSION_LDFLAGS := -X $(VERSION_PKG).Version=$(VERSION_TAG) -X $(VERSION_PKG).Commit=$(GIT_COMMIT)

CONCH_PROTO_DIR := ./api
CONCH_GO_PROTO_DIR := ./api/go_proto
CONCH_PY_PROTO_DIR := ./api/py_proto
CLEANSCRIPT := ./scripts/cleancode.sh

# get all cmd directory subdirectories as binary names
CMDS := $(shell find cmd -mindepth 1 -maxdepth 1 -type d ! -name conch-unpack -exec basename {} \;)

export GO111MODULE=on
export PATH := $(shell go env GOPATH)/bin:$(PATH)

default: build

help: ## Show this help message
	@echo "available commands:"
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2}'

gen-proto: gen-proto-go gen-proto-py ## Generate Go and Python protobuf code

gen-proto-go: ## Generate Go protobuf code
	@echo "installing proto tools (if not exist)..."
	@which protoc >/dev/null 2>&1 || { echo "protoc is required; please install protobuf-compiler"; exit 1; }
	@which protoc-gen-go >/dev/null 2>&1 || GOBIN=$(shell go env GOPATH)/bin go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11
	@which protoc-gen-connect-go >/dev/null 2>&1 || GOBIN=$(shell go env GOPATH)/bin go install connectrpc.com/connect/cmd/protoc-gen-connect-go@v1.20.0
	@gopath_bin="$$(go env GOPATH)/bin"; \
	case ":$$PATH:" in \
		*":$$gopath_bin:"*) ;; \
		*) \
			echo "GOPATH/bin is not in PATH; copying proto tools to /usr/bin..."; \
			cp_cmd=cp; \
			[ -w /usr/bin ] || cp_cmd="sudo cp"; \
			[ -x "$$gopath_bin/protoc-gen-go" ] && $$cp_cmd "$$gopath_bin/protoc-gen-go" /usr/bin/protoc-gen-go; \
			[ -x "$$gopath_bin/protoc-gen-connect-go" ] && $$cp_cmd "$$gopath_bin/protoc-gen-connect-go" /usr/bin/protoc-gen-connect-go; \
			;; \
	esac

	@echo "generating Go proto code..."
	@rm -rf $(CONCH_GO_PROTO_DIR)
	@mkdir -p $(CONCH_GO_PROTO_DIR)
	@protoc \
		--proto_path=$(CONCH_PROTO_DIR) \
		--go_out=$(CONCH_GO_PROTO_DIR) \
		--go_opt=paths=source_relative \
		--connect-go_out=$(CONCH_GO_PROTO_DIR) \
		--connect-go_opt=paths=source_relative \
		$(CONCH_PROTO_DIR)/*.proto;
	@echo "Go proto code generated to $(CONCH_GO_PROTO_DIR)"

gen-proto-py: ## Generate Python protobuf and Connect client code
	@echo "ensuring Python proto tools..."
	@which protoc >/dev/null 2>&1 || { echo "protoc is required; please install protobuf-compiler"; exit 1; }
	@which protoc-gen-connect-python >/dev/null 2>&1 || python3 -m pip install protoc-gen-connect-python==0.9.0
	@echo "generating Python proto code..."
	@rm -rf $(CONCH_PY_PROTO_DIR)
	@mkdir -p $(CONCH_PY_PROTO_DIR)
	@protoc \
		--proto_path=$(CONCH_PROTO_DIR) \
		--python_out=$(CONCH_PY_PROTO_DIR) \
		--pyi_out=$(CONCH_PY_PROTO_DIR) \
		--connect-python_out=$(CONCH_PY_PROTO_DIR) \
		$(CONCH_PROTO_DIR)/agent.proto
	@echo "Fixing Python Connect import..."
	@python3 -c "import pathlib; p=pathlib.Path('$(CONCH_PY_PROTO_DIR)/agent_connect.py'); p.write_text(p.read_text().replace('import agent_pb2 as agent__pb2', 'from . import agent_pb2 as agent__pb2'))"
	@python3 -c "import pathlib; pathlib.Path('$(CONCH_PY_PROTO_DIR)/__init__.py').write_text('# Auto-generated by Makefile gen-proto-py\nfrom . import agent_pb2, agent_connect\n')"
	@echo "Python proto code generated to $(CONCH_PY_PROTO_DIR)"

build: ## Build all Conch binaries
	@mkdir -p $(BIN_DIR)
	@set -e; for cmd in $(CMDS); do \
		echo "building $$cmd..."; \
		$(GOBUILD) -ldflags "$(VERSION_LDFLAGS)" -o "$(BIN_DIR)/$$cmd" "./cmd/$$cmd"; \
	done

build-offline:
	@echo "syncing dependency manifests from image backup..."
	@cp /go/go.mod.backup ./go.mod
	@cp /go/go.sum.backup ./go.sum
	@echo "building binaries using image cache..."
	@mkdir -p $(BIN_DIR)
	@for cmd in $(CMDS); do \
		echo "building static cmd/$$cmd..."; \
		CGO_ENABLED=0 GOOS=linux GOARCH=$$(go env GOARCH) \
			$(GOBUILD) -mod=readonly -tags "netgo,osusergo" \
			-ldflags '-s -w -extldflags "-static" $(VERSION_LDFLAGS)' \
			-o $(BIN_DIR)/$$cmd ./cmd/$$cmd; \
	done
	@git checkout go.mod go.sum 2>/dev/null || true

build-%: ## Build specific binary (e.g., make build-conchd)
	@echo "building cmd/$*..."
	@mkdir -p $(BIN_DIR)
	$(GOBUILD) -ldflags "$(VERSION_LDFLAGS)" -o $(BIN_DIR)/$* ./cmd/$*

build-conch-init-initramfs: ## Build minimal initramfs that runs conch-init as PID 1
	@echo "building static conch-init for initramfs..."
	@mkdir -p $(BIN_DIR)
	CGO_ENABLED=0 GOOS=linux GOARCH=$$(go env GOARCH) \
		$(GOBUILD) -tags "netgo,osusergo" \
		-ldflags '-s -w -extldflags "-static"' -buildvcs=false \
		-o $(BIN_DIR)/conch-init ./cmd/conch-init
	./scripts/build-conch-init-initramfs.sh --init-bin "$(BIN_DIR)/conch-init"

clean: ## Clean build artifacts
	@echo "cleaning build artifacts..."
	$(GOCLEAN)
	rm -rf $(BIN_DIR)
	@echo "cleaning completed"

test: ## Run all tests
	@echo "running tests..."
	$(GOTEST) -v ./...

test-coverage: ## Run tests with coverage report
	@echo "running tests and generating coverage report..."
	$(GOTEST) -v -coverprofile=coverage.out ./...
	$(GOCMD) tool cover -html=coverage.out -o coverage.html
	@echo "coverage report generated: coverage.html"

fmt: cleancode ## Format code (go fmt + cleancode)
	@echo "formatting code with go fmt..."
	$(GOCMD) fmt ./...
	@echo "code formatting completed"

vet: ## Run go vet
	@echo "running go vet..."
	$(GOCMD) vet ./...

lint: fmt vet ## Tidy go modules

mod-tidy: ## Tidy go modules
	@echo "tidying dependencies..."
	$(GOMOD) tidy

mod-vendor: ## Download dependencies to vendor directory
	@echo "downloading dependencies to vendor directory..."
	$(GOMOD) vendor

cleancode: ## Clean code (remove trailing spaces/CR)
	@echo "cleaning code..."
	@if [ ! -f $(CLEANSCRIPT) ]; then \
		echo "Error: cleancode script not found at $(CLEANSCRIPT)"; \
		exit 1; \
	fi
	@if [ ! -x $(CLEANSCRIPT) ]; then \
		chmod +x $(CLEANSCRIPT); \
	fi
	@$(CLEANSCRIPT) > /dev/null 2>&1
	@echo "code cleaning completed"