# go-zero 生成風格
GO_ZERO_STYLE := go_zero
GO ?= go
GOFMT ?= gofmt
GOFILES := $(shell find . -name '*.go' -not -path './generate/doc-generate/*')

GO_DOC_DIR  := generate/doc-generate
GO_DOC_BIN  := $(GO_DOC_DIR)/bin/go-doc
API_ENTRY   := ./generate/api/gateway.api
DOC_OUT     := ./docs/openapi

GOCTL ?= goctl
GOCTL_PKG := github.com/zeromicro/go-zero/tools/goctl@latest
GOLANGCI_PKG := github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2

.DEFAULT_GOAL := help

help: ## 顯示可用指令
	@echo "Gateway Makefile"
	@echo ""
	@echo "首次開發："
	@echo "  make tools      安裝 goctl、goimports、golangci-lint（寫入 \$$GOPATH/bin）"
	@echo "  go mod download"
	@echo ""
	@echo "常用："
	@grep -E '^[a-zA-Z0-9_-]+:.*## ' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*## "}; {printf "  make %-14s %s\n", $$1, $$2}'

tools: ## 安裝 goctl、goimports、golangci-lint（需 Go，且 GOPATH/bin 在 PATH）
	@command -v $(GOCTL) >/dev/null 2>&1 || (echo ">> installing goctl" && $(GO) install $(GOCTL_PKG))
	@command -v goimports >/dev/null 2>&1 || (echo ">> installing goimports" && $(GO) install golang.org/x/tools/cmd/goimports@latest)
	@if ! command -v golangci-lint >/dev/null 2>&1 || ! golangci-lint version 2>/dev/null | grep -q 'version 2\.'; then \
		echo ">> installing golangci-lint v2"; \
		$(GO) install $(GOLANGCI_PKG); \
	fi
	@echo "tools OK"
	@echo "  goctl:          $$(goctl --version 2>/dev/null || echo missing)"
	@echo "  golangci-lint:  $$(golangci-lint version 2>/dev/null | head -1 || echo missing)"

gen-api: tools ## 由 .api 生成 handler / logic / types（自訂 handler 模板）
	$(GOCTL) api go -api $(API_ENTRY) -dir . -style $(GO_ZERO_STYLE) -home generate/goctl

gen-mock: ## 依 go:generate 產生 internal/model/*/mock（gomock）
	$(GO) generate ./internal/model/...

build-go-doc: ## 編譯 go-doc（OpenAPI 文件生成器）
	@echo ">> building $(GO_DOC_BIN)"
	@mkdir -p $(GO_DOC_DIR)/bin
	@cd $(GO_DOC_DIR) && $(GO) build -o bin/go-doc ./cmd/go-doc

gen-doc: build-go-doc ## 從 .api 生成 OpenAPI 3.0 YAML
	@mkdir -p $(DOC_OUT)
	$(GO_DOC_BIN) -a $(API_ENTRY) -d $(DOC_OUT) -f gateway -s openapi3.0 -y
	@echo "Generated: $(DOC_OUT)/gateway.yaml"

test: ## 執行測試
	$(GO) test ./...
fmt: ## gofmt + goimports（不含 lint）
	$(GOFMT) -s -w $(GOFILES)
	@command -v goimports >/dev/null 2>&1 && goimports -w . || (echo "goimports not found; run: make tools" && exit 1)

lint: tools ## golangci-lint 靜態檢查
	golangci-lint run ./...

lint-fix: tools ## 自動修正可修的 lint / formatter 問題（見 .golangci.yml）
	golangci-lint run --fix ./...

fix: fmt lint-fix lint ## 格式化 + 自動修 lint + 再檢查（提交前建議）

check: fix test ## 提交 / PR 前完整檢查（fmt、lint、test）

# ============================================================
# Docker compose（本機依賴）
# ============================================================

DOCKER_COMPOSE ?= docker compose
COMPOSE_FILE   := deploy/docker-compose.yml
COMPOSE        := $(DOCKER_COMPOSE) -f $(COMPOSE_FILE)

deps-up: ## 起 Mongo + Redis（最小本機依賴）
	$(COMPOSE) up -d mongo redis

deps-up-smtp: ## 起 Mongo + Redis + MailHog
	$(COMPOSE) --profile smtp up -d mongo redis mailhog

deps-down: ## 停服務（保留 volume）
	$(COMPOSE) --profile smtp --profile k6 down

deps-down-v: ## 停服務並刪 volume（會清資料）
	$(COMPOSE) --profile smtp --profile k6 down -v

deps-logs: ## 看 compose log
	$(COMPOSE) --profile smtp --profile k6 logs -f

# ============================================================
# k6 測試（test/k6/）
# ============================================================

K6                 ?= k6
K6_GATEWAY_CONFIG  := etc/gateway.k6.yaml
K6_GATEWAY_BIN     := bin/gateway-k6
K6_DIR             := test/k6
K6_PAT_FILE        := deploy/zitadel/machinekey/zitadel-admin-sa.token
K6_ENV_FILE        := deploy/zitadel/machinekey/k6.env
ZITADEL_HEALTH_URL := http://localhost:8080/debug/healthz

# k6 安裝指引（macOS / Linux）
define K6_INSTALL_HINT
k6 not found in PATH.

Install:
  macOS (Homebrew):  brew install k6
  Linux (apt):       sudo gpg -k && sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69 \
                     && echo 'deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main' | sudo tee /etc/apt/sources.list.d/k6.list \
                     && sudo apt update && sudo apt install k6
  Other:             https://grafana.com/docs/k6/latest/set-up/install-k6/

Or use docker (no install): K6='docker run --rm -i --network host -v $$PWD:/app -w /app grafana/k6:latest' make k6-smoke
endef
export K6_INSTALL_HINT

k6-check: ## 檢查 k6 是否安裝（沒裝會印 install 指引）
	@command -v $(K6) >/dev/null 2>&1 || (echo "$$K6_INSTALL_HINT"; exit 1)
	@echo "k6: $$($(K6) version 2>&1 | head -1)"

k6-up: ## 起 k6 全棧（mongo + redis + mailhog + postgres + zitadel）
	$(COMPOSE) --profile k6 up -d mongo redis mailhog postgres zitadel
	@echo "ZITADEL bootstrapping (this can take 30–90s the first time)…"
	@echo "→ run 'make k6-wait' to block until it is ready"

k6-wait: ## 等 ZITADEL ready + 把 PAT 寫到 deploy/zitadel/machinekey/k6.env
	@echo "waiting for ZITADEL at $(ZITADEL_HEALTH_URL)…"
	@for i in $$(seq 1 120); do \
		if curl -fsS $(ZITADEL_HEALTH_URL) >/dev/null 2>&1; then \
			echo "zitadel ready ($$i s)"; break; \
		fi; \
		sleep 1; \
		if [ $$i -eq 120 ]; then echo "zitadel did not become ready in 120s"; exit 1; fi; \
	done
	@for i in $$(seq 1 30); do \
		if [ -s "$(K6_PAT_FILE)" ]; then break; fi; \
		sleep 1; \
	done
	@if [ ! -s "$(K6_PAT_FILE)" ]; then \
		echo "PAT file $(K6_PAT_FILE) missing — check 'docker logs gateway-zitadel'"; \
		exit 1; \
	fi
	@PAT=$$(tr -d '\n' < $(K6_PAT_FILE)); \
		printf 'export ZITADEL_SERVICE_TOKEN=%s\nexport BASE_URL=http://localhost:8888\nexport MAILHOG_URL=http://localhost:8025\nexport REDIS_ADDR=localhost:6379\n' "$$PAT" > $(K6_ENV_FILE); \
		echo "wrote $(K6_ENV_FILE)"
	@$(MAKE) -s k6-seed-fixtures
	@echo "tip: 'source $(K6_ENV_FILE)' to load into your shell"

# k6-seed-fixtures idempotently upserts the k6-tenant + K6INVITE invite code
# into gateway_k6 so /auth/register resolves successfully. The invite hash is
# sha256("K6INVITE") — see internal/model/auth/domain/const.go HashInviteCode.
# Uses mongosh inside the container; no extra Go binary needed.
K6_TENANT_ID    := k6-tenant
K6_INVITE_HASH  := e62a291f0dcf88c50c91fdc78e3539c55e1e20ef645c640b1ec778fe4fabb4cb
k6-seed-fixtures: ## upsert k6-tenant + K6INVITE 到 mongo
	@docker exec gateway-mongo mongosh --quiet --eval '\
db = db.getSiblingDB("gateway_k6"); \
var now = NumberLong(Date.now()); \
var t = db.tenants.updateOne( \
  {tenant_id: "$(K6_TENANT_ID)"}, \
  {$$set: {tenant_id: "$(K6_TENANT_ID)", slug: "$(K6_TENANT_ID)", name: "k6 Tenant", uid_prefix: "K6", status: "active", update_at: now}, $$setOnInsert: {create_at: now}}, \
  {upsert: true} \
); \
var i = db.invite_codes.updateOne( \
  {tenant_id: "$(K6_TENANT_ID)", code_hash: "$(K6_INVITE_HASH)"}, \
  {$$set: {tenant_id: "$(K6_TENANT_ID)", code_hash: "$(K6_INVITE_HASH)", max_uses: NumberLong(1000000), expires_at: NumberLong(0), new_users_only: false, update_at: now}, $$setOnInsert: {used_count: NumberLong(0), create_at: now}}, \
  {upsert: true} \
); \
print("tenant matched=" + t.matchedCount + " upserted=" + (t.upsertedId?1:0) + ", invite matched=" + i.matchedCount + " upserted=" + (i.upsertedId?1:0));' && \
	echo "seeded fixtures (tenant=$(K6_TENANT_ID) invite=K6INVITE) into gateway_k6"

# Back-compat alias
k6-seed-tenant: k6-seed-fixtures ## (alias for k6-seed-fixtures)

k6-build: ## 建 gateway binary 給 k6 使用
	@mkdir -p $(dir $(K6_GATEWAY_BIN))
	$(GO) build -o $(K6_GATEWAY_BIN) ./gateway.go

k6-gateway: k6-build ## 前景啟 gateway（吃 etc/gateway.k6.yaml + ZITADEL env）
	@if [ ! -s "$(K6_ENV_FILE)" ]; then echo "run 'make k6-wait' first"; exit 1; fi
	@set -a; . $(K6_ENV_FILE); set +a; \
		$(K6_GATEWAY_BIN) -f $(K6_GATEWAY_CONFIG)

k6-smoke: k6-check ## 跑 smoke 測試（每個端點一發）
	@if [ ! -s "$(K6_ENV_FILE)" ]; then echo "run 'make k6-wait' first"; exit 1; fi
	@set -a; . $(K6_ENV_FILE); set +a; \
		for f in $(K6_DIR)/smoke/*.js; do \
			echo "==> $$f"; $(K6) run "$$f" || exit 1; \
		done

k6-journey: k6-check ## 跑 journey 測試（多步驟流程）
	@if [ ! -s "$(K6_ENV_FILE)" ]; then echo "run 'make k6-wait' first"; exit 1; fi
	@set -a; . $(K6_ENV_FILE); set +a; \
		for f in $(K6_DIR)/journeys/*.js; do \
			echo "==> $$f"; $(K6) run "$$f" || exit 1; \
		done

k6-all: k6-smoke k6-journey ## smoke + journey

k6-seed-admin: k6-build ## 註冊 k6-admin 並 seed tenant_admin role（rbac journey 用）
	@if [ ! -s "$(K6_ENV_FILE)" ]; then echo "run 'make k6-wait' first"; exit 1; fi
	@mkdir -p bin
	$(GO) build -o bin/k6-seed-admin ./cmd/k6-seed-admin
	@set -a; . $(K6_ENV_FILE); set +a; \
		./bin/k6-seed-admin > $(K6_ENV_FILE).admin || (echo "k6-seed-admin failed"; exit 1); \
		cat $(K6_ENV_FILE).admin >> $(K6_ENV_FILE); \
		echo "admin credentials appended to $(K6_ENV_FILE):"; \
		cat $(K6_ENV_FILE).admin

k6-down: ## 停 k6 stack 並清 volume（會清 Postgres / Mongo / Redis 資料）
	$(COMPOSE) --profile k6 down -v
	@rm -f $(K6_PAT_FILE) $(K6_ENV_FILE) $(K6_ENV_FILE).admin $(K6_ENV_FILE).tmp deploy/zitadel/machinekey/zitadel-admin-sa.json
	@echo "k6 stack stopped, volumes & PAT removed"

# ============================================================
# 一鍵本機測試環境（Docker + Gateway + 種子資料）
# ============================================================

DEV_DIR            := .dev
DEV_GATEWAY_PID    := $(DEV_DIR)/gateway.pid
DEV_GATEWAY_LOG    := $(DEV_DIR)/gateway.log

.PHONY: dev-up dev-down dev-status dev-restart-gateway

dev-up: k6-up k6-wait k6-build ## 一鍵起全套：mongo/redis/mailhog/zitadel + seed + Gateway 背景
	@mkdir -p $(DEV_DIR)
	@if [ -f $(DEV_GATEWAY_PID) ] && kill -0 $$(cat $(DEV_GATEWAY_PID)) 2>/dev/null; then \
		echo "gateway already running (pid $$(cat $(DEV_GATEWAY_PID)))"; \
	else \
		set -a; . $(K6_ENV_FILE); set +a; \
		nohup $(K6_GATEWAY_BIN) -f $(K6_GATEWAY_CONFIG) > $(DEV_GATEWAY_LOG) 2>&1 & \
		echo $$! > $(DEV_GATEWAY_PID); \
		echo "gateway starting (pid $$(cat $(DEV_GATEWAY_PID)), log $(DEV_GATEWAY_LOG))…"; \
		for i in $$(seq 1 30); do \
			if curl -fsS http://localhost:8888/api/v1/health >/dev/null 2>&1; then \
				echo "gateway ready ($$i s)"; break; \
			fi; \
			sleep 1; \
			if [ $$i -eq 30 ]; then \
				echo "gateway did not become ready — tail $(DEV_GATEWAY_LOG)"; \
				tail -20 $(DEV_GATEWAY_LOG); exit 1; \
			fi; \
		done; \
	fi
	@echo ""
	@echo "=========================================="
	@echo " 本機測試環境已就緒"
	@echo "=========================================="
	@echo " Gateway API    http://localhost:8888"
	@echo " 前端（另開終端） make frontend-dev  →  http://localhost:5173"
	@echo " MailHog 收信   http://localhost:8025  （註冊 OTP 在這裡看）"
	@echo " ZITADEL 主控台 http://localhost:8080/ui/console"
	@echo ""
	@echo " 註冊預設：租戶 k6-tenant · 邀請碼 K6INVITE"
	@echo " Email 可填任意地址（例：you@test.com），OTP 不會進真實信箱"
	@echo ""
	@echo " 管理後台：make dev-seed-admin 後用輸出的帳密登入"
	@echo " 關閉環境：make dev-down"
	@echo " 查看狀態：make dev-status"
	@echo "=========================================="

dev-seed-admin: k6-seed-admin ## 建立具 tenant_admin 的管理員（dev-up 之後執行）

dev-down: ## 停 Gateway 背景行程 + k6 docker stack
	@if [ -f $(DEV_GATEWAY_PID) ]; then \
		pid=$$(cat $(DEV_GATEWAY_PID)); \
		if kill -0 $$pid 2>/dev/null; then kill $$pid && echo "stopped gateway (pid $$pid)"; fi; \
		rm -f $(DEV_GATEWAY_PID); \
	fi
	@$(MAKE) -s k6-down
	@rm -rf $(DEV_DIR)
	@echo "dev environment stopped"

dev-status: ## 顯示 docker / gateway / health 狀態
	@echo "=== docker (k6 profile) ==="
	@$(COMPOSE) --profile k6 ps 2>/dev/null || true
	@echo ""
	@echo "=== gateway :8888 ==="
	@if [ -f $(DEV_GATEWAY_PID) ] && kill -0 $$(cat $(DEV_GATEWAY_PID)) 2>/dev/null; then \
		echo "running pid $$(cat $(DEV_GATEWAY_PID))"; \
		curl -fsS http://localhost:8888/api/v1/health 2>/dev/null | head -c 200 || echo "(health check failed)"; \
		echo ""; \
	else \
		echo "not running (run: make dev-up)"; \
	fi

dev-restart-gateway: k6-build ## 只重啟 Gateway（docker 不動）
	@if [ -f $(DEV_GATEWAY_PID) ]; then \
		pid=$$(cat $(DEV_GATEWAY_PID)); \
		kill -0 $$pid 2>/dev/null && kill $$pid || true; \
		rm -f $(DEV_GATEWAY_PID); \
	fi
	@mkdir -p $(DEV_DIR)
	@set -a; . $(K6_ENV_FILE); set +a; \
		nohup $(K6_GATEWAY_BIN) -f $(K6_GATEWAY_CONFIG) > $(DEV_GATEWAY_LOG) 2>&1 & \
		echo $$! > $(DEV_GATEWAY_PID); \
		echo "gateway restarted (pid $$(cat $(DEV_GATEWAY_PID)))"

# ============================================================
# Frontend（使用者前台 + 管理後台）
# ============================================================

frontend-install: ## 安裝 frontend 依賴
	cd frontend && npm install

frontend-dev: frontend-install ## 啟動前端 dev server（:5173，proxy /api → :8888）
	cd frontend && npm run dev

frontend-build: frontend-install ## 建置前端靜態檔 → frontend/dist/
	cd frontend && npm run build