ROOT_DIR := $(shell pwd)
ELECTRON_DIR := electron
BINDIR := $(ROOT_DIR)/bin
BINARY := $(BINDIR)/agentteam
INSTALL_DIR ?= $(HOME)/.agentteam/bin
INSTALL_SOURCE ?= $(BINARY)
INSTALL_BINARY_NAME ?= $(notdir $(BINARY))
INSTALL_TARGET := $(INSTALL_DIR)/$(INSTALL_BINARY_NAME)
HOST_OS ?= $(shell uname -s)
HOST_ARCH ?= $(shell uname -m)
HOST_GOOS := $(shell uname -s | tr '[:upper:]' '[:lower:]')
HOST_GOARCH := $(patsubst aarch64,arm64,$(patsubst x86_64,amd64,$(HOST_ARCH)))
AGENTTEAM_BUILD_GOOS ?=
AGENTTEAM_BUILD_GOARCH ?=
AGENTTEAM_BUILD_CGO_ENABLED ?=

ifeq ($(HOST_OS),Darwin)
HOST_GOOS := darwin
endif
ifeq ($(HOST_OS),Linux)
HOST_GOOS := linux
endif
ifeq ($(HOST_OS),Windows_NT)
HOST_GOOS := windows
endif

ifeq ($(HOST_OS),Darwin)
ifeq ($(HOST_ARCH),arm64)
ifeq ($(origin GOOS),undefined)
ifeq ($(strip $(AGENTTEAM_BUILD_GOOS)),)
AGENTTEAM_BUILD_GOOS := darwin
endif
endif
ifeq ($(origin GOARCH),undefined)
ifeq ($(strip $(AGENTTEAM_BUILD_GOARCH)),)
AGENTTEAM_BUILD_GOARCH := arm64
endif
endif
ifeq ($(origin CGO_ENABLED),undefined)
ifeq ($(strip $(AGENTTEAM_BUILD_CGO_ENABLED)),)
AGENTTEAM_BUILD_CGO_ENABLED := 1
endif
endif
endif
endif

TARGET_GOOS := $(strip $(or $(AGENTTEAM_BUILD_GOOS),$(GOOS),$(HOST_GOOS)))
TARGET_GOARCH := $(strip $(or $(AGENTTEAM_BUILD_GOARCH),$(GOARCH),$(HOST_GOARCH)))

# Electron uses win32/x64 names while Go uses windows/amd64. Keep the mapping
# here so callers can use the same familiar GOOS/GOARCH-style build overrides.
ELECTRON_PLATFORM := $(patsubst windows,win32,$(TARGET_GOOS))
ELECTRON_ARCH := $(patsubst amd64,x64,$(TARGET_GOARCH))
GIT_DESKTOP_TAG := $(shell git describe --tags --abbrev=0 2>/dev/null)
DESKTOP_VERSION ?= $(if $(strip $(AGENTTEAM_DESKTOP_VERSION)),$(strip $(AGENTTEAM_DESKTOP_VERSION)),$(patsubst v%,%,$(GIT_DESKTOP_TAG)))

GO_BUILD_ENV :=
ifneq ($(strip $(AGENTTEAM_BUILD_GOOS)),)
GO_BUILD_ENV += GOOS=$(AGENTTEAM_BUILD_GOOS)
endif
ifneq ($(strip $(AGENTTEAM_BUILD_GOARCH)),)
GO_BUILD_ENV += GOARCH=$(AGENTTEAM_BUILD_GOARCH)
endif
ifneq ($(strip $(AGENTTEAM_BUILD_CGO_ENABLED)),)
GO_BUILD_ENV += CGO_ENABLED=$(AGENTTEAM_BUILD_CGO_ENABLED)
endif

# Version information injected via -ldflags at build time.
VERSION ?= $(strip $(AGENTTEAM_DESKTOP_VERSION))
ifeq ($(strip $(VERSION)),)
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null)
endif
COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "none")
BUILD_AT := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
GO_VERSION := $(shell go version | awk '{print $$3}')

# Package path for version variables.
VERSION_PKG := github.com/tidbcloud/AgentTeam/desktop/internal/version
RUNTIMECONFIG_PKG := github.com/tidbcloud/AgentTeam/desktop/internal/agent/runtimeconfig
MANAGED_OPENCODE_CONFIG ?= $(if $(strip $(AGENTTEAM_MANAGED_OPENCODE_CONFIG)),$(AGENTTEAM_MANAGED_OPENCODE_CONFIG),$(ROOT_DIR)/release/managed-opencode.env)
MANAGED_OPENCODE_SOURCE ?= $(AGENTTEAM_MANAGED_OPENCODE_SOURCE)
MANAGED_OPENCODE_VERSION ?= $(if $(strip $(AGENTTEAM_MANAGED_OPENCODE_VERSION)),$(AGENTTEAM_MANAGED_OPENCODE_VERSION),$(shell sed -n 's/^MANAGED_OPENCODE_VERSION=//p' "$(MANAGED_OPENCODE_CONFIG)" 2>/dev/null))
MANAGED_OPENCODE_NPM_REGISTRY ?= $(if $(strip $(AGENTTEAM_MANAGED_OPENCODE_NPM_REGISTRY)),$(AGENTTEAM_MANAGED_OPENCODE_NPM_REGISTRY),$(shell sed -n 's/^MANAGED_OPENCODE_NPM_REGISTRY=//p' "$(MANAGED_OPENCODE_CONFIG)" 2>/dev/null))
MANAGED_OPENCODE_MODELS_SOURCE ?= $(AGENTTEAM_MANAGED_OPENCODE_MODELS_SOURCE)
MANAGED_OPENCODE_MODELS_URL ?= $(if $(strip $(AGENTTEAM_MANAGED_OPENCODE_MODELS_URL)),$(AGENTTEAM_MANAGED_OPENCODE_MODELS_URL),https://models.dev/api.json)
MANAGED_OPENCODE_LATEST_RELEASE_URL ?= https://github.com/anomalyco/opencode/releases/latest
MANAGED_OPENCODE_CACHE_DIR ?= $(if $(strip $(AGENTTEAM_MANAGED_OPENCODE_CACHE_DIR)),$(AGENTTEAM_MANAGED_OPENCODE_CACHE_DIR),$(ROOT_DIR)/release/.artifacts/managed-opencode)
MANAGED_OPENCODE_BINARY := opencode
MANAGED_OPENCODE_PACKAGE :=

ifeq ($(TARGET_GOOS),linux)
ifeq ($(TARGET_GOARCH),amd64)
MANAGED_OPENCODE_PACKAGE := opencode-linux-x64
endif
ifeq ($(TARGET_GOARCH),arm64)
MANAGED_OPENCODE_PACKAGE := opencode-linux-arm64
endif
endif

ifeq ($(TARGET_GOOS),darwin)
ifeq ($(TARGET_GOARCH),amd64)
MANAGED_OPENCODE_PACKAGE := opencode-darwin-x64
endif
ifeq ($(TARGET_GOARCH),arm64)
MANAGED_OPENCODE_PACKAGE := opencode-darwin-arm64
endif
endif

ifeq ($(TARGET_GOOS),windows)
ifeq ($(TARGET_GOARCH),amd64)
MANAGED_OPENCODE_PACKAGE := opencode-windows-x64
MANAGED_OPENCODE_BINARY := opencode.exe
endif
ifeq ($(TARGET_GOARCH),arm64)
MANAGED_OPENCODE_PACKAGE := opencode-windows-arm64
MANAGED_OPENCODE_BINARY := opencode.exe
endif
endif

ICON_ASSETS := \
	internal/icon/assets/icon-white.png \
	internal/icon/assets/icon-black.png \
	internal/icon/assets/tray-white.png \
	internal/icon/assets/tray-black.png \
	electron/build/icon.icns \
	electron/build/icon.ico

# ldflags to inject build-time version information.
export MACOSX_DEPLOYMENT_TARGET = 12
LDFLAGS := -s -w \
	-X '$(VERSION_PKG).Version=$(VERSION)' \
	-X '$(VERSION_PKG).Commit=$(COMMIT)' \
	-X '$(VERSION_PKG).BuildAt=$(BUILD_AT)' \
	-X '$(VERSION_PKG).GoVersion=$(GO_VERSION)'

ifneq ($(strip $(MANAGED_OPENCODE_VERSION)),)
LDFLAGS += -X '$(RUNTIMECONFIG_PKG).ManagedOpenCodeVersion=$(MANAGED_OPENCODE_VERSION)'
endif

.PHONY: help build desktop build-daemon build-renderer build-electron linux-release ensure-build-version ensure-desktop-version ensure-icon-assets ensure-managed-opencode generate-icons install run clean test ci-required prompt-policy lint fmt vet coverage coverage-html coverage-func

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

.DEFAULT_GOAL := help

build-renderer: ## Build the Electron renderer into electron/dist/renderer
	cd $(ELECTRON_DIR) && npm run build:renderer

ensure-icon-assets:
	@missing=0; \
	for asset in $(ICON_ASSETS); do \
		if [ ! -f "$$asset" ]; then \
			echo "Missing $$asset; run 'make generate-icons' from desktop/ (requires rsvg-convert)." >&2; \
			missing=1; \
		fi; \
	done; \
	if [ "$$missing" -ne 0 ]; then exit 1; fi

ensure-build-version:
	@if [ -z "$(strip $(VERSION))" ]; then \
		echo "VERSION is required; set VERSION or AGENTTEAM_DESKTOP_VERSION when git cannot determine one" >&2; \
		exit 1; \
	fi

ensure-desktop-version:
	@if [ -z "$(strip $(DESKTOP_VERSION))" ]; then \
		echo "Desktop version is required; set AGENTTEAM_DESKTOP_VERSION or fetch the repository tags" >&2; \
		exit 1; \
	fi

build: build-daemon ## Build the pure Go daemon/CLI

desktop: ensure-icon-assets ensure-desktop-version ## Build the Electron GUI app/installer for the current macOS or Windows architecture
	@case "$(ELECTRON_PLATFORM)/$(ELECTRON_ARCH)" in \
		darwin/arm64) builder_args="--mac dmg zip --arm64" ;; \
		darwin/x64) builder_args="--mac dmg zip --x64" ;; \
		win32/x64) builder_args="--win nsis --x64" ;; \
		win32/arm64) builder_args="--win nsis --arm64" ;; \
		*) echo "Electron Desktop does not support $(TARGET_GOOS)/$(TARGET_GOARCH). Supported targets: darwin/arm64, darwin/amd64, windows/arm64, windows/amd64." >&2; exit 1 ;; \
	esac; \
	runtime_version="$$(AGENTTEAM_MANAGED_OPENCODE_VERSION="$${AGENTTEAM_MANAGED_OPENCODE_VERSION:-}" MANAGED_OPENCODE_LATEST_RELEASE_URL="$(MANAGED_OPENCODE_LATEST_RELEASE_URL)" ./scripts/resolve-opencode-version.sh)"; \
	if [[ -z "$${AGENTTEAM_MANAGED_OPENCODE_MODELS_SOURCE:-}" ]]; then \
		models_path="$(MANAGED_OPENCODE_CACHE_DIR)/$$runtime_version/models.json"; \
		if [[ ! -f "$$models_path" ]]; then \
			mkdir -p "$$(dirname "$$models_path")"; \
			models_tmp="$$(mktemp "$$(dirname "$$models_path")/.models.json.XXXXXX")"; \
			trap 'rm -f "$$models_tmp"' EXIT; \
			curl -fsSL "$(MANAGED_OPENCODE_MODELS_URL)" -o "$$models_tmp"; \
			chmod 0644 "$$models_tmp"; \
			mv "$$models_tmp" "$$models_path"; \
			trap - EXIT; \
		fi; \
	fi; \
	echo "Building AgentTeam Desktop $(DESKTOP_VERSION) for $(ELECTRON_PLATFORM)/$(ELECTRON_ARCH) with OpenCode $$runtime_version"; \
	cd $(ELECTRON_DIR); \
	AGENTTEAM_DESKTOP_PLATFORM="$(ELECTRON_PLATFORM)" \
	AGENTTEAM_DESKTOP_ARCH="$(ELECTRON_ARCH)" \
	AGENTTEAM_DESKTOP_VERSION="$(DESKTOP_VERSION)" \
	AGENTTEAM_MANAGED_OPENCODE_VERSION="$$runtime_version" \
		npm run dist -- $$builder_args --publish never

build-daemon: ensure-build-version ensure-icon-assets ## Build the pure Go daemon/CLI without Node or web assets
	@mkdir -p $(BINDIR)
	cd $(ROOT_DIR) && $(GO_BUILD_ENV) go build -buildvcs=false -trimpath -tags "devtools" -ldflags "$(LDFLAGS)" -o $(BINARY) ./cmd/agentteam

build-electron: ## Build and verify the Electron app staging tree
	cd $(ELECTRON_DIR) && npm run build:electron && npm run stage && npm run verify

linux-release: ## Build a Linux daemon release plus its managed builtin runtime
	@if [ "$(TARGET_GOOS)" != "linux" ]; then \
		echo "linux-release requires AGENTTEAM_BUILD_GOOS=linux (got $(TARGET_GOOS))" >&2; \
		exit 1; \
	fi
	@if [ -z "$(MANAGED_OPENCODE_SOURCE)" ] || [ -z "$(MANAGED_OPENCODE_MODELS_SOURCE)" ]; then \
		echo "linux-release requires explicit MANAGED_OPENCODE_SOURCE and MANAGED_OPENCODE_MODELS_SOURCE; it never invokes Node or downloads web/runtime payloads" >&2; \
		exit 1; \
	fi
	$(MAKE) build-daemon ensure-managed-opencode

ensure-managed-opencode:
	@if [ -z "$(MANAGED_OPENCODE_VERSION)" ]; then \
		echo "MANAGED_OPENCODE_VERSION is required; set it in $(MANAGED_OPENCODE_CONFIG) or env" >&2; \
		exit 1; \
	fi
	@if [ -z "$(MANAGED_OPENCODE_NPM_REGISTRY)" ]; then \
		echo "MANAGED_OPENCODE_NPM_REGISTRY is required; set it in $(MANAGED_OPENCODE_CONFIG) or env" >&2; \
		exit 1; \
	fi
	@if [ -z "$(MANAGED_OPENCODE_PACKAGE)" ]; then \
		echo "managed OpenCode is not configured for $(TARGET_GOOS)/$(TARGET_GOARCH)" >&2; \
		exit 1; \
	fi
	@set -e; \
	runtime_path="$(BINDIR)/runtimes/opencode/$(MANAGED_OPENCODE_BINARY)"; \
	models_path="$(BINDIR)/runtimes/opencode/models.json"; \
	if [ -n "$(MANAGED_OPENCODE_SOURCE)" ]; then \
		source_path="$(MANAGED_OPENCODE_SOURCE)"; \
	else \
		package_root="$(MANAGED_OPENCODE_CACHE_DIR)/$(MANAGED_OPENCODE_VERSION)/$(MANAGED_OPENCODE_PACKAGE)"; \
		source_path="$$package_root/package/bin/$(MANAGED_OPENCODE_BINARY)"; \
		if [ ! -f "$$source_path" ]; then \
			command -v npm >/dev/null 2>&1 || { echo "npm is required to download managed OpenCode" >&2; exit 1; }; \
			command -v tar >/dev/null 2>&1 || { echo "tar is required to unpack managed OpenCode" >&2; exit 1; }; \
			mkdir -p "$(MANAGED_OPENCODE_CACHE_DIR)" "$$package_root"; \
			npm_tmp="$$(mktemp -d "$(MANAGED_OPENCODE_CACHE_DIR)/npm-pack.XXXXXX")"; \
			echo "==> download managed OpenCode $(MANAGED_OPENCODE_PACKAGE)@$(MANAGED_OPENCODE_VERSION)"; \
			(cd "$$npm_tmp" && npm pack "$(MANAGED_OPENCODE_PACKAGE)@$(MANAGED_OPENCODE_VERSION)" --registry "$(MANAGED_OPENCODE_NPM_REGISTRY)" --silent >/dev/null); \
			tgz_name="$$(find "$$npm_tmp" -maxdepth 1 -type f -name '*.tgz' -print -quit)"; \
			if [ -z "$$tgz_name" ]; then \
				rm -rf "$$npm_tmp"; \
				echo "npm pack did not produce a tarball for managed OpenCode" >&2; \
				exit 1; \
			fi; \
			rm -rf "$$package_root/package"; \
			tar -xzf "$$tgz_name" -C "$$package_root"; \
			rm -rf "$$npm_tmp"; \
			chmod 0755 "$$source_path"; \
		fi; \
	fi; \
	if [ ! -f "$$source_path" ]; then \
		echo "managed OpenCode source not found: $$source_path" >&2; \
		exit 1; \
	fi; \
	bash "$(ROOT_DIR)/release/managed-opencode-validate.sh" "$(TARGET_GOOS)" "$$source_path"; \
	if [ -n "$(MANAGED_OPENCODE_MODELS_SOURCE)" ]; then \
		models_source_path="$(MANAGED_OPENCODE_MODELS_SOURCE)"; \
	else \
		models_source_path="$(MANAGED_OPENCODE_CACHE_DIR)/$(MANAGED_OPENCODE_VERSION)/models.json"; \
		if [ ! -f "$$models_source_path" ]; then \
			command -v curl >/dev/null 2>&1 || { echo "curl is required to download managed OpenCode models catalog" >&2; exit 1; }; \
			mkdir -p "$$(dirname "$$models_source_path")"; \
			echo "==> download managed OpenCode models catalog $(MANAGED_OPENCODE_MODELS_URL)"; \
			curl -fsSL "$(MANAGED_OPENCODE_MODELS_URL)" -o "$$models_source_path.tmp" || { rm -f "$$models_source_path.tmp"; echo "failed to download managed OpenCode models catalog from $(MANAGED_OPENCODE_MODELS_URL)" >&2; exit 1; }; \
			mv "$$models_source_path.tmp" "$$models_source_path"; \
			chmod 0644 "$$models_source_path"; \
		fi; \
	fi; \
	if [ ! -f "$$models_source_path" ]; then \
		echo "managed OpenCode models source not found: $$models_source_path" >&2; \
		exit 1; \
	fi; \
	mkdir -p "$$(dirname "$$runtime_path")"; \
	install -m 0755 "$$source_path" "$$runtime_path"; \
	install -m 0644 "$$models_source_path" "$$models_path"; \
	echo "Bundled managed OpenCode to $$runtime_path"

generate-icons: ## Generate native icon files (.icns and .ico)
	cd $(ROOT_DIR) && go run ./cmd/genicon -native

install: ## Install agentteam binary locally (default: ~/.agentteam/bin)
	@if [ "$(INSTALL_SOURCE)" = "$(BINARY)" ]; then \
		$(MAKE) build; \
	fi
	@mkdir -p "$(INSTALL_DIR)"
	install -m 0755 "$(INSTALL_SOURCE)" "$(INSTALL_TARGET)"
	@source_runtime_dir="$$(dirname "$(INSTALL_SOURCE)")/runtimes"; \
	if [ -d "$$source_runtime_dir" ]; then \
		mkdir -p "$(INSTALL_DIR)/runtimes"; \
		cp -R "$$source_runtime_dir/." "$(INSTALL_DIR)/runtimes/"; \
		runtime_target="$(INSTALL_DIR)/runtimes/opencode/$(MANAGED_OPENCODE_BINARY)"; \
		if [ -f "$$runtime_target" ]; then chmod 0755 "$$runtime_target"; fi; \
		echo "Installed bundled runtimes to $(INSTALL_DIR)/runtimes"; \
	fi
	@echo "Installed $(INSTALL_BINARY_NAME) to $(INSTALL_TARGET)"

clean: ## Remove build artifacts
	rm -rf $(BINDIR) coverage.out coverage.html coverage-func.txt

test: ## Run all tests
	go test ./...

prompt-policy: ## Validate the compiler-owned prompt registry and repository guardrails
	cd $(ROOT_DIR)/.. && ./scripts/check-prompt-policy.sh

ci-required: prompt-policy ## Run the daemon required-check suite
	env -u ASDF_DATA_DIR -u FNM_DIR -u MISE_DATA_DIR -u NPM_CONFIG_PREFIX -u NVM_DIR -u VOLTA_HOME go test ./...

test-e2e: ## Run e2e tests
	go test -tags e2e ./e2e/...

lint: ## Run linter
	golangci-lint run

fmt: ## Format Go code
	go fmt ./...

vet: ## Run go vet
	go vet ./...

COVERAGE_PKGS = $(shell go list ./... 2>/dev/null | grep -v -e /e2e -e /web -e /cmd/genicon -e /version -e /icon -e /upgrade -e /doctor -e /runtime -e /system -e /session -e /api/dto -e /api$$  -e '^\.$$')

coverage: ## Run tests with coverage (output to coverage.out)
	@rm -f coverage.out
	go test -coverprofile=coverage.out -covermode=atomic $(COVERAGE_PKGS)
	@if [ -f coverage.out ]; then echo "Coverage generated successfully"; else echo "Failed to generate coverage"; fi

coverage-html: coverage ## Generate HTML coverage report (coverage.html)
	go tool cover -html=coverage.out -o coverage.html
	@echo "Coverage report: coverage.html"

coverage-func: coverage ## Show per-function coverage (coverage-func.txt)
	go tool cover -func=coverage.out > coverage-func.txt
	@echo "Per-function coverage: coverage-func.txt"
	@echo ""
	@echo "=== Coverage Summary ==="
	@tail -1 coverage-func.txt
