# Maintainer: dispatch
#
# Split package: builds the base application once and produces three packages:
#   dispatch           — application files + CLI wrappers + env configs
#   dispatch-systemd   — systemd system units (run as a given user: dispatch-api@<user>)
#   dispatch-s6        — s6 service definitions for dispatch-api / dispatch-frontend
#
# The Electron desktop wrapper lives in a separate PKGBUILD at packaging/electron/.
#
# Build with:
#   bin/build-pkg                # makepkg -fd in packaging/

pkgbase=dispatch
pkgname=('dispatch' 'dispatch-systemd' 'dispatch-s6' 'code-search')
pkgver=0.0.1
pkgrel=1
arch=('x86_64')
url='https://github.com/anomalyco/dispatch'
license=('MIT')
makedepends=('bun' 'go' 'git')

# Pinned cs (code spelunker) commit (tag v3.1.0) built for the search_code tool.
# Kept in lockstep with the Docker build (see Dockerfile / Dockerfile.dev).
_cs_commit=697e0bf194bbc7a4a877e5170c70618989fc92e7
# All static files are read directly from ${_projectdir}/packaging/. We don't
# use source=() because two of the s6 files share basenames (run, type), which
# would collide inside ${srcdir}.
source=()
sha256sums=()

_projectdir="${startdir}/.."
_packagingdir="${_projectdir}/packaging"

prepare() {
	mkdir -p "${srcdir}/dispatch-${pkgver}"

	# Copy project files into the src build directory (preserve symlinks)
	cp -a \
		"${_projectdir}/packages" \
		"${_projectdir}/package.json" \
		"${_projectdir}/bun.lock" \
		"${_projectdir}/tsconfig.base.json" \
		"${srcdir}/dispatch-${pkgver}/"

	if [ -f "${_projectdir}/dispatch.toml" ]; then
		cp "${_projectdir}/dispatch.toml" "${srcdir}/dispatch-${pkgver}/"
	fi
}

build() {
	cd "${srcdir}/dispatch-${pkgver}"

	# Install all deps (including devDependencies for vite build)
	bun install --frozen-lockfile

	# Build the frontend with production API port
	VITE_API_URL="${VITE_API_URL:-http://localhost:18390}" \
		bun run --cwd packages/frontend build

	# Slim node_modules for runtime
	rm -rf node_modules
	bun install --frozen-lockfile --production

	# --- Provide the patched `cs` code-search binary for the search_code tool ---
	# Building cs means a network clone + Go compile every run, which is wasted
	# work when a matching cs is already on the system: the code-search package
	# owns /usr/bin/cs and is pinned to the same _cs_commit. If it's already
	# installed, reuse that binary instead of recloning/recompiling. Force a
	# fresh build with DISPATCH_FORCE_CS_BUILD=1 (required when bumping
	# _cs_commit, since the installed binary won't reflect the new commit).
	if [ -z "${DISPATCH_FORCE_CS_BUILD:-}" ] && pacman -Qq code-search &>/dev/null \
		&& [ -x /usr/bin/cs ]; then
		echo "cs: code-search already installed — reusing /usr/bin/cs" \
			"(set DISPATCH_FORCE_CS_BUILD=1 to rebuild)"
		cp /usr/bin/cs "${srcdir}/cs"
	else
		# Clone the pinned cs commit, apply the Luau declaration + fuzzy-distance
		# patches, and build a statically-linked binary. cs vendors its deps, so
		# `go build -mod=vendor` needs no network beyond the clone. Mirrors the
		# Docker cs-builder stage.
		#
		# rm -rf first so a rerun (makepkg -e, or two invocations without -C)
		# that reuses $srcdir doesn't abort on "destination path already exists".
		# This PKGBUILD intentionally avoids source=() (the s6 service files
		# share basenames and would collide in $srcdir), so we clone here rather
		# than via makepkg's VCS source handling.
		rm -rf "${srcdir}/cs-src"
		git clone https://github.com/boyter/cs.git "${srcdir}/cs-src"
		cd "${srcdir}/cs-src"
		git checkout "${_cs_commit}"
		git apply "${_projectdir}/docker/cs/luau-declarations.patch"
		git apply "${_projectdir}/docker/cs/fuzzy-distance.patch"
		CGO_ENABLED=0 GOFLAGS=-mod=vendor GOPATH="${srcdir}/gopath" GOCACHE="${srcdir}/gocache" \
			go build -ldflags="-s -w" -o "${srcdir}/cs" .
	fi
	"${srcdir}/cs" --version
}

# ----------------------------------------------------------------------------
# dispatch — application files, CLI wrappers, env configs
# ----------------------------------------------------------------------------
package_dispatch() {
	pkgdesc='AI Agent Dispatch — backend + frontend (application files)'
	depends=('bun')
	install=dispatch.install
	backup=(
		'etc/dispatch/dispatch-api.conf'
		'etc/dispatch/dispatch-frontend.conf'
	)

	cd "${srcdir}/dispatch-${pkgver}"

	local optdir="${pkgdir}/opt/dispatch"

	# --- Application files ---

	# API package (full source)
	install -dm755 "${optdir}/packages/api"
	cp -a packages/api/. "${optdir}/packages/api/"

	# Core package (full source)
	install -dm755 "${optdir}/packages/core"
	cp -a packages/core/. "${optdir}/packages/core/"

	# Frontend: built dist + serve script + package.json (for workspace resolution)
	install -dm755 "${optdir}/packages/frontend/dist"
	cp -a packages/frontend/dist/. "${optdir}/packages/frontend/dist/"
	install -Dm644 packages/frontend/serve.ts     "${optdir}/packages/frontend/serve.ts"
	install -Dm644 packages/frontend/package.json "${optdir}/packages/frontend/package.json"

	# Runtime node_modules (preserve symlinks for bun workspaces)
	# Exclude prebuilt ARM64 .node binaries — strip (x86_64 host) can't process them.
	install -dm755 "${optdir}/node_modules"
	cp -a node_modules/. "${optdir}/node_modules/"
	find "${optdir}/node_modules" -path '*/prebuilds/linux-arm64/*.node' -delete

	# Root manifest + lockfile
	install -Dm644 package.json "${optdir}/package.json"
	install -Dm644 bun.lock     "${optdir}/bun.lock"

	# Optional default config
	if [ -f dispatch.toml ]; then
		install -Dm644 dispatch.toml "${optdir}/dispatch.toml"
	fi

	# --- Environment configs (preserved across upgrades via backup=()) ---
	install -Dm644 \
		"${_packagingdir}/dispatch-api.conf" \
		"${pkgdir}/etc/dispatch/dispatch-api.conf"
	install -Dm644 \
		"${_packagingdir}/dispatch-frontend.conf" \
		"${pkgdir}/etc/dispatch/dispatch-frontend.conf"

	# --- CLI wrappers ---
	install -Dm755 \
		"${_packagingdir}/dispatch-api-wrapper.sh" \
		"${pkgdir}/usr/bin/dispatch-api"
	install -Dm755 \
		"${_packagingdir}/dispatch-frontend-wrapper.sh" \
		"${pkgdir}/usr/bin/dispatch-frontend"

	# --- License ---
	if [ -f "${_projectdir}/LICENSE" ]; then
		install -Dm644 "${_projectdir}/LICENSE" \
			"${pkgdir}/usr/share/licenses/dispatch/LICENSE"
	fi
}

# ----------------------------------------------------------------------------
# dispatch-systemd — systemd system unit templates (run as a chosen user)
#
# These are *system* units (managed by PID 1), not user units, so they do not
# depend on a per-user `user@UID.service` manager. Each is a template keyed on
# the username: `dispatch-api@tradam` runs as the `tradam` user via User=%i.
# ----------------------------------------------------------------------------
package_dispatch-systemd() {
	pkgdesc='Systemd system unit templates for the Dispatch API and Frontend services (run as dispatch-api@<user>)'
	depends=("dispatch=${pkgver}" 'systemd')
	conflicts=('dispatch-s6')
	install=dispatch-systemd.install

	install -Dm644 \
		"${_packagingdir}/dispatch-api@.service" \
		"${pkgdir}/usr/lib/systemd/system/dispatch-api@.service"

	install -Dm644 \
		"${_packagingdir}/dispatch-frontend@.service" \
		"${pkgdir}/usr/lib/systemd/system/dispatch-frontend@.service"
}

# ----------------------------------------------------------------------------
# dispatch-s6 — s6 service definitions (for Artix / standalone s6)
# ----------------------------------------------------------------------------
package_dispatch-s6() {
	pkgdesc='s6-rc service definitions for the Dispatch API and Frontend services'
	depends=("dispatch=${pkgver}" 's6' 's6-rc')
	conflicts=('dispatch-systemd')
	install=dispatch-s6.install

	# s6-rc layout: each service is split into a `-srv` (the daemon) and a
	# `-log` (s6-log consumer), linked by producer-for/consumer-for/pipeline-name.
	# The pipeline-name lets `s6-rc -u change dispatch-api` bring both up together.

	# --- dispatch-api-srv ---
	install -Dm755 "${_packagingdir}/s6/dispatch-api-srv/run" \
		"${pkgdir}/etc/s6/sv/dispatch-api-srv/run"
	install -Dm644 "${_packagingdir}/s6/dispatch-api-srv/type" \
		"${pkgdir}/etc/s6/sv/dispatch-api-srv/type"
	install -Dm644 "${_packagingdir}/s6/dispatch-api-srv/producer-for" \
		"${pkgdir}/etc/s6/sv/dispatch-api-srv/producer-for"

	# --- dispatch-api-log ---
	install -Dm755 "${_packagingdir}/s6/dispatch-api-log/run" \
		"${pkgdir}/etc/s6/sv/dispatch-api-log/run"
	install -Dm644 "${_packagingdir}/s6/dispatch-api-log/type" \
		"${pkgdir}/etc/s6/sv/dispatch-api-log/type"
	install -Dm644 "${_packagingdir}/s6/dispatch-api-log/consumer-for" \
		"${pkgdir}/etc/s6/sv/dispatch-api-log/consumer-for"
	install -Dm644 "${_packagingdir}/s6/dispatch-api-log/pipeline-name" \
		"${pkgdir}/etc/s6/sv/dispatch-api-log/pipeline-name"
	# notification-fd must match the `-d3` flag in run; without it s6-log
	# aborts with "invalid notification fd" and the API blocks on a full
	# stdout pipe before it can listen().
	install -Dm644 "${_packagingdir}/s6/dispatch-api-log/notification-fd" \
		"${pkgdir}/etc/s6/sv/dispatch-api-log/notification-fd"

	# --- dispatch-frontend-srv ---
	install -Dm755 "${_packagingdir}/s6/dispatch-frontend-srv/run" \
		"${pkgdir}/etc/s6/sv/dispatch-frontend-srv/run"
	install -Dm644 "${_packagingdir}/s6/dispatch-frontend-srv/type" \
		"${pkgdir}/etc/s6/sv/dispatch-frontend-srv/type"
	install -Dm644 "${_packagingdir}/s6/dispatch-frontend-srv/producer-for" \
		"${pkgdir}/etc/s6/sv/dispatch-frontend-srv/producer-for"

	# --- dispatch-frontend-log ---
	install -Dm755 "${_packagingdir}/s6/dispatch-frontend-log/run" \
		"${pkgdir}/etc/s6/sv/dispatch-frontend-log/run"
	install -Dm644 "${_packagingdir}/s6/dispatch-frontend-log/type" \
		"${pkgdir}/etc/s6/sv/dispatch-frontend-log/type"
	install -Dm644 "${_packagingdir}/s6/dispatch-frontend-log/consumer-for" \
		"${pkgdir}/etc/s6/sv/dispatch-frontend-log/consumer-for"
	install -Dm644 "${_packagingdir}/s6/dispatch-frontend-log/pipeline-name" \
		"${pkgdir}/etc/s6/sv/dispatch-frontend-log/pipeline-name"
	# notification-fd must match the `-d3` flag in run (see dispatch-api-log).
	install -Dm644 "${_packagingdir}/s6/dispatch-frontend-log/notification-fd" \
		"${pkgdir}/etc/s6/sv/dispatch-frontend-log/notification-fd"
}


# ----------------------------------------------------------------------------
# code-search — patched `cs` (code spelunker) binary
#
# A standalone, relevance-ranked code search CLI (github.com/boyter/cs), built
# from a pinned commit with an added Luau declaration table (see
# docker/cs/luau-declarations.patch). Powers Dispatch's `search_code` agent
# tool, but is a self-contained binary usable on its own. Built once in build()
# and installed to /usr/bin/cs.
# ----------------------------------------------------------------------------
package_code-search() {
	pkgdesc='code spelunker (cs) — fast, relevance-ranked code search CLI (patched for Luau)'
	depends=()
	# Both this package and the unrelated AUR `cs` (a colored ls) own /usr/bin/cs;
	# declare the conflict so pacman reports it cleanly instead of a raw file
	# collision. We do NOT `provides=('cs')` — this is a different program.
	conflicts=('cs')
	url='https://github.com/boyter/cs'

	install -Dm755 "${srcdir}/cs" "${pkgdir}/usr/bin/cs"

	if [ -f "${srcdir}/cs-src/LICENSE" ]; then
		install -Dm644 "${srcdir}/cs-src/LICENSE" \
			"${pkgdir}/usr/share/licenses/code-search/LICENSE"
	fi
}
