SHELL := /bin/bash
.SHELLFLAGS := -eu -o pipefail -c
.ONESHELL:

GOMAXPROCS ?= 16
GOCACHE ?= /tmp/agentteam-go-cache
GO_TEST_P ?= 2
lane ?=

.PHONY: default sync-env ensure-web-deps init e2e-web e2e-web-lane test \
	regression-go-fast regression-go-tidb regression-tidb \
	check-go-package-names check-import-aliases check-store-import-aliases check-store-refactor-boundaries check-observability \
	check-agent-goimports check-agent-drivercontract check-electron-cutover harness-fast regression-fast regression-channel-collab \
	build\:desktop build\:daemon build\:server \
	run-dep cleanup-leftovers reset-dep run-migrate-up migrate dev\:server dev\:desktop \
	docker-dev docker-dev-down

# dev-cli is the canonical root-module tool dependency for the dev CLI.
dev-cli := go tool github.com/tidbcloud/AgentTeam/dev

default:
	@awk -F: '/^[A-Za-z0-9_-]+:/ { print $$1 }' $(MAKEFILE_LIST) | sort

sync-env:
	@if [[ ! -f .env ]]; then cp .env.example .env; fi

ensure-web-deps:
	./scripts/ensure-web-node-modules.sh

init: sync-env
	go mod download
	$(MAKE) ensure-web-deps

# Build the native Electron GUI package for the current macOS/Windows host.
# desktop/Makefile owns platform staging, the sidecar, managed runtime, renderer,
# and electron-builder details; keep this root target as the stable entry point.
build\:desktop:
	$(MAKE) -C desktop desktop

# Build only the pure-Go Desktop daemon/CLI binary at desktop/bin/agentteam[.exe].
# This deliberately excludes Electron, Node dependencies, and renderer assets.
build\:daemon:
	$(MAKE) -C desktop build-daemon

# Build only the AgentTeam Server binary at bin/agentteam-server[.exe].
# Reuse the canonical build-info helper so local and release binaries expose
# the same version, commit, and build-time fields.
build\:server:
	@mkdir -p bin; \
	server_binary="bin/agentteam-server"; \
	if [[ "$${GOOS:-}" == "windows" ]]; then server_binary+=".exe"; fi; \
	agentteam_go_ldflags="$$(./scripts/agentteam-go-buildinfo-ldflags.sh .)"; \
	go build -buildvcs=false -trimpath -ldflags "$$agentteam_go_ldflags" -o "$$server_binary" ./cmd/agentteam-server; \
	echo "Built $$server_binary"

e2e-web: sync-env
	./scripts/ci-web-e2e-full.sh

e2e-web-lane: sync-env
	@if [[ -z "$(lane)" ]]; then echo "usage: make e2e-web-lane lane=<lane>" >&2; exit 2; fi
	./scripts/ci-web-e2e-lane.sh "$(lane)"

test: sync-env
	echo "running backend go tests"
	GOMAXPROCS=$${GOMAXPROCS:-16} GOCACHE=/tmp/agentteam-go-cache go test ./... -p $(GO_TEST_P) -timeout=20m
	echo "running presets go tests"
	cd presets && GOCACHE=/tmp/agentteam-go-cache go test ./... -count=1
	echo "running desktop go tests"
	cd desktop && make test
	echo "building web"
	$(MAKE) ensure-web-deps
	cd web && npm run build
	echo "running daemon protocol e2e"
	GOMAXPROCS=$${GOMAXPROCS:-16} GOCACHE=/tmp/agentteam-go-cache go test -tags=e2e ./e2e -count=1 -timeout=20m
	echo "running web e2e"
	$(MAKE) e2e-web

regression-go-fast:
	GOMAXPROCS=$${GOMAXPROCS:-16} GOCACHE=/tmp/agentteam-go-cache go test ./internal/store ./internal/store/organization ./internal/api/agent ./internal/api/im ./internal/api/daemon ./internal/store/access ./internal/store/message ./internal/store/agent ./internal/store/taskboard ./cmd/agentteam-e2e-server -count=1 -p $(GO_TEST_P)
	GOMAXPROCS=$${GOMAXPROCS:-16} GOCACHE=/tmp/agentteam-go-cache go test ./internal/router -count=1 -p $(GO_TEST_P) -timeout=20m

regression-go-tidb: sync-env
	explicit_tidb_playground_version="$${AGENTTEAM_DEV_TIDB_VERSION-__loop_unset__}"
	set -a
	source .env
	set +a
	if [[ "$${explicit_tidb_playground_version}" != "__agentteam_unset__" ]]; then
	  export AGENTTEAM_DEV_TIDB_VERSION="$${explicit_tidb_playground_version}"
	elif [[ "$${AGENTTEAM_DEV_TIDB_VERSION:-}" == "v8.5.5" ]]; then
	  unset AGENTTEAM_DEV_TIDB_VERSION
	fi
	regression_tag="agentteam-regression-tidb"
	tidb_test_dsn="$${AGENTTEAM_TIDB_TEST_DSN:-}"
	if [[ -z "$${tidb_test_dsn}" ]]; then
	  tidb_test_dsn='root@tcp(127.0.0.1:14000)/?multiStatements=true&parseTime=true&loc=UTC'
	fi
	if [[ "$${tidb_test_dsn}" == *"@tcp(localhost:14000)"* ]] || [[ "$${tidb_test_dsn}" == *"@tcp(127.0.0.1:14000)"* ]] || [[ "$${tidb_test_dsn}" == *"@tcp([::1]:14000)"* ]]; then
	  AGENTTEAM_DEV_TIDB_TAG="$${regression_tag}" \
	    AGENTTEAM_DEV_TIDB_DB_PORT=14000 \
	    AGENTTEAM_DEV_TIDB_PD_PORT=12379 \
	    AGENTTEAM_DEV_TIDB_KV_PORT=30160 \
	    AGENTTEAM_DEV_TIDB_VERSION="$${AGENTTEAM_DEV_TIDB_VERSION:-v8.5.5}" \
	    $(dev-cli) --database &
	  regression_tidb_pid=$$!
	  trap 'kill "$$regression_tidb_pid" 2>/dev/null || true; wait "$$regression_tidb_pid" 2>/dev/null || true' EXIT
	  # wait for the regression TiDB to become reachable (max ~120s)
	  for i in $$(seq 1 120); do \
	    if mysql --connect-timeout=1 --protocol tcp -h127.0.0.1 -P14000 -uroot --batch --skip-column-names -e 'SELECT VERSION()' 2>/dev/null | grep -qi 'tidb'; then \
	      break; \
	    fi; \
	    sleep 1; \
	  done
	else
	  echo "skipping local tidb for remote AGENTTEAM_TIDB_TEST_DSN"
	fi
	export AGENTTEAM_TIDB_TEST_DSN="$${tidb_test_dsn}"
	tidb_template_database="$$(GOCACHE=/tmp/agentteam-go-cache go run ./cmd/tidb-template-preparer)"
	GOCACHE=/tmp/agentteam-go-cache go test ./internal/coredb/testkit ./internal/coredb ./internal/config -count=1
	GOCACHE=/tmp/agentteam-go-cache go test -p 1 ./internal/tidb -count=1 -v
	GOMAXPROCS=$${GOMAXPROCS:-16} GOCACHE=/tmp/agentteam-go-cache AGENTTEAM_TIDB_TEMPLATE_DATABASE="$${tidb_template_database}" go test -p 3 ./internal/server ./internal/store/taskboard ./internal/store -run 'Test(NewStartsAgainstTiDBMainPath|UserTaskLifecycleOnTiDB|MessagePathOnTiDB)' -count=1 -v
	GOCACHE=/tmp/agentteam-go-cache go test -p 1 ./internal/database -run TestTiDBWorkflowQueueLeaseSemantics -count=1 -v

regression-tidb: regression-go-tidb

check-go-package-names:
	PYTHONDONTWRITEBYTECODE=1 python3 guardrails/repository/check-go-package-names-test.py
	PYTHONDONTWRITEBYTECODE=1 python3 guardrails/repository/check-go-package-names.py

check-import-aliases:
	PYTHONDONTWRITEBYTECODE=1 python3 scripts/check-import-aliases.test.py
	PYTHONDONTWRITEBYTECODE=1 ./scripts/check-import-aliases.py --baseline guardrails/importalias/baseline.txt

check-store-import-aliases: check-import-aliases

check-store-refactor-boundaries:
	./guardrails/store/check-app-canonical-runtime.sh
	./guardrails/store/check-store-refactor-boundaries.sh

check-retired-role-model:
	./guardrails/repository/retired-role-model.sh

check-observability:
	./guardrails/observability/check-observability.sh

check-agent-goimports:
	./guardrails/agent/check-agent-goimports.sh

check-agent-drivercontract:
	cd desktop && go test -count=1 ./internal/agent/drivercontract

check-electron-cutover:
	./guardrails/desktop/check-electron-cutover-test.sh
	./guardrails/desktop/check-electron-cutover.sh

harness-fast:
	./scripts/harness-fast.sh

regression-fast: regression-go-fast check-import-aliases check-agent-goimports check-agent-drivercontract harness-fast
	AGENTTEAM_TIDB_MIGRATION_BASE_REF=$$(git rev-parse --verify 'origin/main^{commit}') ./scripts/ci-backend-required.sh validate
	$(MAKE) ensure-web-deps
	cd web && npm run build

regression-channel-collab:
	$(MAKE) ensure-web-deps
	GOCACHE=/tmp/agentteam-go-cache go test ./internal/store -run 'Test(SendMessageForUser(DirectMentionsOnlyDeliverToMentionedAgents|ControlledBroadcastRequiresWorkspaceAdmin|ChannelBroadcastAllDurablyDeliversAllAgentsAndWakesOnlineOnly|ChannelBroadcastOnlineOnlyUsesCurrentOnlineSnapshot|ThreadRepliesDeliverToInheritedParticipants|ThreadMentionAddsFutureParticipant|RejectsThreadBroadcastMentions)|GetMessageDeliveryDiagnosticsForUser(ExplainsDirectMentionsAndWakeState|ExplainsOnlineBroadcastExclusions|ExplainsThreadContextScope|ExplainsDMContextScope))' -count=1
	GOCACHE=/tmp/agentteam-go-cache go test ./internal/api/im -run 'TestStreamEventHub|TestEventsWSHandler' -count=1
	cd desktop && GOCACHE=/tmp/agentteam-go-cache go test ./internal/channel/agentteam ./internal/dispatcher ./internal/agent ./internal/workspace ./internal/cmd -count=1
	cd web && npm run build && GOCACHE=/tmp/agentteam-go-cache npm run e2e -- e2e/channel.spec.ts e2e/tasks.spec.ts e2e/replyThread.spec.ts e2e/humanDm.spec.ts --grep '@channel-collab'

run-dep: sync-env
	set -a
	source .env
	set +a
	tidb_database_url="$${AGENTTEAM_DATABASE_DSN:-$${AGENTTEAM_CORE_STORAGE_DSN:-}}"
	if [[ "$${tidb_database_url}" != *"@tcp("* ]]; then
	  tidb_database_url='root@tcp(127.0.0.1:4000)/agentteam_core?multiStatements=true&parseTime=true&loc=UTC'
	fi
	if [[ "$${tidb_database_url}" == *"@tcp(localhost:4000)"* ]] || [[ "$${tidb_database_url}" == *"@tcp(127.0.0.1:4000)"* ]] || [[ "$${tidb_database_url}" == *"@tcp([::1]:4000)"* ]]; then
	  tidb_port="4000"
	  if command -v mysql >/dev/null 2>&1 && mysql --connect-timeout=1 --protocol tcp -h127.0.0.1 -P"$${tidb_port}" -uroot --batch --skip-column-names -e 'SELECT VERSION()' 2>/dev/null | grep -qi 'tidb'; then
	    echo "skipping local tidb startup; database is already reachable"
	  else
	    AGENTTEAM_DEV_TIDB_TAG=loop-dev-tidb \
	      AGENTTEAM_DEV_TIDB_DB_PORT="$${tidb_port}" \
	      AGENTTEAM_DEV_TIDB_PD_PORT=2379 \
	      AGENTTEAM_DEV_TIDB_KV_PORT=20160 \
	      AGENTTEAM_DEV_TIDB_VERSION="$${AGENTTEAM_DEV_TIDB_VERSION:-v8.5.5}" \
	      AGENTTEAM_DEV_TIDB_DATABASE=loop_core \
	      $(dev-cli) --database-start
	  fi
	else
	  echo "skipping local tidb for remote AGENTTEAM_DATABASE_DSN"
	fi

cleanup-leftovers: sync-env
	set -a
	source .env
	set +a
	exec node scripts/cleanup-leftovers.mjs

reset-dep: cleanup-leftovers
	@:

run-migrate-up: sync-env
	set -a
	source .env
	set +a
	eval "$$(node scripts/dev-defaults.mjs)"
	tidb_core_storage_dsn="$${AGENTTEAM_DATABASE_DSN:-$${AGENTTEAM_CORE_STORAGE_DSN:-}}"
	if [[ "$${tidb_core_storage_dsn}" != *"@tcp("* ]]; then
	  tidb_core_storage_dsn='root@tcp(127.0.0.1:4000)/agentteam_core?multiStatements=true&parseTime=true&loc=UTC'
	fi
	export AGENTTEAM_DATABASE_DSN="$${tidb_core_storage_dsn}"
	for i in {1..5}; do
	  if go run ./cmd/agentteam-schema init; then
	    exit 0
	  fi
	  if [[ "$${i}" -eq 5 ]]; then
	    exit 1
	  fi
	  echo "agentteam-schema init failed (attempt $${i}/5), retrying..." >&2
	  sleep 1
	done

migrate: sync-env
	$(MAKE) run-dep
	$(MAKE) run-migrate-up

dev\:server: sync-env
	$(dev-cli) --server

dev\:desktop: sync-env
	$(dev-cli) --desktop

docker-dev:
	docker-compose -f docker-compose.dev.yml up --build

update-ags:
	./scripts/update-agent-git-service.sh

docker-dev-down:
	docker-compose -f docker-compose.dev.yml down
