COMPOSE_SERVICES := mysql redis rocketmq-namesrv rocketmq-broker gateway-service room-service wallet-service user-service activity-service lucky-gift-service luck-gateway cron-service robot-service game-service notice-service statistics-service
GO_RUN_SERVICES := gateway-service room-service wallet-service user-service activity-service lucky-gift-service luck-gateway cron-service robot-service game-service notice-service statistics-service
SERVICE_ALIASES := gs gateway rs room ws wallet us user as activity lucky lucky-gift lgs luckygift lg cs cron robot robots rb game games ns notice stats statistics mq namesrv broker mysql redis rocketmq-namesrv rocketmq-broker gateway-service room-service wallet-service user-service activity-service lucky-gift-service luck-gateway cron-service robot-service game-service notice-service statistics-service
SERVICE_TOKEN := $(firstword $(filter $(SERVICE_ALIASES),$(MAKECMDGOALS)))
SERVICE_ID := $(strip $(or $(SERVICE),$(SERVICE_TOKEN)))
SERVICE_ID := $(patsubst gs,gateway-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst gateway,gateway-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst rs,room-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst room,room-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst ws,wallet-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst wallet,wallet-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst us,user-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst user,user-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst as,activity-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst activity,activity-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst lucky,lucky-gift-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst lucky-gift,lucky-gift-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst lgs,lucky-gift-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst luckygift,lucky-gift-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst lg,luck-gateway,$(SERVICE_ID))
SERVICE_ID := $(patsubst cs,cron-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst cron,cron-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst robot,robot-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst robots,robot-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst rb,robot-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst game,game-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst games,game-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst ns,notice-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst notice,notice-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst stats,statistics-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst statistics,statistics-service,$(SERVICE_ID))
SERVICE_ID := $(patsubst namesrv,rocketmq-namesrv,$(SERVICE_ID))
SERVICE_ID := $(patsubst mq,rocketmq-broker,$(SERVICE_ID))
SERVICE_ID := $(patsubst broker,rocketmq-broker,$(SERVICE_ID))
ADMIN_CONFIG ?= configs/config.yaml
DEV_RUN_SERVICES ?= all
DEV_RUN_MQ ?= 0
DEV_RUN_COMPOSE_ONLY := mysql redis rocketmq-namesrv rocketmq-broker
DEV_RUN_SERVICE_LIST := $(strip $(if $(filter $(DEV_RUN_COMPOSE_ONLY),$(SERVICE_ID)),,$(if $(SERVICE_ID),$(SERVICE_ID),$(DEV_RUN_SERVICES))))
ROCKETMQ_BROKER_CONFIG ?= ./tmp/rocketmq/broker.local.conf
export ROCKETMQ_BROKER_CONFIG

.PHONY: proto vet test build run admin admin-up admin-deps admin-bootstrap admin-test admin-build up db-init databi-real-flow-check rebuild restart stop logs ps addr down up-service check-service resolve-compose-conflicts $(SERVICE_ALIASES)

# `proto` 负责把独立 api module 内的 .proto 重新生成成 Go 代码。
proto:
	cd api && protoc \
		-I . \
		--go_out=. \
		--go_opt=paths=source_relative \
		--go-grpc_out=. \
		--go-grpc_opt=paths=source_relative \
		proto/user/v1/user.proto \
		proto/user/v1/auth.proto \
		proto/user/v1/host.proto \
		proto/activity/v1/activity.proto \
		proto/luckygift/v1/luckygift.proto \
		proto/room/v1/room.proto \
		proto/game/v1/game.proto \
		proto/robot/v1/robot.proto \
		proto/wallet/v1/wallet.proto \
		proto/events/room/v1/events.proto

# `vet` 使用当前 Go 工具链检查 workspace 内所有后端模块，升级 Go 版本时先暴露静态风险。
vet:
	go vet ./...
	go vet ./api/...
	go vet ./server/admin/...
	go vet ./server/luck-gateway/...

# `test` 统一跑 workspace 内的 app 后端、api 契约和后台管理服务测试。
test: vet
	go test ./...
	go test ./api/...
	go test ./server/admin/...
	go test ./server/luck-gateway/...

# `build` 验证 app 后端和后台管理服务命令入口都能编译。
build:
	go build ./...
	go build ./server/admin/...
	go build ./server/luck-gateway/...

# `run` 默认只拉 MySQL/Redis 并让 go run 服务读取内置默认配置，避免本地开发被 MQ topic/consumer 初始化拖慢。
# 需要验证 MQ fanout 时使用 `DEV_RUN_MQ=1 make run`，该模式会恢复 RocketMQ 配置生成和 topic 初始化。
# 可用 `make run rs`、`make run SERVICE=room-service` 或 `make run DEV_RUN_SERVICES=user-service,gateway-service` 只跑子集。
run:
	./scripts/resolve-compose-container-conflicts.sh
	./scripts/prepare-local-mysql-initdb.sh
	@if [ "$(DEV_RUN_MQ)" = "1" ]; then \
		./scripts/prepare-local-rocketmq-config.sh; \
		docker compose up -d mysql redis rocketmq-namesrv rocketmq-broker; \
	else \
		docker compose up -d mysql redis; \
	fi
	./scripts/apply-local-mysql-initdb.sh
	@if [ "$(DEV_RUN_MQ)" = "1" ]; then \
		./scripts/apply-local-rocketmq-topics.sh; \
	else \
		echo "DEV_RUN_MQ=0: skip RocketMQ startup and topic initialization"; \
	fi
	docker compose stop $(GO_RUN_SERVICES) >/dev/null 2>&1 || true
	DEV_RUN_MQ="$(DEV_RUN_MQ)" ./scripts/print-compose-addresses.sh
	@if [ -n "$(DEV_RUN_SERVICE_LIST)" ]; then \
		DEV_RUN_MQ="$(DEV_RUN_MQ)" TZ=UTC go run ./cmd/dev-run -services "$(DEV_RUN_SERVICE_LIST)"; \
	else \
		echo "compose dependencies are running; no Go service selected"; \
	fi

# `admin` 在本机直接启动后台管理后端；依赖默认从 server/admin/configs/config.yaml 读取。
# 启动前自动杀掉占用 13100 端口的旧进程，避免 bind: address already in use。
ADMIN_PORT ?= 13100
admin:
	@pids=$$(lsof -ti tcp:$(ADMIN_PORT)) && [ -n "$$pids" ] && kill -9 $$pids 2>/dev/null || true
	cd server/admin && go run ./cmd/server -config $(ADMIN_CONFIG)

# `admin-up` 先拉起后台本地依赖，再在本机启动后台管理后端。
admin-up: admin-deps admin

# `admin-deps` 拉起后台本地运行所需的 MySQL、Redis 和 user-service。
admin-deps:
	./scripts/resolve-compose-container-conflicts.sh
	./scripts/prepare-local-mysql-initdb.sh
	docker compose up -d mysql redis
	./scripts/apply-local-mysql-initdb.sh
	@if [ "$(DEV_RUN_MQ)" = "1" ]; then \
		./scripts/prepare-local-rocketmq-config.sh; \
		docker compose up -d rocketmq-namesrv rocketmq-broker; \
		./scripts/apply-local-rocketmq-topics.sh; \
	else \
		echo "DEV_RUN_MQ=0: skip RocketMQ startup and topic initialization"; \
	fi
	docker compose up --build -d user-service
	DEV_RUN_MQ="$(DEV_RUN_MQ)" ./scripts/print-compose-addresses.sh

# `admin-bootstrap` 显式执行后台初始化种子后启动服务。
admin-bootstrap:
	@pids=$$(lsof -ti tcp:$(ADMIN_PORT)) && [ -n "$$pids" ] && kill -9 $$pids 2>/dev/null || true
	cd server/admin && go run ./cmd/server -config $(ADMIN_CONFIG) -bootstrap

# `admin-test` 只跑后台管理后端测试。
admin-test:
	go test ./server/admin/...

# `admin-build` 只验证后台管理后端能编译。
admin-build:
	go build ./server/admin/...

# `up` 后台拉起本地服务；带 SERVICE 时只启动单个服务。
# 完整 up 结束后自动启动 admin 前台进程。
up:
	./scripts/resolve-compose-container-conflicts.sh
	./scripts/prepare-local-mysql-initdb.sh
	@if [ -z "$(SERVICE_ID)" ]; then \
		docker compose up -d mysql redis; \
		./scripts/apply-local-mysql-initdb.sh; \
		./scripts/apply-local-rocketmq-topics.sh; \
		docker compose up --build -d gateway-service room-service wallet-service user-service activity-service lucky-gift-service luck-gateway cron-service robot-service game-service notice-service statistics-service; \
	elif [ "$(SERVICE_ID)" = "mysql" ] || [ "$(SERVICE_ID)" = "redis" ]; then \
		docker compose up -d $(SERVICE_ID); \
	else \
		./scripts/apply-local-mysql-initdb.sh; \
		./scripts/apply-local-rocketmq-topics.sh; \
		docker compose up -d $(SERVICE_ID); \
	fi
	./scripts/print-compose-addresses.sh
	@if [ -z "$(SERVICE_ID)" ]; then \
		$(MAKE) admin; \
	fi

# `db-init` 对当前 compose MySQL 补齐所有服务 schema 和本地授权。
db-init:
	./scripts/resolve-compose-container-conflicts.sh mysql
	./scripts/apply-local-mysql-initdb.sh

# `databi-real-flow-check` 用真实 owner outbox 重放并审计 Databi 口径；充值允许走本地模拟回调，其余必须先通过真实业务流程产生事实。
databi-real-flow-check:
	./scripts/databi-real-flow-check.sh

# `rebuild` 重新构建并启动单个本地服务。
rebuild: check-service
	./scripts/resolve-compose-container-conflicts.sh
	./scripts/prepare-local-mysql-initdb.sh
	@if [ "$(SERVICE_ID)" != "mysql" ] && [ "$(SERVICE_ID)" != "redis" ]; then \
		./scripts/apply-local-mysql-initdb.sh; \
		./scripts/apply-local-rocketmq-topics.sh; \
	fi
	docker compose up -d --build $(SERVICE_ID)
	./scripts/print-compose-addresses.sh

# `restart` 重启单个本地服务。
restart: check-service
	docker compose restart $(SERVICE_ID)

# `stop` 停止单个本地服务。
stop: check-service
	docker compose stop $(SERVICE_ID)

# `logs` 查看日志；带 SERVICE 时只看单个服务。
logs:
	@if [ -z "$(SERVICE_ID)" ]; then \
		docker compose logs -f; \
	else \
		docker compose logs -f --tail=200 $(SERVICE_ID); \
	fi

# `ps` 查看状态；带 SERVICE 时只看单个服务。
ps:
	@if [ -z "$(SERVICE_ID)" ]; then \
		docker compose ps; \
	else \
		docker compose ps $(SERVICE_ID); \
	fi

# `addr` 打印本地和 Docker 内网访问地址。
addr:
	./scripts/print-compose-addresses.sh

# `down` 停止并清理本地 compose 服务。
down:
	docker compose down

# `check-service` 校验单服务部署目标的 SERVICE 参数。
check-service:
	@if [ -z "$(SERVICE_ID)" ]; then \
		echo "Service is required. Example: make restart rs"; \
		echo "Aliases: gs/gateway rs/room ws/wallet us/user as/activity lucky/lgs lg/luck-gateway cs/cron game/games ns/notice mysql redis"; \
		echo "Services: $(COMPOSE_SERVICES)"; \
		exit 1; \
	fi
	@if ! printf '%s\n' $(COMPOSE_SERVICES) | grep -qx "$(SERVICE_ID)"; then \
		echo "Unknown service: $(SERVICE_ID)"; \
		echo "Aliases: gs/gateway rs/room ws/wallet us/user as/activity lucky/lgs lg/luck-gateway cs/cron game/games ns/notice mysql redis"; \
		echo "Services: $(COMPOSE_SERVICES)"; \
		exit 1; \
	fi

# `resolve-compose-conflicts` 清理旧 compose 项目遗留的固定容器名占用。
resolve-compose-conflicts:
	./scripts/resolve-compose-container-conflicts.sh

# `up-service` 是 `up SERVICE=...` 的内部实现。
up-service: check-service
	./scripts/resolve-compose-container-conflicts.sh
	./scripts/prepare-local-mysql-initdb.sh
	@if [ "$(SERVICE_ID)" != "mysql" ] && [ "$(SERVICE_ID)" != "redis" ]; then \
		./scripts/apply-local-mysql-initdb.sh; \
		./scripts/apply-local-rocketmq-topics.sh; \
	fi
	docker compose up -d $(SERVICE_ID)
	./scripts/print-compose-addresses.sh

# 服务别名只用于 `make restart rs` 这类第二参数，不执行动作。
$(SERVICE_ALIASES):
	@:
