SHELL := /bin/bash
.SHELLFLAGS := -eu -o pipefail -c
PY ?= .venv/bin/python
SLUG := terraform-teste-et-gouverne
# Pas de `-include .env` + `export` : la ligne de commande doit toujours l'emporter.
# On lit dans .env les deux seules valeurs utiles, sans exporter le reste.
PROJECT ?= $(shell sed -n 's/^GOOGLE_CLOUD_PROJECT=//p' .env 2>/dev/null)
TICKETING_TOKEN ?= $(shell sed -n 's/^TICKETING_TOKEN=//p' .env 2>/dev/null)
REGION ?= europe-west1
BOOT := infra/bootstrap
DEV := infra/envs/dev
PROD := infra/envs/prod
MODULES_TESTES := $(patsubst %/tests,%,$(wildcard infra/modules/*/tests))
CONFTEST_VERSION := 0.71.1
CONFTEST := .tools/conftest
TFLINT := tflint --config $(CURDIR)/.tflint.hcl
# Terraform lit ses variables dans TF_VAR_* : seules celles-ci sont exportées.
export TF_VAR_project_id = $(PROJECT)
export TF_VAR_ticketing_token = $(TICKETING_TOKEN)
STATE_BUCKET = $$(terraform -chdir=$(BOOT) output -raw state_bucket)
PROD_IMAGE := europe-west1-docker.pkg.dev/logi-ia-prod/lab-ttg-prod-images/triage@sha256:$(shell printf "0%.0s" {1..64})

.DEFAULT_GOAL := help
help: ## Liste les commandes
	@grep -hE '^[a-z0-9-]+:.*## ' $(MAKEFILE_LIST) | awk -F':.*## ' '{printf "  make %-13s %s\n", $$1, $$2}'

setup: ## .venv (checkov, pre-commit), conftest vérifié par SHA-256, plugin tflint
	@test -d .venv || python3 -m venv .venv
	@.venv/bin/pip install -q -r requirements.txt
	@test -f .env || cp .env.example .env
	@if [ ! -x $(CONFTEST) ]; then mkdir -p .tools && cd .tools && \
	  base=https://github.com/open-policy-agent/conftest/releases/download/v$(CONFTEST_VERSION); \
	  f=conftest_$(CONFTEST_VERSION)_Linux_x86_64.tar.gz; \
	  curl -sSfLO $$base/$$f && curl -sSfL $$base/checksums.txt | grep " $$f$$" | sha256sum -c - && \
	  tar xzf $$f conftest && rm $$f; fi
	@$(TFLINT) --init >/dev/null
	@terraform version | sed -n 1p; $(CONFTEST) --version | sed -n 1p; .venv/bin/checkov --version; tflint --version | sed -n 1p
	@echo "Prêt. Puis : make test"

fmt: ## Vérifie le formatage
	@terraform fmt -check -recursive infra && echo "fmt : OK"

validate: ## terraform validate de chaque racine (sans backend)
	@for d in $(BOOT) $(DEV) $(PROD); do export TF_DATA_DIR=.terraform-ci; \
	  terraform -chdir=$$d init -backend=false -input=false >/dev/null && \
	  terraform -chdir=$$d validate -no-color | grep -v '^$$' | sed "s|^|$$d : |"; done

test: ## terraform test (providers simulés) + tests des politiques : sans clé ni cloud
	@for m in $(MODULES_TESTES); do \
	  terraform -chdir=$$m init -input=false >/dev/null && \
	  terraform -chdir=$$m test -no-color | grep -E "^Success|^Failure|fail" | sed "s|^|$$m : |"; done
	@$(CONFTEST) verify --policy policies --no-color

lint: ## tflint (règles Terraform + Google)
	@$(TFLINT) --recursive --format compact && echo "tflint : OK"

security: ## checkov sur le code
	@.venv/bin/checkov -d infra --framework terraform --compact --quiet -o cli | grep -E "Passed checks|Check:|FAILED|File:"

plans/dev.json plans/prod.json: plan
plan: init ## Plans dev et prod en JSON (plans/)
	@mkdir -p plans
	@terraform -chdir=$(DEV) plan -input=false -lock-timeout=60s -out=$(CURDIR)/plans/dev.tfplan >/dev/null
	@terraform -chdir=$(DEV) show -json $(CURDIR)/plans/dev.tfplan > plans/dev.json
	@terraform -chdir=$(PROD) plan -input=false -lock-timeout=60s -var image=$(PROD_IMAGE) -out=$(CURDIR)/plans/prod.tfplan >/dev/null
	@terraform -chdir=$(PROD) show -json $(CURDIR)/plans/prod.tfplan > plans/prod.json
	@terraform -chdir=$(PROD) show -no-color $(CURDIR)/plans/prod.tfplan | grep -E "^Plan:" | sed "s|^|prod : |"
	@terraform -chdir=$(DEV) show -no-color $(CURDIR)/plans/dev.tfplan | grep -E "^Plan:|^No changes" | sed "s|^|dev  : |"

policy: plan ## Politiques OPA (conftest) et checkov sur les plans JSON
	@$(CONFTEST) test --policy policies --no-color plans/dev.json plans/prod.json
	@for f in plans/dev.json plans/prod.json; do .venv/bin/checkov --config-file .checkov-plan.yaml -f $$f -o cli | grep -E "Passed checks|Check:|FAILED" | sed "s|^|checkov $$f : |"; done

ci: fmt validate test lint security policy ## Toute la chaîne, dans l'ordre de la CI

run: ci ## Alias de make ci

state-up: ## Crée le bucket d'état (versionné, verrouillé)
	@terraform -chdir=$(BOOT) init -input=false >/dev/null
	@terraform -chdir=$(BOOT) apply -input=false -auto-approve -no-color | grep -E "^state_bucket|Apply complete"

init: ## Branche dev et prod sur le bucket d'état (make state-up d'abord, ou STATE_BUCKET=...)
	@test -n "$(STATE_BUCKET)" || { echo "Pas de bucket d'état : make state-up, ou make ... STATE_BUCKET=<nom>"; exit 1; }
	@for d in $(DEV) $(PROD); do terraform -chdir=$$d init -input=false -reconfigure -backend-config=bucket=$(STATE_BUCKET) >/dev/null; done

cloud-up: state-up init ## Crée le socle dev (sans service : aucune image à construire)
	@terraform -chdir=$(DEV) apply -input=false -auto-approve -no-color | grep -E "Apply complete"

drift: ## Détecte la dérive de dev (code 2 = dérive)
	@scripts/drift.sh $(DEV)

e2e: cloud-up ## Réel : socle dev, dérive provoquée, détection, réconciliation
	@scripts/drift.sh $(DEV) && echo "Pas de dérive après apply."
	@b=$$(terraform -chdir=$(DEV) output -raw archive_bucket); \
	echo "Modification manuelle : gcloud storage buckets update gs://$$b --update-labels=cost-center=finance"; \
	gcloud storage buckets update gs://$$b --project $(PROJECT) --update-labels=cost-center=finance >/dev/null 2>&1
	@rc=0; scripts/drift.sh $(DEV) || rc=$$?; test $$rc -eq 2 && echo "Dérive détectée (code 2), comme attendu."
	@terraform -chdir=$(DEV) apply -input=false -auto-approve -no-color | grep -E "Apply complete"
	@scripts/drift.sh $(DEV) && echo "E2E OK : dérive corrigée."

cloud-status: ## Ressources du lab encore présentes (code 1 s'il en reste)
	@n=0; p=lab-ttg; \
	chk() { out=$$("$${@:2}" 2>/dev/null || true); if [ -n "$$out" ]; then echo "RESTE $$1 : $$out"; n=$$((n+1)); fi; }; \
	chk "Cloud Run" gcloud run services list --project $(PROJECT) --region $(REGION) --filter="metadata.name~^$$p" --format="value(metadata.name)"; \
	chk "Artifact Registry" gcloud artifacts repositories list --project $(PROJECT) --location $(REGION) --filter="name~/$$p" --format="value(name)"; \
	chk "Secrets" gcloud secrets list --project $(PROJECT) --filter="labels.formation-lab=$(SLUG)" --format="value(name)"; \
	chk "Buckets" gcloud storage buckets list --project $(PROJECT) --filter="labels.formation-lab=$(SLUG)" --format="value(name)"; \
	chk "Comptes de service" gcloud iam service-accounts list --project $(PROJECT) --filter="email~^$$p" --format="value(email)"; \
	chk "Sinks" gcloud logging sinks list --project $(PROJECT) --filter="name~^$$p" --format="value(name)"; \
	chk "Métriques" gcloud logging metrics list --project $(PROJECT) --filter="name~^$$p" --format="value(name)"; \
	chk "IAM projet" gcloud projects get-iam-policy $(PROJECT) --flatten="bindings[].members" --filter="bindings.members~$$p" --format="value(bindings.role)"; \
	if [ $$n -eq 0 ]; then echo "Aucune ressource $(SLUG) restante."; else exit 1; fi

cloud-down: ## Détruit dev puis le bucket d'état, et vérifie
	@if terraform -chdir=$(BOOT) output -raw state_bucket >/dev/null 2>&1; then \
	  terraform -chdir=$(DEV) init -input=false -reconfigure -backend-config=bucket=$(STATE_BUCKET) >/dev/null; \
	  terraform -chdir=$(DEV) destroy -input=false -auto-approve -no-color | tail -1; \
	  terraform -chdir=$(BOOT) destroy -input=false -auto-approve -no-color | tail -1; \
	fi
	@# Les listes IAM sont à cohérence différée : un compte supprimé peut y figurer encore
	@# quelques secondes. On revérifie avant de conclure qu'il reste quelque chose.
	@for i in 1 2 3; do $(MAKE) --no-print-directory cloud-status && exit 0; sleep 15; done; exit 1

clean: ## Supprime plans, caches (.terraform, .terraform-ci, .tools)
	@rm -rf plans .tools .terraform-ci
	@find infra \( -name .terraform -o -name .terraform-ci \) -type d -prune -exec rm -rf {} +
	@echo "Artefacts supprimés (l'état local de infra/bootstrap est conservé)."

reset: clean ## clean + supprime .venv (garde .env)
	@rm -rf .venv
	@echo "Environnement supprimé (.env conservé)."

down: ## Sans objet : pas de pile Docker locale
	@echo "Pas de pile locale. Pour le cloud : make cloud-down"
