# ──────────────────────────────────────────────────────────────
# NNTmux – Docker / Sail convenience targets
# ──────────────────────────────────────────────────────────────
#
# Run `make` or `make help` to see all available targets.
# See DOCKER.md for full documentation.
#
# Common flags:
#   FORCE=1         Skip confirmation prompts on destructive targets
#   MAINTENANCE=1   Wrap `upgrade` migrations in artisan down/up
#   SERVICE=name    Restrict a target (e.g. logs) to a single service
#   CMD="…"         Free-form command for `artisan`, `exec`, etc.
#
# Requires Docker Compose v2.22+ (uses `pull --ignore-buildable`).
# ──────────────────────────────────────────────────────────────
.DEFAULT_GOAL := help
# Source SEARCH_DRIVER from .env and export as COMPOSE_PROFILES
# so the correct search engine container starts automatically.
-include .env
export COMPOSE_PROFILES ?= $(SEARCH_DRIVER)
SAIL           := ./sail
DOCKER_COMPOSE := docker compose
# Optional parameters with safe defaults
SERVICE     ?=
CMD         ?=
FORCE       ?=
MAINTENANCE ?=
# Colors
CYAN   := \033[36m
GREEN  := \033[32m
YELLOW := \033[33m
RED    := \033[31m
BOLD   := \033[1m
RESET  := \033[0m
# ── Confirmation helper ──────────────────────────────────────
# Usage: $(call confirm,Are you sure?)
# Honours FORCE=1 for non-interactive / CI usage.
define confirm
	@if [ "$(FORCE)" = "1" ]; then \
		echo "$(YELLOW)⚠  FORCE=1 set — skipping confirmation.$(RESET)"; \
	else \
		printf "$(YELLOW)⚠  $(1) [y/N] $(RESET)"; \
		read confirm; [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ] || (echo "Aborted." && exit 1); \
	fi
endef
# ── Pre-flight ───────────────────────────────────────────────
.PHONY: check-env
check-env: ## Verify .env exists before running docker targets
	@if [ ! -f .env ]; then \
		echo "$(RED)✘ .env not found. Run: cp .env.example .env$(RESET)"; \
		exit 1; \
	fi
# ── Lifecycle ────────────────────────────────────────────────
.PHONY: up
up: check-env ## Start all services in the background
	@$(SAIL) up -d
.PHONY: down
down: ## Stop all services
	@$(SAIL) down
.PHONY: stop
stop: down ## Alias for 'down'
.PHONY: restart
restart: ## Restart all services
	@$(SAIL) restart
.PHONY: recreate
recreate: check-env ## Force-recreate containers without rebuilding
	@$(SAIL) up -d --force-recreate --remove-orphans
.PHONY: build
build: check-env ## Build the app image (cached layers OK)
	@$(SAIL) build
.PHONY: rebuild
rebuild: check-env pull ## Rebuild from scratch with fresh base images and recreate
	@$(SAIL) build --no-cache --pull
	@$(SAIL) up -d --force-recreate --remove-orphans
	@echo "$(GREEN)✔ Rebuild complete.$(RESET)"
.PHONY: pull
pull: check-env ## Pull latest enabled-profile base images (skips buildable)
	@$(DOCKER_COMPOSE) pull --ignore-buildable
.PHONY: update
update: check-env pull ## Infra-only: pull base images, rebuild with --pull, restart
	@$(SAIL) build --pull
	@$(SAIL) up -d --remove-orphans
	@echo "$(GREEN)✔ Infra updated.$(RESET)"
.PHONY: upgrade
upgrade: update fix-permissions composer-install npm-build ## Full app upgrade: update + perms + composer + npm + migrate + caches
	@if [ "$(MAINTENANCE)" = "1" ]; then $(SAIL) artisan down; fi
	@$(SAIL) artisan migrate --force
	@if [ "$(MAINTENANCE)" = "1" ]; then $(SAIL) artisan up; fi
	@$(MAKE) data-cache
	@$(MAKE) optimize
	@echo "$(GREEN)✔ Upgrade complete.$(RESET)"
.PHONY: fresh
fresh: check-env ## Destroy ALL volumes, rebuild, and start clean (DATA LOSS!)
	$(call confirm,This will destroy all Docker volumes (database, redis, search index). Continue?)
	@$(SAIL) down -v
	@$(DOCKER_COMPOSE) pull --ignore-buildable
	@$(SAIL) build --no-cache --pull
	@$(SAIL) up -d --force-recreate --remove-orphans
	@echo "$(GREEN)✔ Fresh environment is up. Run 'make artisan cmd=nntmux:install' to initialise.$(RESET)"
# ── Shell Access ─────────────────────────────────────────────
.PHONY: shell
shell: ## Open a bash shell in the app container
	@$(SAIL) shell
.PHONY: root-shell
root-shell: ## Open a root bash shell in the app container
	@$(SAIL) root-shell
.PHONY: tinker
tinker: ## Open a Laravel Tinker session
	@$(SAIL) tinker
# ── Artisan / PHP ────────────────────────────────────────────
.PHONY: artisan
artisan: ## Run an artisan command (usage: make artisan cmd="migrate")
	@$(SAIL) artisan $(cmd)$(CMD)
.PHONY: migrate
migrate: ## Run pending database migrations
	@$(SAIL) artisan migrate --force
.PHONY: migrate-fresh
migrate-fresh: ## Drop all tables and re-run migrations (DATA LOSS!)
	$(call confirm,This will DROP ALL TABLES and re-run every migration. Continue?)
	@$(SAIL) artisan migrate:fresh --force
.PHONY: seed
seed: ## Run database seeders
	@$(SAIL) artisan db:seed --force
.PHONY: cache-clear
cache-clear: ## Clear config / route / view / application caches (and stale bootstrap/cache files)
	@$(SAIL) artisan config:clear
	@$(SAIL) artisan route:clear
	@$(SAIL) artisan view:clear
	@$(SAIL) artisan cache:clear
	@rm -f bootstrap/cache/config.php bootstrap/cache/routes-v7.php bootstrap/cache/services.php bootstrap/cache/packages.php
	@echo "$(GREEN)✔ Caches cleared.$(RESET)"
.PHONY: optimize
optimize: ## Dev-safe optimize: warm data structures + view cache only (no config/route cache)
	@$(SAIL) artisan view:cache
	@$(SAIL) artisan data:cache-structures
	@echo "$(GREEN)✔ Optimized (dev-safe).$(RESET)"
.PHONY: optimize-deploy
optimize-deploy: ## Production optimize: cache config / routes / views + data structures (bakes absolute paths!)
	@echo "$(YELLOW)⚠  This caches absolute paths from inside the container — only run on the deploy host.$(RESET)"
	@$(SAIL) artisan config:cache
	@$(SAIL) artisan route:cache
	@$(SAIL) artisan view:cache
	@$(SAIL) artisan data:cache-structures
	@echo "$(GREEN)✔ Optimized for deploy.$(RESET)"
.PHONY: queue-work
queue-work: ## Run a foreground queue worker (Ctrl-C to stop)
	@$(SAIL) artisan queue:work
.PHONY: queue-restart
queue-restart: ## Signal all queue workers to restart gracefully
	@$(SAIL) artisan queue:restart
.PHONY: tmux-start
tmux-start: ## Start the NNTmux tmux processing engine
	@$(SAIL) artisan tmux:start
.PHONY: tmux-stop
tmux-stop: ## Stop the NNTmux tmux processing engine
	@$(SAIL) artisan tmux:stop
.PHONY: tmux-attach
tmux-attach: ## Attach to the running tmux session
	@$(SAIL) artisan tmux:attach
.PHONY: horizon
horizon: ## Show Horizon status
	@$(SAIL) artisan horizon:status
# ── Testing & Quality ────────────────────────────────────────
.PHONY: test
test: ## Run the PHPUnit test suite (usage: make test filter=TestName)
	@$(SAIL) test $(if $(filter),--filter=$(filter),)
.PHONY: pint
pint: ## Run Laravel Pint code formatter on dirty files
	@$(SAIL) pint --dirty
.PHONY: pint-all
pint-all: ## Run Laravel Pint on all files
	@$(SAIL) pint
.PHONY: phpstan
phpstan: ## Run PHPStan static analysis
	@$(SAIL) php vendor/bin/phpstan analyse --memory-limit=2G
.PHONY: rector
rector: ## Run Rector in dry-run mode (no changes written)
	@$(SAIL) php vendor/bin/rector process --dry-run
.PHONY: rector-fix
rector-fix: ## Apply Rector refactorings
	@$(SAIL) php vendor/bin/rector process
# ── Frontend ─────────────────────────────────────────────────
.PHONY: npm-build
npm-build: ## Run npm install and build inside the container
	@$(SAIL) npm install
	@$(SAIL) npm run build
.PHONY: npm-dev
npm-dev: ## Start Vite dev server inside the container
	@$(SAIL) npm run dev
.PHONY: ts-types
ts-types: ## Regenerate TypeScript types from PHP DTOs/Enums
	@$(SAIL) artisan typescript:transform
.PHONY: ts-types-check
ts-types-check: ## CI: regenerate TS types and fail if working tree drifts
	@$(SAIL) artisan typescript:transform --quiet
	@git diff --exit-code resources/js/types/generated.d.ts \
		|| (echo "❌ resources/js/types/generated.d.ts is out of date — run 'make ts-types' and commit." && exit 1)
.PHONY: data-cache
data-cache: ## Cache spatie/laravel-data structures (run on deploy)
	@$(SAIL) artisan data:cache-structures
# ── Dependencies ─────────────────────────────────────────────
.PHONY: composer-install
composer-install: ## Run composer install inside the container
	@$(SAIL) composer install
.PHONY: composer-update
composer-update: ## Run composer update inside the container
	@$(SAIL) composer update
# ── Database / Services ──────────────────────────────────────
.PHONY: db
db: ## Open a MariaDB CLI session
	@$(SAIL) mariadb
.PHONY: redis-cli
redis-cli: ## Open a Redis CLI session
	@$(SAIL) redis
# Host UID/GID — used by fix-permissions so files stay writable from BOTH
# the host and the in-container sail user (whose UID should match WWWUSER).
HOST_UID ?= $(shell id -u)
HOST_GID ?= $(shell id -g)
.PHONY: fix-permissions
fix-permissions: ## Align UIDs: remap sail to host UID, chown project, register git safe.directory
	@echo "$(YELLOW)→ Remapping container 'sail' user to UID $(HOST_UID) and chowning /var/www/html…$(RESET)"
	@$(DOCKER_COMPOSE) exec -u root laravel.test usermod -u $(HOST_UID) -o sail >/dev/null 2>&1 || true
	@$(DOCKER_COMPOSE) exec -u root laravel.test groupmod -g $(HOST_GID) -o sail >/dev/null 2>&1 || true
	@$(DOCKER_COMPOSE) exec -u root laravel.test chown -R $(HOST_UID):$(HOST_GID) /var/www/html
	@$(DOCKER_COMPOSE) exec -u root laravel.test git config --system --add safe.directory /var/www/html
	@echo "$(GREEN)✔ Permissions fixed.$(RESET)"
	@if [ "$(WWWUSER)" != "$(HOST_UID)" ] || [ -z "$(WWWUSER)" ]; then 		echo "$(YELLOW)⚠  WWWUSER ($(WWWUSER)) ≠ host UID ($(HOST_UID)). Set WWWUSER=$(HOST_UID) and WWWGROUP=$(HOST_GID) in .env, then run 'make rebuild' to align the container's sail user.$(RESET)"; 	fi
.PHONY: fix-perms
fix-perms: fix-permissions ## Alias for 'fix-permissions'

.PHONY: git-safe-directory
git-safe-directory: ## Register /var/www/html as a git safe.directory inside the container
	@$(DOCKER_COMPOSE) exec -u root laravel.test git config --system --add safe.directory /var/www/html >/dev/null 2>&1 || true
	@$(DOCKER_COMPOSE) exec laravel.test git config --global --add safe.directory /var/www/html >/dev/null 2>&1 || true
# ── Logs & Status ────────────────────────────────────────────
.PHONY: logs
logs: ## Tail logs (usage: make logs [SERVICE=mariadb])
	@$(DOCKER_COMPOSE) logs -f --tail=100 $(SERVICE)
.PHONY: tail-laravel
tail-laravel: ## Tail storage/logs/laravel.log inside the app container
	@$(SAIL) exec laravel.test tail -f storage/logs/laravel.log
.PHONY: status
status: ## Show running containers and their status
	@$(DOCKER_COMPOSE) ps
.PHONY: ps
ps: status ## Alias for 'status'
.PHONY: top
top: ## Show running processes inside each container
	@$(DOCKER_COMPOSE) top
.PHONY: images
images: ## Show images used by each service
	@$(DOCKER_COMPOSE) images
.PHONY: health
health: ## Show healthcheck status for each service (Compose v2.20+)
	@$(DOCKER_COMPOSE) ps --format json 2>/dev/null \
		| awk -F'"' '/"Service"/ {svc=$$4} /"Health"/ {print svc": "$$4}' \
		| sort -u \
		|| $(DOCKER_COMPOSE) ps
# ── Cleanup ──────────────────────────────────────────────────
.PHONY: clean
clean: ## Remove stopped containers and dangling images
	@docker system prune -f
	@echo "$(GREEN)✔ Cleaned up dangling resources.$(RESET)"
.PHONY: nuke
nuke: ## Remove ALL project containers, images, and volumes (DATA LOSS!)
	$(call confirm,This will remove ALL project containers/images/volumes. Continue?)
	@$(SAIL) down -v --rmi all
	@echo "$(GREEN)✔ All project Docker resources removed.$(RESET)"
# ── Help ─────────────────────────────────────────────────────
.PHONY: help
help: ## Show this help message, grouped by section
	@echo ""
	@echo "$(CYAN)$(BOLD)NNTmux Docker / Sail Commands$(RESET)"
	@echo "$(CYAN)─────────────────────────────$(RESET)"
	@awk ' \
/^# ── .* ──/ { \
line=$$0; sub(/^# ── /, "", line); sub(/ ──.*/, "", line); \
printf "\n\033[1;33m%s\033[0m\n", line; next \
} \
/^[a-zA-Z_-]+:.*?## / { \
split($$0, a, ":.*?## "); \
split(a[1], b, ":"); \
printf "  \033[32m%-20s\033[0m %s\n", b[1], a[2] \
}' $(MAKEFILE_LIST)
	@echo ""
	@echo "$(CYAN)Flags:$(RESET) FORCE=1  MAINTENANCE=1  SERVICE=name  CMD=\"…\""
	@echo ""
