.PHONY: all build build-klor build-linux build-klor-linux test test-linux test-bpf-helpers e2e e2e-setup e2e-run e2e-cleanup e2e-klor e2e-klor-rooted \
        e2e-k3s e2e-k3s-setup e2e-k3s-run e2e-k3s-cleanup \
        clean deps docker-build generate-ebpf generate-vmlinux run help \
        lima-start lima-stop lima-delete lima-shell lima-exec lima-check \
        lima-k3d-ensure lima-k3d-shell lima-k3d-e2e-setup lima-k3d-e2e-run lima-k3d-e2e lima-k3d-stop lima-k3d-delete \
        go-tls-fixtures go-tls-discover

# Go parameters
GOCMD=go
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOTEST=$(GOCMD) test
GOMOD=$(GOCMD) mod
GOGENERATE=$(GOCMD) generate

# Binary names
BINARY_NAME=kloak
WEBHOOK_BINARY=kloak-webhook

# Klor — the host-CLI runtime. Separate `main` package at cmd/klor/ so its
# dependency closure shrinks independently (no k8s.io/client-go, no
# controller-runtime). Not part of the controller Docker image.
KLOR_BINARY=klor

# Release tag baked into the binary for `kloak version`. `git describe
# --tags --always --dirty` gives `v0.1.0` exactly on tag, `v0.1.0-3-gabc1234-dirty`
# between tags with uncommitted changes, and falls back to the short
# commit when no tags exist. CI release builds override VERSION explicitly.
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
LDFLAGS = -ldflags "-X main.version=$(VERSION)"

# Build directories
BUILD_DIR=bin
CMD_DIR=cmd
BPF_DIR=pkg/ebpf
EBPF_PKG=pkg/ebpf

# Source files
GO_SOURCES=$(shell find . -name '*.go' -not -path './vendor/*')
BPF_SOURCES=$(wildcard $(BPF_DIR)/*.c $(BPF_DIR)/*.h)
EBPF_GENERATED=$(EBPF_PKG)/tlsuprobe_bpfel.go $(EBPF_PKG)/tlsuprobe_bpfeb.go

# Docker
DOCKER_IMAGE=kloak
DOCKER_TAG=latest

# Lima VM
LIMA_VM=kloak
LIMA_WORKDIR=$(shell pwd)
LIMA_CONFIG=lima.yaml

# ============================================================================
# Main targets
# ============================================================================

all: build build-klor

# Build depends on Go sources (and generated eBPF on Linux)
build: deps $(BUILD_DIR)/$(BINARY_NAME)

$(BUILD_DIR)/$(BINARY_NAME): $(GO_SOURCES)
	@mkdir -p $(BUILD_DIR)
	$(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME) ./$(CMD_DIR)/kloak

# Build the klor host-CLI binary. Sibling of `build`; lives at cmd/klor/.
build-klor: deps $(BUILD_DIR)/$(KLOR_BINARY)

$(BUILD_DIR)/$(KLOR_BINARY): $(GO_SOURCES)
	@mkdir -p $(BUILD_DIR)
	$(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(KLOR_BINARY) ./$(CMD_DIR)/klor

# Build for Linux (cross-compile or via Lima)
build-linux: lima-ensure
	@if [ "$$(uname)" = "Linux" ]; then \
		GOOS=linux GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux ./$(CMD_DIR)/kloak; \
	else \
		$(MAKE) lima-exec CMD="cd $(LIMA_WORKDIR) && go build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux ./$(CMD_DIR)/kloak"; \
	fi

build-klor-linux: lima-ensure
	@if [ "$$(uname)" = "Linux" ]; then \
		GOOS=linux GOARCH=amd64 $(GOBUILD) $(LDFLAGS) -o $(BUILD_DIR)/$(KLOR_BINARY)-linux ./$(CMD_DIR)/klor; \
	else \
		$(MAKE) lima-exec CMD="cd $(LIMA_WORKDIR) && go build $(LDFLAGS) -o $(BUILD_DIR)/$(KLOR_BINARY)-linux ./$(CMD_DIR)/klor"; \
	fi

test: deps
	$(GOTEST) -v ./...

# Run tests in Linux VM (for eBPF tests) - depends on Lima running
test-linux: lima-ensure
	$(MAKE) lima-exec CMD="cd $(LIMA_WORKDIR) && go test -v ./..."

# Run eBPF helper unit tests (pure C, no Linux/BPF required)
test-bpf-helpers:
	gcc -Wall -Werror -o /tmp/helpers_test pkg/ebpf/bpf/helpers_test.c
	/tmp/helpers_test

# Build a tiny Go binary against every supported Go version × arch using
# Docker. Outputs ELF fixtures to pkg/ebpf/testdata/go-tls-fixtures/.
# Used by the table-driven DWARF test in pkg/ebpf/go_tls_offsets_test.go.
# Containers spin once per (version, arch) — never per test invocation.
# Override versions with: GO_VERSIONS="1.23 1.24" make go-tls-fixtures
go-tls-fixtures:
	tools/go-tls-offsets/build-fixtures.sh

# Run the existing tools/go-tls-offsets discovery tool against every
# fixture from `make go-tls-fixtures`, writing JSON results to
# tools/go-tls-offsets/results/. The JSONs are the canonical reference
# the in-tree test asserts against. Depends on go-tls-fixtures.
go-tls-discover: go-tls-fixtures
	tools/go-tls-offsets/discover-all.sh

# E2E cluster and image configuration
E2E_CLUSTER=kloak-e2e
E2E_IMAGE_TAG=e2e

# Full e2e: setup cluster, run tests, tear down.
e2e: e2e-setup e2e-run e2e-cleanup

# Create k3d cluster with BPF mounts, build and import all images.
e2e-setup:
	@which k3d > /dev/null || (echo "Error: k3d not found. Install with: brew install k3d" && exit 1)
	@mountpoint -q /sys/kernel/tracing 2>/dev/null || sudo mount -t tracefs tracefs /sys/kernel/tracing 2>/dev/null || true
	@echo "==> Creating k3d cluster '$(E2E_CLUSTER)' with BPF mounts..."
	@k3d cluster create $(E2E_CLUSTER) --wait --timeout 120s \
		--volume /sys/kernel/btf:/sys/kernel/btf:ro@server:0 \
		--volume /sys/fs/bpf:/sys/fs/bpf:rw@server:0 \
		--volume /sys/kernel/tracing:/sys/kernel/tracing:rw@server:0 \
		2>/dev/null || true
	@echo "==> Building kloak image..."
	@docker build -t $(DOCKER_IMAGE):$(E2E_IMAGE_TAG) .
	@echo "==> Building demo images..."
	@docker build -t kloak-demo-go:$(E2E_IMAGE_TAG) -t kloak-demo-go:latest ./examples/demo-go/
	@docker build -t kloak-demo-python:latest ./examples/demo-python/
	@docker build -t kloak-demo-js:latest ./examples/demo-js/
	@docker build -t kloak-demo-go-boring:latest ./examples/demo-go-boring/
	@docker build -t kloak-demo-gnutls:latest ./examples/demo-gnutls/
	@docker build -t kloak-demo-python-raw-tls:latest ./examples/demo-python-raw-tls/
	@docker build -t kloak-tls-echo:latest ./test/e2e/tls-echo-server/
	@echo "==> Importing images into k3d (via tar to avoid pipe EOF)..."
	@mkdir -p /tmp/k3d-images
	@for img in $(DOCKER_IMAGE):$(E2E_IMAGE_TAG) kloak-demo-go:latest kloak-demo-python:latest \
		kloak-demo-js:latest kloak-demo-go-boring:latest kloak-demo-gnutls:latest \
		kloak-demo-python-raw-tls:latest kloak-tls-echo:latest; do \
		echo "  Importing $$img..."; \
		docker save $$img -o /tmp/k3d-images/$$(echo $$img | tr ':/' '__').tar && \
		k3d image import /tmp/k3d-images/$$(echo $$img | tr ':/' '__').tar -c $(E2E_CLUSTER); \
	done
	@rm -rf /tmp/k3d-images
	@echo "==> E2E environment ready."

# Run e2e tests (including eBPF) against an existing k3d cluster.
e2e-run:
	KUBECONFIG=$$(k3d kubeconfig write $(E2E_CLUSTER)) \
	$(GOTEST) -v -timeout 1500s -tags=e2e_ebpf -count=1 ./test/e2e/

# klor e2e — builds the klor binary and exercises its CLI against
# a tempdir cgroup root. No cluster, no root required (Linux-only,
# skips on macOS). Cheap; safe to run as part of unit-test CI.
e2e-klor:
	$(GOTEST) -v -timeout 120s -tags=e2e_klor -count=1 ./test/e2e/klor/

# klor rootless e2e — exercises install.sh + cap-on-binary so klor
# runs without sudo. TestMain skips unless running as root (so
# install.sh can setcap); the test itself then drops to a non-root
# uid via setpriv to prove the cap-on-binary path actually delivers
# the privilege drop. CI's ubuntu-latest runs root by default; dev
# hosts use `sudo make e2e-klor-rooted`.
e2e-klor-rooted:
	$(GOTEST) -v -timeout 180s -tags=e2e_klor_rooted -count=1 ./test/e2e/klor/

# Run e2e tests against the current kube context.
# Builds images, pushes to ttl.sh (anonymous ephemeral registry, 2h TTL),
# and runs tests with E2E_REGISTRY so images are pulled from there.
# Set E2E_REGISTRY to use a different registry (e.g. localhost:5000).
# WARNING: This will helm install/uninstall kloak in kloak-system and
# create/delete a kloak-e2e namespace.
# Usage: make e2e-local
E2E_TTL_TAG ?= kloak-$(shell date +%s)
E2E_REGISTRY ?= ttl.sh/$(E2E_TTL_TAG)

e2e-local: e2e-local-push e2e-local-run

e2e-local-push:
	@echo "==> Building and pushing images to $(E2E_REGISTRY) ..."
	@docker build -t $(E2E_REGISTRY)/kloak:e2e .
	@docker push $(E2E_REGISTRY)/kloak:e2e
	@for demo in demo-go demo-python demo-js demo-go-boring demo-gnutls demo-python-raw-tls; do \
		echo "  Pushing kloak-$$demo..."; \
		docker build -t $(E2E_REGISTRY)/kloak-$$demo:latest ./examples/$$demo/ && \
		docker push $(E2E_REGISTRY)/kloak-$$demo:latest; \
	done
	@docker build -t $(E2E_REGISTRY)/kloak-tls-echo:latest ./test/e2e/tls-echo-server/
	@docker push $(E2E_REGISTRY)/kloak-tls-echo:latest
	@echo "==> All images pushed."

# E2E_RUN: optional -run filter (e.g. E2E_RUN=TestCipherSuites make e2e-local)
E2E_RUN ?=

e2e-local-run:
	E2E_REGISTRY=$(E2E_REGISTRY) E2E_SKIP_INSTALL=$(E2E_SKIP_INSTALL) \
	$(GOTEST) -v -timeout 1500s -tags=e2e_ebpf -count=1 $(if $(E2E_RUN),-run $(E2E_RUN)) ./test/e2e/

# Tear down e2e k3d cluster.
e2e-cleanup:
	@k3d cluster delete $(E2E_CLUSTER) 2>/dev/null || true

# ============================================================================
# k3s-native e2e targets (for CI / Linux workstations — no Docker nesting)
# ============================================================================

# Full k3s e2e: setup + run + cleanup.
e2e-k3s: e2e-k3s-setup e2e-k3s-run e2e-k3s-cleanup

# Install k3s, build all images, import into k3s containerd.
e2e-k3s-setup:
	@echo "==> Installing k3s..."
	@curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--write-kubeconfig-mode 644 --disable=traefik" sh -
	@echo "==> Waiting for k3s node to register..."
	@for i in $$(seq 1 30); do \
		sudo k3s kubectl get nodes -o name 2>/dev/null | grep -q . && break; \
		echo "  Waiting for k3s node ($$i/30)..."; \
		sleep 2; \
	done
	@sudo k3s kubectl wait --for=condition=Ready node --all --timeout=120s
	@mkdir -p $(HOME)/.kube
	@sudo cp /etc/rancher/k3s/k3s.yaml $(HOME)/.kube/k3s-e2e.yaml
	@sudo chown $$(id -u):$$(id -g) $(HOME)/.kube/k3s-e2e.yaml
	@chmod 600 $(HOME)/.kube/k3s-e2e.yaml
	@echo "==> Building images..."
	@docker build --build-arg TARGETARCH=$$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') \
		--build-arg COVER=1 -t $(DOCKER_IMAGE):$(E2E_IMAGE_TAG) .
	@docker build -t kloak-demo-go:$(E2E_IMAGE_TAG) -t kloak-demo-go:latest ./examples/demo-go/
	@docker build -t kloak-demo-python:latest ./examples/demo-python/
	@docker build -t kloak-demo-js:latest ./examples/demo-js/
	@docker build -t kloak-demo-go-boring:latest ./examples/demo-go-boring/
	@docker build -t kloak-demo-gnutls:latest ./examples/demo-gnutls/
	@docker build -t kloak-demo-python-raw-tls:latest ./examples/demo-python-raw-tls/
	@docker build -t kloak-tls-echo:latest ./test/e2e/tls-echo-server/
	@echo "==> Importing images into k3s containerd..."
	@docker save $(DOCKER_IMAGE):$(E2E_IMAGE_TAG) kloak-demo-go:latest kloak-demo-python:latest \
		kloak-demo-js:latest kloak-demo-go-boring:latest kloak-demo-gnutls:latest \
		kloak-demo-python-raw-tls:latest kloak-tls-echo:latest | sudo k3s ctr -n k8s.io images import -
	@echo "==> k3s e2e environment ready."

# Run e2e tests against local k3s.
e2e-k3s-run:
	KUBECONFIG=$(HOME)/.kube/k3s-e2e.yaml \
	$(GOTEST) -v -timeout 1500s -tags=e2e_ebpf -count=1 $(if $(E2E_RUN),-run $(E2E_RUN)) ./test/e2e/

# Tear down k3s.
e2e-k3s-cleanup:
	@sudo /usr/local/bin/k3s-killall.sh 2>/dev/null || true
	@sudo /usr/local/bin/k3s-uninstall.sh 2>/dev/null || true
	@rm -f $(HOME)/.kube/k3s-e2e.yaml

clean:
	$(GOCLEAN)
	rm -rf $(BUILD_DIR)
	rm -f $(EBPF_PKG)/tlsuprobe_bpfel.go $(EBPF_PKG)/tlsuprobe_bpfeb.go
	rm -f $(EBPF_PKG)/tlsuprobe_bpfel.o $(EBPF_PKG)/tlsuprobe_bpfeb.o

deps:
	$(GOMOD) download
	$(GOMOD) tidy

docker-build: build
	docker build -t $(DOCKER_IMAGE):$(DOCKER_TAG) .

run: build
	./$(BUILD_DIR)/$(BINARY_NAME)

# ============================================================================
# eBPF generation targets
# ============================================================================

# Generate eBPF code - depends on BPF sources and vmlinux.h.
# KLOAK_TARGET_ARCH defaults to arm64 (Lima VM). Set to x86 for amd64 builds.
generate-ebpf: $(BPF_SOURCES) $(BPF_DIR)/vmlinux.h lima-ensure
	@echo "Generating eBPF code (arch=$${KLOAK_TARGET_ARCH:-arm64})..."
	@if [ "$$(uname)" = "Linux" ]; then \
		cd $(EBPF_PKG) && $(GOGENERATE); \
	else \
		echo "Using Lima VM for eBPF generation..."; \
		$(MAKE) lima-exec CMD="cd $(LIMA_WORKDIR)/$(EBPF_PKG) && KLOAK_TARGET_ARCH=$${KLOAK_TARGET_ARCH:-arm64} go generate"; \
	fi
	@echo "eBPF generation complete."

# Generate vmlinux.h from kernel BTF - depends on Lima
$(BPF_DIR)/vmlinux.h: lima-ensure
	@echo "Generating vmlinux.h from kernel BTF..."
	@if [ "$$(uname)" = "Linux" ]; then \
		bpftool btf dump file /sys/kernel/btf/vmlinux format c > $@; \
	else \
		$(MAKE) lima-exec CMD="bpftool btf dump file /sys/kernel/btf/vmlinux format c > $(LIMA_WORKDIR)/$@"; \
	fi

generate-vmlinux: $(BPF_DIR)/vmlinux.h

# ============================================================================
# Lima VM targets (for eBPF development on macOS)
# ============================================================================

# Check if Lima is installed
lima-check:
	@if [ "$$(uname)" = "Linux" ]; then \
		exit 0; \
	else \
		which limactl > /dev/null || (echo "Error: limactl not found. Install with: brew install lima" && exit 1); \
	fi

# Ensure Lima VM is running (idempotent)
lima-ensure: lima-check $(LIMA_CONFIG)
	@if [ "$$(uname)" = "Linux" ]; then \
		exit 0; \
	elif ! limactl list 2>/dev/null | grep -q "^$(LIMA_VM)"; then \
		echo "Creating Lima VM '$(LIMA_VM)'..."; \
		limactl start --name=$(LIMA_VM) $(LIMA_CONFIG); \
	elif limactl list | grep "^$(LIMA_VM)" | grep -q "Stopped"; then \
		echo "Starting Lima VM '$(LIMA_VM)'..."; \
		limactl start $(LIMA_VM); \
	fi

# Start Lima VM for eBPF development
lima-start: lima-check $(LIMA_CONFIG)
	@if ! limactl list 2>/dev/null | grep -q "^$(LIMA_VM)"; then \
		echo "Creating Lima VM '$(LIMA_VM)'..."; \
		limactl start --name=$(LIMA_VM) $(LIMA_CONFIG); \
	elif limactl list | grep "^$(LIMA_VM)" | grep -q "Stopped"; then \
		echo "Starting Lima VM '$(LIMA_VM)'..."; \
		limactl start $(LIMA_VM); \
	else \
		echo "Lima VM '$(LIMA_VM)' is already running"; \
	fi

# Stop Lima VM
lima-stop:
	@limactl stop $(LIMA_VM) 2>/dev/null || true

# Delete Lima VM
lima-delete: lima-stop
	@limactl delete $(LIMA_VM) 2>/dev/null || true

# Shell into Lima VM
lima-shell: lima-ensure
	limactl shell $(LIMA_VM)

# Execute command in Lima VM (uses login shell for proper PATH)
lima-exec: lima-ensure
	@limactl shell $(LIMA_VM) -- bash -lc '$(CMD)'

# ============================================================================
# Lima k3d targets (for e2e testing on ARM Mac)
# ============================================================================

LIMA_K3D_VM=kloak-k3d
LIMA_K3D_CONFIG=lima-k3d.yaml

# Ensure k3d Lima VM is running
lima-k3d-ensure: lima-check $(LIMA_K3D_CONFIG)
	@if ! limactl list 2>/dev/null | grep -q "^$(LIMA_K3D_VM)"; then \
		echo "Creating Lima k3d VM '$(LIMA_K3D_VM)'..."; \
		limactl start --name=$(LIMA_K3D_VM) $(LIMA_K3D_CONFIG); \
	elif limactl list | grep "^$(LIMA_K3D_VM)" | grep -q "Stopped"; then \
		echo "Starting Lima k3d VM '$(LIMA_K3D_VM)'..."; \
		limactl start $(LIMA_K3D_VM); \
	fi

# Shell into k3d Lima VM
lima-k3d-shell: lima-k3d-ensure
	limactl shell $(LIMA_K3D_VM)

# Run e2e setup inside k3d Lima VM
lima-k3d-e2e-setup: lima-k3d-ensure
	limactl shell $(LIMA_K3D_VM) -- bash -lc 'sg docker -c "cd $(LIMA_WORKDIR) && make e2e-setup"'

# Run e2e tests inside k3d Lima VM
lima-k3d-e2e-run: lima-k3d-ensure
	limactl shell $(LIMA_K3D_VM) -- bash -lc 'sg docker -c "cd $(LIMA_WORKDIR) && make e2e-run"'

# Full e2e inside k3d Lima VM
lima-k3d-e2e: lima-k3d-e2e-setup lima-k3d-e2e-run

# Stop k3d Lima VM
lima-k3d-stop:
	@limactl stop $(LIMA_K3D_VM) 2>/dev/null || true

# Delete k3d Lima VM
lima-k3d-delete: lima-k3d-stop
	@limactl delete $(LIMA_K3D_VM) 2>/dev/null || true

# ============================================================================
# Help
# ============================================================================

help:
	@echo "Kloak Makefile - Kubernetes eBPF HTTPS Interceptor"
	@echo ""
	@echo "Build targets:"
	@echo "  all              - Build both kloak and klor binaries"
	@echo "  build            - Build the kloak binary (native)"
	@echo "  build-klor       - Build the klor host-CLI binary (native)"
	@echo "  build-linux      - Build kloak for Linux (uses Lima on macOS)"
	@echo "  build-klor-linux - Build klor for Linux (uses Lima on macOS)"
	@echo "  clean            - Clean build artifacts"
	@echo "  deps             - Download and tidy dependencies"
	@echo "  docker-build     - Build Docker image"
	@echo "  run              - Build and run kloak locally"
	@echo ""
	@echo "Test targets:"
	@echo "  test             - Run unit tests"
	@echo "  test-linux       - Run tests in Lima VM (needed for eBPF tests)"
	@echo "  test-bpf-helpers - Run eBPF helper C unit tests"
	@echo ""
	@echo "k3d-based e2e (k8s tests; macOS-friendly via Docker):"
	@echo "  e2e              - Full e2e: setup + run + cleanup"
	@echo "  e2e-setup        - Create k3d cluster with BPF mounts and import images"
	@echo "  e2e-run          - Run e2e tests against the existing k3d cluster"
	@echo "  e2e-cleanup      - Delete e2e k3d cluster"
	@echo "  e2e-local        - Build + push images to ttl.sh and run e2e against current kube context"
	@echo "  e2e-local-push   - Build + push e2e images to ttl.sh (or E2E_REGISTRY)"
	@echo "  e2e-local-run    - Run e2e against existing kube context (assumes images pushed)"
	@echo ""
	@echo "k3s-native e2e (CI / Linux workstations — no Docker nesting):"
	@echo "  e2e-k3s          - Full e2e: setup + run + cleanup (k3s-native)"
	@echo "  e2e-k3s-setup    - Install k3s, build and import images"
	@echo "  e2e-k3s-run      - Run e2e tests against local k3s"
	@echo "  e2e-k3s-cleanup  - Uninstall k3s"
	@echo ""
	@echo "klor e2e (no cluster required):"
	@echo "  e2e-klor         - Build klor and exercise its CLI (Linux-only; self-skips on macOS)"
	@echo "  e2e-klor-rooted  - Run install.sh + verify cap-on-binary lets klor run without sudo (needs root)"
	@echo ""
	@echo "eBPF code generation:"
	@echo "  generate-ebpf    - Generate eBPF Go bindings (uses Lima on macOS)"
	@echo "  generate-vmlinux - Generate vmlinux.h from kernel BTF"
	@echo "  go-tls-fixtures  - Build / refresh Go TLS offset fixtures"
	@echo "  go-tls-discover  - Discover new Go TLS offsets across versions"
	@echo ""
	@echo "Lima VM (for eBPF on macOS):"
	@echo "  lima-start       - Start the Lima VM"
	@echo "  lima-stop        - Stop the Lima VM"
	@echo "  lima-delete      - Delete the Lima VM"
	@echo "  lima-shell       - Open shell in Lima VM"
	@echo "  lima-ensure      - Ensure VM is running (idempotent)"
	@echo ""
	@echo "Lima k3d (for k8s e2e on ARM Mac):"
	@echo "  lima-k3d-shell      - Shell into k3d Lima VM"
	@echo "  lima-k3d-e2e        - Full e2e: setup + run inside Lima k3d VM"
	@echo "  lima-k3d-e2e-setup  - Create k3d cluster inside Lima VM"
	@echo "  lima-k3d-e2e-run    - Run e2e tests inside Lima VM"
	@echo "  lima-k3d-stop       - Stop k3d Lima VM"
	@echo "  lima-k3d-delete     - Delete k3d Lima VM"
