.PHONY: help build run test lint verify verify-deploy loadtest acme-cert-manager-test acme-rfc-conformance-test keycloak-integration-test okta-smoke-test benchmark-auth benchmark-auth-coldcache clean docker-up docker-down migrate-up migrate-down generate test-cover frontend-build qa-stats

# Default target - show help
help:
	@echo "Certctl Development Commands"
	@echo "============================="
	@echo ""
	@echo "Build & Run:"
	@echo "  make build          Build server and agent binaries"
	@echo "  make run            Run server locally (requires DB)"
	@echo "  make run-agent      Run agent locally"
	@echo ""
	@echo "Testing & Quality:"
	@echo "  make test           Run all tests"
	@echo "  make test-verbose   Run tests with verbose output"
	@echo "  make lint           Run linter (golangci-lint)"
	@echo "  make fmt            Format code with gofmt"
	@echo "  make verify         Pre-commit gate: fmt + vet + lint + test (CI-parity)"
	@echo "  make verify-deploy  Pre-push gate:   digest validity + OpenAPI parity + docker build smoke"
	@echo "  make loadtest       k6 throughput run against postgres + certctl (NOT in verify; manual + cron only)"
	@echo ""
	@echo "Database:"
	@echo "  make migrate-up     Run migrations (requires DB_URL)"
	@echo "  make migrate-down   Rollback last migration"
	@echo "  make db-seed        Seed database with test data"
	@echo ""
	@echo "Docker:"
	@echo "  make docker-build   Build Docker images"
	@echo "  make docker-up      Start Docker Compose stack"
	@echo "  make docker-down    Stop Docker Compose stack"
	@echo "  make docker-logs    View Docker logs"
	@echo "  make docker-clean   Remove Docker resources"
	@echo ""
	@echo "Code Generation:"
	@echo "  make generate       Run go generate"
	@echo "  make clean          Clean build artifacts"
	@echo ""

# Build targets
build:
	@echo "Building server and agent..."
	mkdir -p bin
	CGO_ENABLED=0 go build -o bin/server ./cmd/server
	CGO_ENABLED=0 go build -o bin/agent ./cmd/agent
	@echo "Build complete: bin/server, bin/agent"

build-server:
	@echo "Building server..."
	mkdir -p bin
	CGO_ENABLED=0 go build -o bin/server ./cmd/server
	@echo "Server build complete"

build-agent:
	@echo "Building agent..."
	mkdir -p bin
	CGO_ENABLED=0 go build -o bin/agent ./cmd/agent
	@echo "Agent build complete"

# Run targets
run: build-server
	@echo "Starting server (requires DATABASE_URL or DB_* env vars)..."
	./bin/server

run-agent: build-agent
	@echo "Starting agent (requires SERVER_URL and API_KEY env vars)..."
	./bin/agent

# Testing targets
test:
	@echo "Running tests..."
	go test ./...

test-verbose:
	@echo "Running tests with verbose output..."
	go test -v ./...

test-coverage:
	@echo "Running tests with coverage..."
	go test -coverprofile=coverage.out ./...
	go tool cover -html=coverage.out -o coverage.html
	@echo "Coverage report: coverage.html"

test-cover:
	@echo "Running tests with coverage..."
	go test ./internal/service/... ./internal/api/handler/... ./internal/integration/... -count=1 -cover -coverprofile=coverage.out
	@echo "Coverage report: coverage.out"

# Linting targets
lint:
	@echo "Running golangci-lint..."
	@which golangci-lint > /dev/null || (echo "Installing golangci-lint..." && go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest)
	golangci-lint run ./...

fmt:
	@echo "Formatting code..."
	go fmt ./...
	@echo "Code formatted"

vet:
	@echo "Running go vet..."
	go vet ./...

# verify: aggregate pre-commit gate. Mirrors what CI enforces, so
# running `make verify` locally before committing prevents the
# class of breakages that ship green-locally / red-on-CI (e.g.
# Bundle-9's ST1018 invisible-Unicode-literal hits, which `go vet`
# alone cannot catch — staticcheck under golangci-lint does).
verify:
	@echo "==> fmt"
	@go fmt ./... | { ! grep -q '.'; } || (echo "gofmt produced changes — commit them" && exit 1)
	@echo "==> go vet ./..."
	@go vet ./...
	@echo "==> golangci-lint run ./... (incl. staticcheck ST*)"
	@which golangci-lint > /dev/null || (echo "Installing golangci-lint..." && go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest)
	@golangci-lint run ./... --timeout 5m
	@echo "==> go test -short ./..."
	@go test -short -count=1 ./...
	@echo ""
	@echo "verify: PASS — safe to commit"

# verify-deploy: optional pre-push gate. Runs the digest-validity check,
# the OpenAPI ↔ handler parity check, and a Docker build smoke for the
# production images (server + agent only — fast subset for local; CI
# builds all 4 Dockerfiles per ci-pipeline-cleanup Phase 8 / frozen
# decision 0.10).
#
# Per ci-pipeline-cleanup bundle Phase 11 / frozen decision 0.13.
verify-deploy:
	@echo "==> Digest validity"
	@bash scripts/ci-guards/digest-validity.sh
	@echo "==> OpenAPI ↔ handler parity"
	@bash scripts/ci-guards/openapi-handler-parity.sh
	@echo "==> Docker build smoke (server + agent — fast subset)"
	@docker build -f Dockerfile        -t certctl:verify           .
	@docker build -f Dockerfile.agent  -t certctl-agent:verify     .
	@echo ""
	@echo "verify-deploy: PASS — safe to push"

# Load-test harness — closes the #8 acquisition-readiness blocker from
# the 2026-05-01 issuer coverage audit. Boots a minimal certctl stack
# (postgres + tls-init + certctl-server) and runs k6 against the API
# tier for ~5 minutes. Exits non-zero on any threshold breach.
#
# NOT in `make verify` — load tests take minutes, not seconds, and
# don't gate per-PR signal. CI gates this behind workflow_dispatch +
# weekly cron in .github/workflows/loadtest.yml. See
# deploy/test/loadtest/README.md for thresholds, baseline, and how to
# interpret a regression.
loadtest:
	@echo "==> spinning up postgres + certctl + k6 driver (this takes ~7m)"
	@cd deploy/test/loadtest && docker compose up --build --abort-on-container-exit --exit-code-from k6
	@echo ""
	@echo "==> results landed in deploy/test/loadtest/results/"
	@if [ -f deploy/test/loadtest/results/summary.txt ]; then cat deploy/test/loadtest/results/summary.txt; fi

# Auth Bundle 2 Phase 10 — Keycloak end-to-end OIDC integration test.
# Boots a Keycloak container via testcontainers-go (quay.io/keycloak:25.0),
# imports a canned realm with two groups + two users, and drives the
# full OIDC flow against the certctl service: discovery + JWKS,
# auth-code login, group-claim parsing, group-role mapping, session
# mint, and JWKS rotation.
#
# Build-tag-gated under `integration` so `make verify` (which runs
# go test -short) NEVER pulls in the 60-90s Keycloak boot. Requires a
# local Docker daemon. Skips cleanly with t.Skip() when -short is set.
keycloak-integration-test:
	@echo "==> running Keycloak OIDC integration test (requires Docker)"
	@go test -tags=integration -count=1 -timeout=10m \
	  ./internal/auth/oidc/...

# Auth Bundle 2 Phase 10 — optional Okta smoke test. Gated behind TWO
# build tags (integration + okta_smoke) so it only runs when invoked
# manually against the operator's own Okta dev tenant. Requires the
# OKTA_ISSUER + OKTA_CLIENT_ID + OKTA_CLIENT_SECRET env vars; the test
# t.Skip's with a clear message when any are missing. Documented in
# internal/auth/oidc/integration_okta_smoke_test.go.
okta-smoke-test:
	@echo "==> running Okta smoke test (requires OKTA_ISSUER / _CLIENT_ID / _CLIENT_SECRET env vars)"
	@go test -tags='integration okta_smoke' -count=1 -timeout=2m \
	  ./internal/auth/oidc/...

# Auth Bundle 2 Phase 14 — auth performance benchmarks. Three default-
# tag benchmarks (session steady-state + session cold-process + oidc
# steady-state) producing p50/p95/p99/max numbers per the auth-
# benchmarks.md operator-doc table.
benchmark-auth:
	@echo "==> running auth performance benchmarks (session + oidc steady-state)"
	@go test -bench='BenchmarkSession_|BenchmarkOIDC_SteadyState' -benchmem \
	  -benchtime=2000x -run='^$$' \
	  ./internal/auth/session/ ./internal/auth/oidc/

# Auth Bundle 2 Phase 14 — OIDC cold-cache benchmark against a live
# Keycloak container (requires Docker). Build-tag-gated so the
# default-tag benchmarks above never pull in the 60-90s container
# boot. Runs the integration test FIRST to populate the
# sharedKeycloak fixture, then runs the benchmark.
benchmark-auth-coldcache:
	@echo "==> running OIDC cold-cache benchmark against live Keycloak (requires Docker)"
	@go test -tags integration -count=1 -timeout=10m \
	  -run TestKeycloakIntegration_RefreshKeysFetchesDiscoveryAndJWKS \
	  -bench BenchmarkOIDC_ColdCache -benchmem -benchtime=10x \
	  ./internal/auth/oidc/

# Phase 5 — kind-driven cert-manager integration test. Requires
# `kind`, `kubectl`, `helm`, and a local Docker daemon. Sets
# KIND_AVAILABLE=1 so the test runs (it skips cleanly when unset, which
# is the CI default — kind is too heavy for per-PR CI). The test
# brings up a fresh cluster, installs cert-manager 1.15, helm-installs
# certctl-test, applies a ClusterIssuer + Certificate, and asserts the
# Secret lands.
acme-cert-manager-test:
	@echo "==> running cert-manager integration test (requires kind/kubectl/helm)"
	@KIND_AVAILABLE=1 go test -tags=integration -count=1 -timeout=15m \
	  ./deploy/test/acme-integration/...

# Phase 5 — RFC 8555 conformance against `lego` driving the certctl
# server. Hermetic: brings up a single certctl-server via docker
# compose, points lego at it, runs the conformance scenarios. Skips
# when the operator hasn't built the test image (`make docker-build`
# first).
acme-rfc-conformance-test:
	@echo "==> running RFC 8555 conformance via lego"
	@if ! command -v lego >/dev/null 2>&1; then \
	  echo "lego not installed — go install github.com/go-acme/lego/v4/cmd/lego@latest"; \
	  exit 1; \
	fi
	@cd deploy/test/loadtest && docker compose up -d certctl postgres
	@sleep 8
	@CERTCTL_ACME_DIR=https://localhost:8443/acme/profile/prof-test/directory \
	  bash deploy/test/acme-integration/conformance-lego.sh
	@cd deploy/test/loadtest && docker compose down

# Database targets (requires migrate tool)
migrate-up:
	@echo "Running migrations..."
	@which migrate > /dev/null || (echo "Installing migrate CLI..." && go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest)
	migrate -path migrations -database "${DB_URL:-postgres://certctl:certctl@localhost:5432/certctl?sslmode=disable}" up

migrate-down:
	@echo "Rolling back last migration..."
	@which migrate > /dev/null || (echo "Installing migrate CLI..." && go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest)
	migrate -path migrations -database "${DB_URL:-postgres://certctl:certctl@localhost:5432/certctl?sslmode=disable}" down 1

migrate-status:
	@echo "Checking migration status..."
	@which migrate > /dev/null || (echo "Installing migrate CLI..." && go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest)
	migrate -path migrations -database "${DB_URL:-postgres://certctl:certctl@localhost:5432/certctl?sslmode=disable}" version

db-seed:
	@echo "Seeding database with test data..."
	go run ./scripts/seed/main.go

# Docker targets
docker-build:
	@echo "Building Docker images..."
	docker-compose -f deploy/docker-compose.yml build

docker-up:
	@echo "Starting Docker Compose stack..."
	docker-compose -f deploy/docker-compose.yml up -d
	@echo "Stack running. Access server at http://localhost:8443"

docker-up-dev:
	@echo "Starting Docker Compose stack (dev mode)..."
	docker-compose -f deploy/docker-compose.yml -f deploy/docker-compose.dev.yml up -d
	@echo "Stack running. PgAdmin at http://localhost:5050"

docker-down:
	@echo "Stopping Docker Compose stack..."
	docker-compose -f deploy/docker-compose.yml down

docker-logs:
	docker-compose -f deploy/docker-compose.yml logs -f

docker-logs-server:
	docker-compose -f deploy/docker-compose.yml logs -f certctl-server

docker-logs-agent:
	docker-compose -f deploy/docker-compose.yml logs -f certctl-agent

docker-clean:
	@echo "Removing Docker resources..."
	docker-compose -f deploy/docker-compose.yml down -v
	@echo "Cleaned up"

# Code generation
generate:
	@echo "Running go generate..."
	go generate ./...
	@echo "Code generation complete"

# Frontend build
frontend-build:
	@echo "Building frontend..."
	cd web && npm ci && npx vite build
	@echo "Frontend build complete"

# qa-stats: snapshot of the test-suite size at the current commit.
# Backend Go tests + subtests + fuzz targets + skipped sites, plus the
# seed-data counts in migrations/seed_demo.sql. Useful before a release
# to spot-check that no whole layer dropped off.
qa-stats:
	@echo "=== certctl QA Suite Stats ==="
	@echo "Date: $$(date +%Y-%m-%d)"
	@echo "HEAD: $$(git rev-parse HEAD 2>/dev/null || echo 'not-a-git-repo')"
	@echo ""
	@echo "Backend test files: $$(find . -name '*_test.go' -not -path './web/*' 2>/dev/null | wc -l | tr -d ' ')"
	@echo "Backend Test functions: $$(find . -name '*_test.go' -not -path './web/*' 2>/dev/null | xargs grep -c '^func Test' 2>/dev/null | awk -F: '{s+=$$2} END{print s+0}')"
	@echo "Backend t.Run subtests: $$(find . -name '*_test.go' -not -path './web/*' 2>/dev/null | xargs grep -c 't\.Run(' 2>/dev/null | awk -F: '{s+=$$2} END{print s+0}')"
	@echo "Frontend test files: $$(find web/src -name '*.test.ts' -o -name '*.test.tsx' 2>/dev/null | wc -l | tr -d ' ')"
	@echo "Fuzz targets: $$(grep -rE 'func Fuzz[A-Z]' --include='*_test.go' . 2>/dev/null | wc -l | tr -d ' ')"
	@echo "t.Skip sites: $$(grep -rE 't\.Skip(Now|f)?\(' --include='*_test.go' . 2>/dev/null | wc -l | tr -d ' ')"
	@echo "qa_test.go Part_ subtests: $$(grep -cE 't\.Run\(\"Part[0-9]+_' deploy/test/qa_test.go 2>/dev/null || echo 0)"
	@echo "Seed unique mc-* IDs:  $$(grep -oE "mc-[a-z0-9_-]+" migrations/seed_demo.sql 2>/dev/null | sort -u | wc -l | tr -d ' ')"
	@echo "Seed unique ag-* IDs:  $$(grep -oE "ag-[a-z0-9_-]+" migrations/seed_demo.sql 2>/dev/null | sort -u | wc -l | tr -d ' ') (incl. agent_groups; agents-table count is 13 incl. agent-demo-1 + 3 cloud sentinels + server-scanner)"
	@echo "Seed unique iss-* IDs: $$(grep -oE "iss-[a-z0-9_-]+" migrations/seed_demo.sql 2>/dev/null | sort -u | wc -l | tr -d ' ') (issuers table count is 13)"
	@echo "Seed unique tgt-* IDs: $$(grep -oE "tgt-[a-z0-9_-]+" migrations/seed_demo.sql 2>/dev/null | sort -u | wc -l | tr -d ' ')"
	@echo "Seed unique nst-* IDs: $$(grep -oE "nst-[a-z0-9_-]+" migrations/seed_demo.sql 2>/dev/null | sort -u | wc -l | tr -d ' ')"

# Cleanup
clean:
	@echo "Cleaning build artifacts..."
	rm -rf bin/ dist/ coverage.out coverage.html
	go clean -testcache
	cd web && rm -rf node_modules dist
	@echo "Cleanup complete"

install-tools:
	@echo "Installing development tools..."
	go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
	go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
	go install github.com/cosmtrek/air@latest
	@echo "Tools installed"
