# ========= Makefile (optimized + DRY + annotated) =========

# --- strict shell & make behavior ---
SHELL := $(shell command -v bash 2>/dev/null || echo /bin/sh)
.SHELLFLAGS := -eu -o pipefail -c

.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
.ONESHELL:

# --- variables ---
GOCMD        ?= go
GOINSTALL    := $(GOCMD) install
BIN          ?= autoglue
MAIN         ?= main.go
UI_DIR       ?= ui
UI_DEST_DIR  ?= internal/web

# Module path (used for ldflags to internal/version)
GIT_HOST     ?= github.com
GIT_USER     ?= glueops
MODULE_PATH  ?= $(GIT_HOST)/$(GIT_USER)/$(BIN)

# SDK / module settings (Go)
SDK_REPO     ?= $(BIN)-sdk-go               # repo name used for module path
SDK_OUTDIR   ?= sdk/go                      # output directory (inside repo)
SDK_PKG      ?= ${BIN}                      # package name inside the SDK

UI_SSG_ROUTES ?= /,/login,/docs,/pricing

# Go versioning (go.mod uses major.minor; you’re on 1.25.4)
GO_VERSION   ?= 1.25.4

# SDK / package settings (TypeScript)
SDK_TS_OUTDIR     ?= sdk/ts
SDK_TS_GEN        ?= typescript-fetch
SDK_TS_NPM_NAME   ?= @glueops/$(SDK_REPO)
SDK_TS_NPM_VER    ?= 0.1.0
SDK_TS_DIR        := $(abspath $(SDK_TS_OUTDIR))
SDK_TS_PROPS      ?= supportsES6=true,typescriptThreePlus=true,useSingleRequestParameter=true,withSeparateModelsAndApi=true,modelPropertyNaming=original,enumPropertyNaming=original,useUnionTypes=true
SDK_TS_PROPS_FLAGS := $(foreach p,$(subst , ,$(SDK_TS_PROPS)),-p $(p))

# Path for vendored UI SDK (absolute, path-safe)
SDK_TS_UI_OUTDIR ?= ui/src/sdk
SDK_TS_UI_DIR    := $(abspath $(SDK_TS_UI_OUTDIR))

SWAG         := $(shell command -v swag 2>/dev/null)
GMU          := $(shell command -v go-mod-upgrade 2>/dev/null)
YARN         := $(shell command -v yarn 2>/dev/null)
NPM          := $(shell command -v npm 2>/dev/null)
BROTLI       := $(shell command -v brotli 2>/dev/null)
GZIP         := $(shell command -v gzip 2>/dev/null)

# OpenAPI Generator wrapper (npm) and *core* version pin
OGC_WRAPPER                ?= @openapitools/openapi-generator-cli@latest
OPENAPI_GENERATOR_VERSION  ?= 7.17.0
OGC_BIN                    := npx -y $(OGC_WRAPPER)

# Cache the core generator jar (faster CI/local)
export OPENAPI_GENERATOR_CLI_CACHE_DIR ?= $(HOME)/.openapi-generator

# Toggle alias-as-model (can trigger name collisions in some specs)
ALIAS_AS_MODEL ?= true
ALIAS_FLAG     := $(if $(filter true,$(ALIAS_AS_MODEL)),--generate-alias-as-model,)

# Post-process generated Go with gofmt (OpenAPI Generator honors this env var)
export GO_POST_PROCESS_FILE := gofmt -w

# Default goal
.DEFAULT_GOAL := help

# --- version metadata (ldflags) ---
VERSION  := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT   := $(shell git rev-parse HEAD 2>/dev/null || echo "none")
DATE     := $(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
BUILT_BY := $(shell whoami)

LDFLAGS := -X '$(MODULE_PATH)/internal/version.Version=$(VERSION)' \
           -X '$(MODULE_PATH)/internal/version.Commit=$(COMMIT)' \
           -X '$(MODULE_PATH)/internal/version.Date=$(DATE)' \
           -X '$(MODULE_PATH)/internal/version.BuiltBy=$(BUILT_BY)'

# --- whitespace trimming helper ---
trim = $(strip $1)

# sanitized copies (use these everywhere in recipes)
SDK_OUTDIR_CLEAN    := $(call trim,$(SDK_OUTDIR))
SDK_TS_DIR_CLEAN    := $(call trim,$(SDK_TS_DIR))
SDK_TS_UI_DIR_CLEAN := $(call trim,$(SDK_TS_UI_DIR))
GIT_HOST_CLEAN      := $(call trim,$(GIT_HOST))
GIT_USER_CLEAN      := $(call trim,$(GIT_USER))
SDK_REPO_CLEAN      := $(call trim,$(SDK_REPO))
SDK_PKG_CLEAN       := $(call trim,$(SDK_PKG))

# --- phony targets ---
.PHONY: all prepare ui-install ui-build ui swagger build clean fmt vet tidy upgrade \
        sdk sdk-go sdk-ts sdk-ts-ui sdk-all help dev ui-compress print-version \
        validate-spec check-tags doctor diff-swagger

# --- inputs/outputs for swagger (incremental) ---
DOCS_JSON := docs/swagger.json
DOCS_YAML := docs/swagger.yaml
# Prefer git for speed; fall back to find. Exclude UI dir.
#GO_SRCS := $(shell (git ls-files '*.go' ':!$(UI_DIR)/**' 2>/dev/null || find . -name '*.go' -not -path './$(UI_DIR)/*' -type f))
GO_SRCS := $(shell ( \
  git ls-files '*.go' ':!$(UI_DIR)/**' ':!docs/**' ':!sdk/**' 2>/dev/null \
  || find . -name '*.go' -not -path './$(UI_DIR)/*' -not -path './docs/*' -type f \
))

# Rebuild swagger when Go sources change
$(DOCS_JSON) $(DOCS_YAML): $(GO_SRCS)
	@echo ">> Generating Swagger docs..."
	@if ! command -v swag >/dev/null 2>&1; then \
		echo "Installing swag/v2 CLI @v2.0.0-rc4..."; \
		$(GOINSTALL) github.com/swaggo/swag/v2/cmd/swag@v2.0.0-rc4; \
	fi
	@rm -rf docs/swagger.* docs/docs.go
	@swag init -g $(MAIN) -o docs

# --- spec validation + tag guard ---
validate-spec: $(DOCS_JSON) ## Validate docs/swagger.json and pin the core OpenAPI Generator version
	@$(OGC_BIN) version-manager set "$(OPENAPI_GENERATOR_VERSION)"
	@$(OGC_BIN) version
	@$(OGC_BIN) validate -i $(DOCS_JSON)
	@echo ">> Spec valid."

check-tags: $(DOCS_JSON) ## Check that Swagger tags contain no spaces/slashes (jq optional)
	@echo ">> Checking tags for invalid characters (spaces or slashes)"
	@if command -v jq >/dev/null 2>&1; then \
		! jq -r '..|.tags? // empty | .[]' $(DOCS_JSON) | grep -Eq '[ /]'; \
	else \
		echo "jq not found; skipping tag check (install jq to enable)"; \
	fi

# Optional: quick diff between JSON & YAML swagger (nice for drift)
diff-swagger: $(DOCS_JSON) $(DOCS_YAML) ## Show diff between swagger.json and swagger.yaml (requires yq)
	@command -v yq >/dev/null 2>&1 || { echo "yq not found; brew install yq"; exit 1; }
	@diff -u <(jq -S . $(DOCS_JSON)) <(yq -o=json -S '.' $(DOCS_YAML)) || true

# --- meta targets ---
all: build ## Default meta-target: build everything
prepare: fmt vet tidy upgrade ## go fmt, vet, tidy, and upgrade module dependencies

# --- go hygiene ---
fmt: ## go fmt ./...
	@$(GOCMD) fmt ./...

vet: ## go vet ./...
	@$(GOCMD) vet ./...

tidy: ## go mod tidy
	@$(GOCMD) mod tidy

upgrade: ## Upgrade module requirements with go-mod-upgrade (best-effort)
	@echo ">> Checking go-mod-upgrade..."
	@if [ -z "$(GMU)" ]; then \
		echo "Installing go-mod-upgrade..."; \
		$(GOINSTALL) github.com/oligot/go-mod-upgrade@latest; \
	fi
	@go-mod-upgrade -f || true

# --- ui ---
ui-install: ## Install frontend dependencies (yarn or npm)
	@echo ">> Installing UI deps in $(UI_DIR)..."
	@if [ -n "$(YARN)" ]; then \
		cd $(UI_DIR) && yarn install --frozen-lockfile; \
	elif [ -n "$(NPM)" ]; then \
		cd $(UI_DIR) && npm ci; \
	else \
		echo "Error: neither yarn nor npm is installed." >&2; exit 1; \
	fi

ui-build: ui-install ## Build frontend (Vite)
	@echo ">> Building UI in $(UI_DIR)..."
	@rm -rf $(UI_DEST_DIR)/dist
	@if [ -n "$(YARN)" ]; then \
		cd $(UI_DIR) && yarn build; \
	else \
		cd $(UI_DIR) && npm run build; \
	fi

ui-compress: ui-build ## Precompress UI assets with brotli/gzip if available
	@echo ">> Precompressing assets (brotli + gzip) in $(UI_DEST_DIR)/dist"
	@if [ -n "$(BROTLI)" ]; then \
		find "$(UI_DEST_DIR)/dist" -type f \( -name '*.js' -o -name '*.css' -o -name '*.html' \) -print0 | \
		 xargs -0 -I{} brotli -f {}; \
	else echo "brotli not found; skipping .br"; fi
	@if [ -n "$(GZIP)" ]; then \
		find "$(UI_DEST_DIR)/dist" -type f \( -name '*.js' -o -name '*.css' -o -name '*.html' \) -print0 | \
		 xargs -0 -I{} gzip -kf {}; \
	else echo "gzip not found; skipping .gz"; fi

ui: ui-compress ## Build and precompress UI

# --- swagger convenience phony (kept for UX) ---
.PHONY: swagger
swagger: $(DOCS_JSON) ## Generate Swagger docs if stale
	@true

# --- build ---
build: prepare ui swagger sdk-all ## Build everything: Go hygiene, UI, Swagger, SDKs, then Go binary
	@echo ">> Building Go binary: $(BIN)"
	@$(GOCMD) build -trimpath -ldflags "$(LDFLAGS)" -o $(BIN) $(MAIN)

# Handy: print resolved version metadata
print-version: ## Print ldflags/version metadata
	@echo "VERSION  = $(VERSION)"
	@echo "COMMIT   = $(COMMIT)"
	@echo "DATE     = $(DATE)"
	@echo "BUILT_BY = $(BUILT_BY)"
	@echo "LDFLAGS  = $(LDFLAGS)"

# --- development ---
dev: ui-install swagger ## Run Vite dev server and Go API (serve)
	@echo ">> Starting Vite (frontend) and Go API (backend) with dev env..."
	@cd $(UI_DIR) && \
	( \
		if command -v yarn >/dev/null 2>&1; then \
			yarn dev & \
		elif command -v npm >/dev/null 2>&1; then \
			npm run dev & \
		else \
			echo "Error: neither yarn nor npm is installed." >&2; exit 1; \
		fi; \
		cd .. && \
		$(GOCMD) run . serve & \
		wait \
	)

# --- shared generator flags ---
GEN_COMMON := --enable-post-process-file $(ALIAS_FLAG) -i $(DOCS_JSON)

# --- DRY macro for openapi-generator generate ---
# Usage: $(call OGC_GENERATE,<generator>,<outdir>,<extra flags>)
define OGC_GENERATE
	$(OGC_BIN) generate \
		$(GEN_COMMON) \
		-g $(1) \
		-o $(2) \
		$(3)
endef

# Convenience bundles
OAG_GIT_PROPS := --git-host "$(GIT_HOST_CLEAN)" --git-user-id "$(GIT_USER_CLEAN)" --git-repo-id "$(SDK_REPO_CLEAN)"
TS_PROPS      := -p npmName=$(SDK_TS_NPM_NAME) -p npmVersion=$(SDK_TS_NPM_VER) $(SDK_TS_PROPS_FLAGS)

# --- sdk generation (Go) ---
sdk-go: $(DOCS_JSON) validate-spec check-tags ## Generate Go SDK + tidy module
	@echo ">> Generating Go SDK (module $(GIT_HOST_CLEAN)/$(GIT_USER_CLEAN)/$(SDK_REPO_CLEAN), Go $(GO_VERSION))..."
	@rm -rf "$(SDK_OUTDIR_CLEAN)"; mkdir -p "$(SDK_OUTDIR_CLEAN)"
	@$(call OGC_GENERATE,go,$(SDK_OUTDIR_CLEAN),--additional-properties=packageName=$(SDK_PKG_CLEAN) $(OAG_GIT_PROPS))
	@cd "$(SDK_OUTDIR_CLEAN)"; \
	$(GOCMD) mod edit -go=$(GO_VERSION); \
	$(GOCMD) mod tidy

# --- sdk generation (TypeScript) ---
sdk-ts: $(DOCS_JSON) validate-spec check-tags ## Generate TypeScript SDK, format, and build
	@echo ">> Generating TypeScript SDK in $(SDK_TS_DIR_CLEAN)"
	@rm -rf "$(SDK_TS_DIR_CLEAN)"; mkdir -p "$(SDK_TS_DIR_CLEAN)"
	@$(call OGC_GENERATE,$(SDK_TS_GEN),$(SDK_TS_DIR_CLEAN),$(TS_PROPS))
	@if command -v npx >/dev/null 2>&1; then \
		echo ">> Prettier: formatting generated TS SDK"; \
		cd "$(SDK_TS_DIR_CLEAN)" && npx --yes prettier -w . || true; \
	fi
	@echo ">> Installing & building TS SDK in $(SDK_TS_DIR_CLEAN)"
	@if command -v yarn >/dev/null 2>&1; then \
		cd "$(SDK_TS_DIR_CLEAN)" && yarn install --frozen-lockfile || true; \
		cd "$(SDK_TS_DIR_CLEAN)" && yarn build || true; \
	elif command -v npm >/dev/null 2>&1; then \
		cd "$(SDK_TS_DIR_CLEAN)" && npm ci || npm install || true; \
		cd "$(SDK_TS_DIR_CLEAN)" && npm run build || true; \
	else \
		echo "Warning: neither yarn nor npm is installed; skipping install/build for TS SDK."; \
	fi

# --- sdk generation (TypeScript into UI/src) ---
sdk-ts-ui: $(DOCS_JSON) validate-spec check-tags ## Generate TS SDK directly into UI src (no package files)
	@echo ">> Generating TypeScript SDK directly into UI source: $(SDK_TS_UI_DIR_CLEAN)"
	@rm -rf "$(SDK_TS_UI_DIR_CLEAN)"; mkdir -p "$(SDK_TS_UI_DIR_CLEAN)"
	@$(call OGC_GENERATE,typescript-fetch,$(SDK_TS_UI_DIR_CLEAN),$(TS_PROPS))
	@if [ -d "$(SDK_TS_UI_DIR_CLEAN)/src" ]; then \
		mv "$(SDK_TS_UI_DIR_CLEAN)/src/"* "$(SDK_TS_UI_DIR_CLEAN)/"; \
		rm -rf "$(SDK_TS_UI_DIR_CLEAN)/src"; \
	fi
	@rm -f "$(SDK_TS_UI_DIR_CLEAN)/package.json" "$(SDK_TS_UI_DIR_CLEAN)/tsconfig.json" "$(SDK_TS_UI_DIR_CLEAN)/README.md"

# --- convenience ---
sdk-all: sdk-go sdk-ts sdk-ts-ui ## Generate Go + TS SDKs (tip: run with "make -j sdk-all" for parallel)
sdk: sdk-go ## Alias for sdk-go

# --- clean/help ---
clean: ## Clean build artifacts, Swagger outputs, UI dist, and SDKs
	@echo ">> Cleaning artifacts..."
	@rm -rf "$(BIN)" docs/swagger.* docs/docs.go \
		"$(UI_DEST_DIR)/dist" "$(UI_DIR)/dist" "$(UI_DIR)/node_modules" \
		"$(SDK_OUTDIR_CLEAN)" "$(SDK_TS_DIR_CLEAN)" "$(SDK_TS_UI_DIR_CLEAN)"

doctor: ## Print environment diagnostics (shell, versions, generator availability)
	@echo ">> Make is using shell: $(SHELL)"
	@{ \
		echo ">> Detected runtime shell process: $$0"; \
		command -v bash >/dev/null 2>&1 || { echo "bash not found. On macOS: brew install bash"; exit 1; }; \
		echo ">> Versions:"; \
		bash --version | head -1 || true; \
		go version || true; \
		node -v || true; \
		npm -v || true; \
		npx -v || true; \
		jq --version || true; \
		echo ">> OpenAPI Generator (wrapper) available via npx:"; \
		$(OGC_BIN) version || true; \
	}

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

# ========= end Makefile =========
