# ------------------
# Configuration sources
BASE_PROJECT_DIR := $(or $(BASE_PROJECT_DIR),$(shell pwd))
AIV_CONFIG := $(or $(AIV_CONFIG),$(BASE_PROJECT_DIR)/aiv-config.yml)

$(info BASE_PROJECT_DIR: $(BASE_PROJECT_DIR))
$(info AIV_CONFIG: $(AIV_CONFIG))

define aiv_config
$(shell < $(AIV_CONFIG) awk -F: '$$1 ~ /^ *$(1)$$/ {print $$2; found=1} END {if (found!=1) print "$(2)"}' | xargs)
endef

# ------------------
# Configuration variables

# example: linux/amd64, darwin/arm64

ifeq ($(PLATFORM),)
# detect platform
UNAME_S := $(shell uname -s)
UNAME_M := $(shell uname -m)

ifeq ($(UNAME_S),Linux)
  ifeq ($(UNAME_M),x86_64)
	PLATFORM=linux/amd64
  else ifeq ($(UNAME_M),aarch64)
	PLATFORM=linux/arm64
  else
	$(error Unsupported architecture: $(UNAME_M))
  endif
else ifeq ($(UNAME_S),Darwin)
  ifeq ($(UNAME_M),x86_64)
	PLATFORM=darwin/amd64
  else ifeq ($(UNAME_M),arm64)
	PLATFORM=darwin/arm64
  else
	$(error Unsupported architecture: $(UNAME_M))
  endif
else
  $(error Unsupported OS: $(UNAME_S))
endif

$(info Auto-detected PLATFORM: $(PLATFORM))

else
$(info Using user-defined PLATFORM: $(PLATFORM))
endif


KUBECTL_VERSION?=1.31.2
HELM_VERSION?=3.17.3
KIND_VERSION?=0.30.0

REGISTRY_IMAGE=harbor.cta-observatory.org/proxy_cache/registry:2

TOOLKITDIR=$(PWD)/.toolkit
WGET?=wget
YQ?=$(TOOLKITDIR)/bin/yq
HELM?=$(TOOLKITDIR)/bin/helm
KIND?=$(TOOLKITDIR)/bin/kind
KUBECTL?=$(TOOLKITDIR)/bin/kubectl
export KIND KUBECTL HELM YQ WGET

N_IMAGE_PULL_JOBS?=2

KUBECLUSTER?=aiv-local-$(shell basename ${PWD})
KUBECONFIG?=.toolkit/kubeconfig.yaml
export KUBECONFIG

# kind cluster configuration
IF_CLUSTER_EXISTS?=reuse
# KIND_CONFIG file takes precedence over individual settings; set it to empty to generate config from settings
KIND_CONFIG?=$(call aiv_config,KIND_CONFIG,)
ENABLE_INGRESS?=$(call aiv_config,ENABLE_INGRESS,false)
ENABLE_REGISTRY_MIRRORS?=$(call aiv_config,ENABLE_REGISTRY_MIRRORS,true)
MOUNT_REPO?=$(call aiv_config,MOUNT_REPO,true)
HARBOR_PROJECT?=$(call aiv_config,HARBOR_PROJECT,dpps)

# file with randomized names for release, namespace, etc.
RANDOM_NAMES_FILE=$(TOOLKITDIR)/random-instance-names.yaml

# Chart configuration
CHART_LOCATION?=$(call aiv_config,CHART_LOCATION)
CHART_NAME?=$(call aiv_config,CHART_NAME)

CHART_VERSION?=dev
CHART_VALUES?=$(call aiv_config,CHART_VALUES)
CHART_EXTRA_VALUES?=$(call aiv_config,CHART_EXTRA_VALUES)

ifneq ($(CHART_VALUES),)
export CHART_EXTRA_VALUES:=-f $(CHART_VALUES) $(CHART_EXTRA_VALUES)
endif

# TODO: this should be taken from a variable
CHART_APP_VERSION?=dev

HELM_REPO_CONFIG?=$(call aiv_config,HELM_REPO_CONFIG)

ifneq ($(HELM_REPO_CONFIG),)
export HELM_DEP_EXTRA_ARGS:=--repository-config $(HELM_REPO_CONFIG) $(HELM_DEP_EXTRA_ARGS)
endif

HARBOR_LOGIN?=dpps+gitlab-publisher

# docker image configuration
AIV_DOCKER_IMAGE_CONTEXT?=$(call aiv_config,AIV_DOCKER_IMAGE_CONTEXT,$(PWD))
$(info AIV_DOCKER_IMAGE_CONTEXT=$(AIV_DOCKER_IMAGE_CONTEXT))
AIV_DOCKERFILE?=$(call aiv_config,AIV_DOCKERFILE,${AIV_DOCKER_IMAGE_CONTEXT}/Dockerfile)
$(info AIV_DOCKERFILE=$(AIV_DOCKERFILE))

DEV_DOCKER_IMAGE_CONTEXT?=${AIV_DOCKER_IMAGE_CONTEXT}
DEV_DOCKERFILE?=${AIV_DOCKERFILE}


CHART_IMAGE_LIST?=$(TOOLKITDIR)/chart-image-list.txt
PULL_STATISTICS_JSON?=$(TOOLKITDIR)/stats/pull-images-statistics.json

MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
KIT_BASE_DIR := $(dir $(MKFILE_PATH))
TEST_REPORT_CONFIG?=$(AIV_CONFIG)

INGRESS_TIMEOUT?=300s

# ------------------
# Print configuration

$(info KIND binary: $(KIND))
$(info KUBECTL binary: $(KUBECTL))
$(info HELM binary: $(HELM))
$(info YQ binary: $(YQ))
$(info )
$(info CHART_LOCATION: $(CHART_LOCATION))
$(info CHART_NAME: $(CHART_NAME))
$(info CHART_VERSION: $(CHART_VERSION))
$(info CHART_VALUES: $(CHART_VALUES))
$(info CHART_EXTRA_VALUES: $(CHART_EXTRA_VALUES))
$(info )
$(info To use the cluster, export KUBECONFIG=$(KUBECONFIG) or run 'make export-kubeconfig')
$(info )

ifeq ($(HARBOR_TOKEN),)
$(info NOTE: HARBOR_TOKEN not set or empty: using public-only images read-only)
endif

# ------------------
# Targets

default: dev


help:
	aiv-deploy help-variables


check-tookit-installed:
	@if ! command -v aiv-deploy > /dev/null; then \
		echo -e "\nPlease install the aiv toolkit python package, we recommend uv: uv tool install ./aiv-toolkit"; \
		exit 1; \
	fi


test-chart: check-tookit-installed install-chart install-testkit
	aiv-deploy helm-test

collect-reports: | $(TOOLKITDIR)/logs
	for fn in report.xml coverage.xml; do \
		$(KUBECTL) exec $$($(KUBECTL) get pod -l app=testkit -o name) -- cat /test-results/$$fn > $$fn || echo "No such file: $$fn"; \
	done
	for fn in logs.ndjson logs.txt ;do \
		$(KUBECTL) exec $$($(KUBECTL) get pod -l app=testkit -o name) -- cat /test-results/$$fn > $(TOOLKITDIR)/logs/$$fn; \
	done
	# this is to show the logs in the end
	tail -n 1000 $(TOOLKITDIR)/logs/logs.txt
	# TODO: collect from /sys/fs/cgroup/{memory*,cpu*} and /proc/{meminfo,cpuinfo}

testkit-deps:
	${HELM} dependency $(HELM_DEP_EXTRA_ARGS) build ${KIT_BASE_DIR}/charts/testkit

install-testkit: testkit-deps setup-k8s-cluster pull-load-helm-images
	aiv-deploy \
		--chart-location ${KIT_BASE_DIR}/charts/testkit \
		--randomized-release-name=False \
		helm-upgrade \
			--release-name toolkit \
			--chart-extra-values "--set fluent-bit.collect_only_tests=$(call aiv_config,COLLECT_ONLY_TESTS,false) --set stream_only_tests=$(call aiv_config,STREAM_ONLY_TESTS,true)"

PLATFORM_WITH_DASHES=$(shell echo ${PLATFORM} | tr / -)
PLATFORM_WITH_UNDERSCORES=$(shell echo ${PLATFORM} | tr / _)

$(TOOLKITDIR)/bin/kind: | $(TOOLKITDIR)/bin
	${WGET} -q -O $@ https://kind.sigs.k8s.io/dl/v${KIND_VERSION}/kind-${PLATFORM_WITH_DASHES}
	chmod +x $@

$(TOOLKITDIR)/bin/kubectl: | $(TOOLKITDIR)/bin
	${WGET} -q -O $@ https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/${PLATFORM}/kubectl
	chmod +x $@

$(TOOLKITDIR)/bin/helm: | $(TOOLKITDIR)/bin
	${WGET} -q -O- https://get.helm.sh/helm-v${HELM_VERSION}-${PLATFORM_WITH_DASHES}.tar.gz | tar -C $(TOOLKITDIR)/bin/ --strip-components 1 -zxvf - ${PLATFORM_WITH_DASHES}/helm
	chmod +x $@

$(TOOLKITDIR)/bin/yq: | $(TOOLKITDIR)/bin
	${WGET} -q -O $@ https://github.com/mikefarah/yq/releases/download/v4.45.4/yq_${PLATFORM_WITH_UNDERSCORES}
	chmod +x $@

install-tools: $(addprefix $(TOOLKITDIR)/bin/, kind kubectl helm yq)

setup-k8s-cluster: $(KUBECONFIG)


$(KUBECONFIG): install-tools ${KIND_CONFIG} $(TOOLKITDIR)/stats
	aiv-deploy \
		--kind-cluster-name ${KUBECLUSTER} \
		kind-up \
			--kind-config "${KIND_CONFIG}" \
			--kubeconfig ${KUBECONFIG}

destroy-k8s-cluster: install-tools
	aiv-deploy \
		--kind-cluster-name ${KUBECLUSTER} \
		kind-down

HELM_DEP_CMD := build
ifeq ($(CHART_VERSION),dev)
	HELM_DEP_CMD = update
endif


ifeq ($(SKIP_HELM_DEPS),yes)
	$(warning skipping help dependencies update, use with caution)
else
update-helm-deps: install-tools
	# the loop is needed because update is not recursive
	set -xe && \
	for chart in $$(find -name Chart.yaml -not -wholename \*/charts/testkit/\* | sed 's@/Chart.yaml$$@@g'); do \
	    ${HELM} dependency $(HELM_DEP_EXTRA_ARGS) ${HELM_DEP_CMD}  $$chart; \
	done
endif

update-toolkit-helm-deps: install-tools
	# the loop is needed because update is not recursive
	set -xe && \
	for chart in $$(find -name Chart.yaml -wholename \*/charts/testkit/\* | sed 's@/Chart.yaml$$@@g'); do \
	    ${HELM} dependency $(HELM_DEP_EXTRA_ARGS) $(HELM_DEP_CMD)  $$chart; \
	done

ifeq ($(SKIP_HELM_DEPS),yes)
install-chart: harbor-login install-testkit pull-load-helm-images
else
install-chart: harbor-login install-testkit update-helm-deps pull-load-helm-images
endif
	aiv-deploy \
		helm-upgrade

ifeq ($(HARBOR_TOKEN),)
harbor-login-helm: install-tools
harbor-login-docker:
else
harbor-login-helm: install-tools
	@echo ${HARBOR_TOKEN} | ${HELM} registry login -u 'robot$$'"${HARBOR_LOGIN}" --password-stdin harbor.cta-observatory.org

harbor-login-docker:
	@echo ${HARBOR_TOKEN} | docker login -u 'robot$$'"${HARBOR_LOGIN}" --password-stdin harbor.cta-observatory.org
endif

harbor-login: harbor-login-helm harbor-login-docker

### Interactive Dev setup

DEV_TAG?=dev

DEV_IMAGE_SUFFIX?=-client
DEV_DOCKER_EXTRA_ARGS?=
DEV_IMAGE_REPO=harbor.cta-observatory.org/${HARBOR_PROJECT}/$(CHART_NAME)$(DEV_IMAGE_SUFFIX)

export DEV_IMAGE=$(DEV_IMAGE_REPO):$(DEV_TAG)

# TODO: build and load all dev images, #139

# in local env, DEV_TAG is set to dev, the image is build locally and loaded into cluster, without pushing
ifeq ($(DEV_TAG),dev)
build-dev-docker:
	docker build -t $(DEV_IMAGE) \
		--build-arg USERID=$(shell id -u) \
		--build-arg GROUPID=$(shell id -g) \
		${DEV_DOCKER_EXTRA_ARGS} \
		${DEV_DOCKER_IMAGE_CONTEXT} -f ${DEV_DOCKERFILE}

load-dev-docker: build-dev-docker setup-k8s-cluster
	$(KIND) load -n ${KUBECLUSTER} docker-image $(DEV_IMAGE) --nodes ${KUBECLUSTER}-control-plane

dev: load-dev-docker install-testkit
endif

dev: setup-k8s-cluster pull-load-helm-images
	aiv-deploy \
		helm-dev


report:
	$(MAKE) -C $(KIT_BASE_DIR)/report AIV_CONFIG=$(AIV_CONFIG) BASE_PROJECT_DIR=$(BASE_PROJECT_DIR)

clean-images:
	docker exec -it ${KUBECLUSTER}-control-plane crictl rmi --prune

registry-mirror:
	docker inspect --type network kind > /dev/null || docker network create kind
	docker inspect proxy-docker-hub >/dev/null || docker run -d --name proxy-docker-hub --restart=always   --net=kind   -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io  ${REGISTRY_IMAGE}
	docker inspect proxy-harbor-ctao >/dev/null || docker run -d --name proxy-harbor-ctao --restart=always   --net=kind   -e REGISTRY_PROXY_REMOTEURL=https://harbor.cta-observatory.org  ${REGISTRY_IMAGE}
	docker inspect proxy-quay >/dev/null || docker run -d --name proxy-quay --restart=always   --net=kind   -e REGISTRY_PROXY_REMOTEURL=https://quay.io  ${REGISTRY_IMAGE}
	docker inspect proxy-ghcr >/dev/null || docker run -d --name proxy-ghcr --restart=always   --net=kind   -e REGISTRY_PROXY_REMOTEURL=https://ghcr.io  ${REGISTRY_IMAGE}


# inspection on the cluster and chart content

dump-cluster-image-list: install-tools
	$(KUBECTL) get pods --all-namespaces -o jsonpath="{.items[*].spec['initContainers', 'containers'][*].image}" \
		|tr -s '[[:space:]]' '\n' |sort |uniq | tee cluster-images.txt

report-helm-images: $(CHART_IMAGE_LIST)

$(CHART_IMAGE_LIST): testkit-deps update-helm-deps install-tools | $(TOOLKITDIR)
	$(HELM) template $(KIT_BASE_DIR)/charts/testkit | $(YQ) '.. | select(has("image")) | .image'  | sort -u | awk '!/---/' | tee $(CHART_IMAGE_LIST)
	$(HELM) template $(CHART_LOCATION) $(CHART_EXTRA_VALUES) | $(YQ) '.. | select(has("image")) | .image'  | sort -u | awk '!/---/' | tee -a $(CHART_IMAGE_LIST)

pull-load-helm-images: $(CHART_IMAGE_LIST) setup-k8s-cluster check-tookit-installed
	aiv-deploy \
			--kind-cluster-name ${KUBECLUSTER} \
			kind-pull-images \
				--image-list-file $(CHART_IMAGE_LIST) \
				--max-pull-jobs $(N_IMAGE_PULL_JOBS) \
				--statistics-json $(PULL_STATISTICS_JSON)

$(TOOLKITDIR) $(TOOLKITDIR)/bin $(TOOLKITDIR)/logs $(TOOLKITDIR)/stats:
	mkdir -pv $@

# can be used to connect to the cluster workloads from local environment through proxy-pass or ingress
DPPS_CERTS_DIR?=${TOOLKITDIR}/certs

fetch-cluster-certs:
	mkdir -pv ${DPPS_CERTS_DIR}
	kubectl get secrets ${CHART_NAME}-server-cafile -o jsonpath='{.data}' | jq '.["ca.pem"]' -rc | base64 -d > ${DPPS_CERTS_DIR}/ca.pem
	kubectl get secrets ${CHART_NAME}-server-cafile -o jsonpath='{.data}' | jq '.["dpps_test_ca.crl.r0"]' -rc | base64 -d > ${DPPS_CERTS_DIR}/crl
	kubectl get secrets ${CHART_NAME}-dppsuser-certkey -o jsonpath='{.data}' | jq '.["x509up"]' -rc | base64 -d > ${DPPS_CERTS_DIR}/x509up
	kubectl get secrets ${CHART_NAME}-dppsuser-certkey -o jsonpath='{.data}' | jq '.["dppsuser.key.pem"]' -rc | base64 -d > ${DPPS_CERTS_DIR}/dppsuser.key.pem
	kubectl get secrets ${CHART_NAME}-dppsuser-certkey -o jsonpath='{.data}' | jq '.["dppsuser.pem"]' -rc | base64 -d > ${DPPS_CERTS_DIR}/dppsuser.pem


# Export kubeconfig for the cluster in current default location
export-kubeconfig:
	KUBECONFIG="" $(KIND) export kubeconfig --name ${KUBECLUSTER}

load-toolkit-dev:
	$(MAKE) load-dev-docker AIV_DOCKER_IMAGE_CONTEXT=docker-image DEV_IMAGE_SUFFIX="-bootstrap"


ifeq ($(ENABLE_REGISTRY_MIRRORS),true)
pull-load-helm-images: registry-mirror
endif

.PHONY: dev report
