project_name := "baibot"
container_image_name := "localhost/baibot"
project_container_network := "baibot"

admin_username := "admin"
admin_password := "admin"
bot_username := "baibot"
bot_password := "baibot"

homeserver := `cat var/homeserver 2>/dev/null || echo continuwuity`

mise_data_dir := env("MISE_DATA_DIR", justfile_directory() / "var/mise")
mise_trusted_config_paths := justfile_directory() / "mise.toml"

# Show help by default
default:
	@just --list --justfile {{ justfile() }}

# Selects which homeserver implementation to use (continuwuity or synapse)
homeserver-init value:
	#!/bin/sh
	mkdir -p {{ justfile_directory() }}/var
	echo {{ value }} > {{ justfile_directory() }}/var/homeserver
	echo ""
	echo "⚠️  If you had already prepared your app configuration (var/app/local/config.yml or var/app/container/config.yml),"
	echo "    you will need to update it manually or delete it and re-run the prepare step."
	echo "    You should also delete var/app/local/data and/or var/app/container/data,"
	echo "    as old application state is not compatible across homeserver implementations."
	echo ""
	echo "⚠️  If Element Web was already prepared, delete var/services/element-web/ to regenerate its config."

# Builds and runs a development binary
run-locally *extra_args: app-local-prepare
	RUST_BACKTRACE=1 \
	BAIBOT_CONFIG_FILE_PATH={{ justfile_directory() }}/var/app/local/config.yml \
	BAIBOT_PERSISTENCE_DATA_DIR_PATH={{ justfile_directory() }}/var/app/local/data \
	cargo run -- {{ extra_args }}

# Builds and runs the bot in a container
run-in-container *extra_args: app-container-prepare build-container-image-debug
	/usr/bin/env docker run \
	-it \
	--rm \
	--name={{ project_name }} \
	--user=$(id -u):$(id -g) \
	--cap-drop=ALL \
	--read-only \
	--network={{ project_container_network }} \
	--env BAIBOT_PERSISTENCE_DATA_DIR_PATH=/data \
	--mount type=bind,src={{ justfile_directory() }}/var/app/container/config.yml,dst=/app/config.yml,ro \
	--mount type=bind,src={{ justfile_directory() }}/var/app/container/data,dst=/data \
	{{ container_image_name }}:latest {{ extra_args }}

# Runs tests
test *extra_args:
	RUST_BACKTRACE=1 cargo test {{ extra_args }}

# Formats the code
fmt:
	RUST_BACKTRACE=1 cargo fmt --all

# Builds a debug binary (target/debug/*)
build-debug *extra_args:
	RUST_BACKTRACE=1 cargo build {{ extra_args }}

# Builds an optimized release binary (target/release/*)
build-release *extra_args: (build-debug "--release")

# Builds a container image (debug mode)
build-container-image-debug tag='latest': (_build-container-image "false" tag)

# Builds a container image (release mode)
build-container-image-release tag='latest': (_build-container-image "true" tag)

_build-container-image release_build tag:
	/usr/bin/env docker build \
	--build-arg RELEASE_BUILD={{ release_build }} \
	-f {{ justfile_directory() }}/Dockerfile \
	-t {{ container_image_name }}:{{ tag }} \
	.

# Runs a docker-compose command
docker-compose services_type *extra_args:
	/usr/bin/docker compose \
	--project-directory var/services \
	--env-file var/services/env \
	-f etc/services/{{ services_type }}/compose.yml \
	-p {{ project_name }}-{{ services_type }} \
	{{ extra_args }}

# Runs a docker-compose command against the synapse services
docker-compose-synapse *extra_args:
	just docker-compose synapse {{ extra_args }}

# Runs a docker-compose command against the element-web services
docker-compose-element-web *extra_args:
	just docker-compose element-web {{ extra_args }}

# Runs a docker-compose command against the localai services
docker-compose-localai *extra_args:
	just docker-compose localai {{ extra_args }}

# Runs a docker-compose command against the ollama services
docker-compose-ollama *extra_args:
	just docker-compose ollama {{ extra_args }}

# Runs a docker-compose command against the continuwuity services
docker-compose-continuwuity *extra_args:
	just docker-compose continuwuity {{ extra_args }}

# Runs the homeserver and Element Web (in the background)
services-start: services-prepare
	just -f {{ justfile_directory() }}/justfile {{ homeserver }}-start
	just -f {{ justfile_directory() }}/justfile element-web-start

# Stops Element Web and the homeserver
services-stop:
	just -f {{ justfile_directory() }}/justfile element-web-stop
	just -f {{ justfile_directory() }}/justfile {{ homeserver }}-stop

# Tails the logs for the homeserver and Element Web
services-tail-logs:
	just -f {{ justfile_directory() }}/justfile {{ homeserver }}-tail-logs

# Prepares the homeserver and Element Web for running
services-prepare:
	just -f {{ justfile_directory() }}/justfile {{ homeserver }}-prepare
	just -f {{ justfile_directory() }}/justfile element-web-prepare

# Runs Synapse (in the background)
synapse-start: synapse-prepare (docker-compose-synapse "up" "-d")

# Stops Synapse
synapse-stop: (docker-compose-synapse "down")

# Tails the logs for Synapse
synapse-tail-logs: (docker-compose-synapse "logs" "-f")

# Prepares Synapse for running
synapse-prepare: _prepare-var-services-env _prepare-var-services-postgres _prepare-var-services-synapse _prepare-container-network

# Runs Element Web (in the background)
element-web-start: element-web-prepare (docker-compose-element-web "up" "-d")

# Stops Element Web
element-web-stop: (docker-compose-element-web "down")

# Tails the logs for Element Web
element-web-tail-logs: (docker-compose-element-web "logs" "-f")

# Prepares Element Web for running
element-web-prepare: _prepare-var-services-env _prepare-var-services-element-web _prepare-container-network

# Runs LocalAI (in the background)
localai-start: localai-prepare (docker-compose-localai "up" "-d")

# Stops LocalAI
localai-stop: (docker-compose-localai "down")

# Tails the logs for LocalAI
localai-tail-logs: (docker-compose-localai "logs" "-f")

# Prepares LocalAI for running
localai-prepare: _prepare-var-services-env _prepare-var-services-localai _prepare-container-network

# Runs Ollama (in the background)
ollama-start: ollama-prepare (docker-compose-ollama "up" "-d")

# Stops Ollama
ollama-stop: (docker-compose-ollama "down")

# Tails the logs for Ollama
ollama-tail-logs: (docker-compose-ollama "logs" "-f")

# Prepares Ollama for running
ollama-prepare: _prepare-var-services-env _prepare-var-services-ollama _prepare-container-network

# Runs Continuwuity (in the background)
continuwuity-start: continuwuity-prepare (docker-compose-continuwuity "up" "-d")

# Stops Continuwuity
continuwuity-stop: (docker-compose-continuwuity "down")

# Tails the logs for Continuwuity
continuwuity-tail-logs: (docker-compose-continuwuity "logs" "-f")

# Prepares Continuwuity for running
continuwuity-prepare: _prepare-var-services-env _prepare-var-services-continuwuity _prepare-container-network

# Registers a user on Continuwuity via the Matrix Client-Server API
continuwuity-register-user username password:
	{{ justfile_directory() }}/etc/services/continuwuity/register-user.sh {{ justfile_directory() }}/var/services/env {{ username }} {{ password }}

# Prepares the Continuwuity user accounts
continuwuity-users-prepare: continuwuity-prepare
	just -f {{ justfile_directory() }}/justfile continuwuity-register-user "{{ admin_username }}" "{{ admin_password }}"
	just -f {{ justfile_directory() }}/justfile continuwuity-register-user "{{ bot_username }}" "{{ bot_password }}"

# Pulls an Ollama model
ollama-pull-model model_id:
	just -f {{ justfile_directory() }}/justfile docker-compose-ollama \
		exec ollama \
		ollama pull {{ model_id }}

# Prepares the app for running locally
app-local-prepare: _prepare-var-app-local-config_yml _prepare-var-app-local-data

# Prepares the app for running in a container
app-container-prepare: _prepare-var-app-container-config_yml _prepare-var-app-container-data

# Prepares the user accounts
users-prepare:
	just -f {{ justfile_directory() }}/justfile {{ homeserver }}-users-prepare

# Prepares the Synapse user accounts
synapse-users-prepare: synapse-prepare
	just -f {{ justfile_directory() }}/justfile synapse-register-admin-user "{{ admin_username }}" "{{ admin_password }}"
	just -f {{ justfile_directory() }}/justfile synapse-register-regular-user "{{ bot_username }}" "{{ bot_password }}"

# Starts a Postgres CLI (psql)
postgres-cli: synapse-prepare (docker-compose-synapse "exec" "postgres" "/bin/sh" "-c" "'PGUSER=synapse PGPASSWORD=synapse-password PGDATABASE=homeserver psql -h postgres'")

# Creates an administrator user on Synapse
synapse-register-admin-user username password: synapse-prepare
	just -f {{ justfile_directory() }}/justfile docker-compose-synapse \
		exec synapse \
		register_new_matrix_user \
		--admin \
		-u {{ username }} \
		-p {{ password }} \
		-c /config/homeserver.yaml \
		http://localhost:8008

# Creates a regular user on Synapse
synapse-register-regular-user username password: synapse-prepare
	just -f {{ justfile_directory() }}/justfile docker-compose-synapse \
		exec synapse \
		register_new_matrix_user \
		--no-admin \
		-u {{ username }} \
		-p {{ password }} \
		-c /config/homeserver.yaml \
		http://localhost:8008

# Runs the clippy linter
clippy *extra_args:
	cargo clippy {{ extra_args }}

# Checks that the code compiles without building
check:
	cargo check

# Invokes mise with the project-local data directory
mise *args: _ensure_mise_data_directory
	#!/bin/sh
	export MISE_DATA_DIR="{{ mise_data_dir }}"
	export MISE_TRUSTED_CONFIG_PATHS="{{ mise_trusted_config_paths }}"
	mise {{ args }}

# Runs prek (pre-commit hooks manager) with the given arguments
prek *args: _ensure_mise_tools_installed
	@just --justfile {{ justfile() }} mise exec -- prek {{ args }}

# Runs pre-commit hooks on staged files
prek-run-on-staged *args: _ensure_mise_tools_installed
	@just --justfile {{ justfile() }} mise exec -- prek run {{ args }}

# Runs pre-commit hooks on all files
prek-run-on-all *args: _ensure_mise_tools_installed
	@just --justfile {{ justfile() }} mise exec -- prek run --all-files {{ args }}

# Installs the git pre-commit hook (runs prek automatically before each commit)
prek-install-git-pre-commit-hook: _ensure_mise_tools_installed
	#!/usr/bin/env sh
	set -eu
	just --justfile {{ justfile() }} mise exec -- prek install
	# The installed git hooks run later under Git, outside this just/mise environment,
	# so they need to be told how to find their tooling:
	#
	# - MISE_DATA_DIR / MISE_TRUSTED_CONFIG_PATHS make mise resolve against this project's
	#   own data directory. Without them mise falls back to the global one and silently
	#   installs a second copy of the tool there.
	# - prek bakes the full path of the currently installed version into the hook
	#   (var/mise/installs/prek/<version>/...), which stops working as soon as the pinned
	#   version changes or old versions are pruned. Pointing at mise's shim instead makes
	#   the hook resolve whatever mise.toml pins, at the time it runs.
	#
	# Which hook files prek installs depends on `default_install_hook_types` in
	# .pre-commit-config.yaml, so patch every hook file that prek generated.
	for hook in "{{ justfile_directory() }}"/.git/hooks/*; do
		[ -f "$hook" ] || continue
		grep -q 'generated by prek' "$hook" || continue
		grep -q '^export MISE_DATA_DIR=' "$hook" || sed -i '2iexport MISE_DATA_DIR="{{ mise_data_dir }}"' "$hook"
		grep -q '^export MISE_TRUSTED_CONFIG_PATHS=' "$hook" || sed -i '3iexport MISE_TRUSTED_CONFIG_PATHS="{{ mise_trusted_config_paths }}"' "$hook"
		sed -i 's#^PREK=".*"$#PREK="{{ mise_data_dir }}/shims/prek"#' "$hook"
	done

# Internal - ensures var/mise directory exists
_ensure_mise_data_directory:
	#!/bin/sh
	if [ ! -d "{{ mise_data_dir }}" ]; then
		mkdir -p "{{ mise_data_dir }}"
	fi

# Internal - ensures mise tools are installed
_ensure_mise_tools_installed: _ensure_mise_data_directory
	@just --justfile {{ justfile() }} mise install --quiet

_prepare-var-services-env:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/services/env ]; then
		mkdir -p var/services
		cp {{ justfile_directory() }}/etc/services/env.dist var/services/env
		echo 'UID='`id -u` >> var/services/env;
		echo 'GID='`id -g` >> var/services/env;
		echo 'NETWORK_NAME={{ project_container_network }}' >> var/services/env;
	fi

_prepare-var-services-postgres:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/services/postgres ]; then
		mkdir -p var/services/postgres
		chown `id -u`:`id -g` var/services/postgres
	fi

_prepare-var-services-synapse:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/services/synapse ]; then
		mkdir -p var/services/synapse/media-store
	fi

_prepare-var-services-element-web:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/services/element-web/config.json ]; then
		mkdir -p var/services/element-web
		cp {{ justfile_directory() }}/etc/services/element-web/config.json.dist var/services/element-web/config.json

		homeserver="{{ homeserver }}"
		if [ "$homeserver" = "continuwuity" ]; then
			sed --in-place 's|__HOMESERVER_CLIENT_URL__|http://continuwuity.127.0.0.1.nip.io:42030|g' var/services/element-web/config.json
		elif [ "$homeserver" = "synapse" ]; then
			sed --in-place 's|__HOMESERVER_CLIENT_URL__|http://synapse.127.0.0.1.nip.io:42020|g' var/services/element-web/config.json
		fi
	fi

_prepare-var-services-ollama:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/services/ollama ]; then
		mkdir -p var/services/ollama
	fi

_prepare-var-services-continuwuity:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/services/continuwuity ]; then
		mkdir -p var/services/continuwuity/data
	fi

_prepare-var-services-localai:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/services/localai ]; then
		mkdir -p var/services/localai
	fi

_prepare-container-network:
	#!/bin/sh
	network_definition=$(/usr/bin/env docker network ls --filter='name={{ project_container_network }}' --format=json)

	if [ "$network_definition" = "" ]; then
		/usr/bin/docker network create {{ project_container_network }}
	fi

_prepare-var-app-local-config_yml:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/app/local/config.yml ]; then
		mkdir -p var/app/local
		cp {{ justfile_directory() }}/etc/app/config.yml.dist var/app/local/config.yml

		homeserver="{{ homeserver }}"
		if [ "$homeserver" = "continuwuity" ]; then
			sed --in-place 's/__HOMESERVER_SERVER_NAME__/continuwuity.127.0.0.1.nip.io/g' var/app/local/config.yml
			sed --in-place 's|__HOMESERVER_URL__|http://continuwuity.127.0.0.1.nip.io:42030|g' var/app/local/config.yml
		elif [ "$homeserver" = "synapse" ]; then
			sed --in-place 's/__HOMESERVER_SERVER_NAME__/synapse.127.0.0.1.nip.io/g' var/app/local/config.yml
			sed --in-place 's|__HOMESERVER_URL__|http://synapse.127.0.0.1.nip.io:42020|g' var/app/local/config.yml
		fi
	fi

_prepare-var-app-local-data:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/app/local/data ]; then
		mkdir -p var/app/local/data
	fi

_prepare-var-app-container-config_yml:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/app/container/config.yml ]; then
		mkdir -p var/app/container
		cp {{ justfile_directory() }}/etc/app/config.yml.dist var/app/container/config.yml

		homeserver="{{ homeserver }}"
		if [ "$homeserver" = "continuwuity" ]; then
			sed --in-place 's/__HOMESERVER_SERVER_NAME__/continuwuity.127.0.0.1.nip.io/g' var/app/container/config.yml
			sed --in-place 's|__HOMESERVER_URL__|http://continuwuity.127.0.0.1.nip.io:42030|g' var/app/container/config.yml
			sed --in-place 's/continuwuity.127.0.0.1.nip.io:42030/continuwuity:6167/g' var/app/container/config.yml
		elif [ "$homeserver" = "synapse" ]; then
			sed --in-place 's/__HOMESERVER_SERVER_NAME__/synapse.127.0.0.1.nip.io/g' var/app/container/config.yml
			sed --in-place 's|__HOMESERVER_URL__|http://synapse.127.0.0.1.nip.io:42020|g' var/app/container/config.yml
			sed --in-place 's/synapse.127.0.0.1.nip.io:42020/synapse:8008/g' var/app/container/config.yml
		fi

		sed --in-place 's/127.0.0.1:42026/ollama:11434/g' var/app/container/config.yml
		sed --in-place 's/127.0.0.1:42027/localai:8080/g' var/app/container/config.yml
	fi

_prepare-var-app-container-data:
	#!/bin/sh
	cd {{ justfile_directory() }};

	if [ ! -f var/app/container/data ]; then
		mkdir -p var/app/container/data
	fi
