export GOPRIVATE := $(GOPRIVATE),amadan.net/oficina/estilo.git

APP := calls

# What `carlos ship -target` defaults to, and therefore what a release
# must be built for. Building for your own machine and shipping that is a
# silent architecture mismatch: the binary uploads fine and fails to exec
# on the instance. Override for a one-off:
#   make release RELEASE_GOARCH=amd64
RELEASE_GOOS   ?= linux
RELEASE_GOARCH ?= arm64
RELEASE_BIN := releases/$(APP)-$(RELEASE_GOOS)-$(RELEASE_GOARCH)

# What the running binary reports from GET /api/version. An unstamped
# binary answers "dev" from every build, so the platform's deploy
# verification can never disagree with itself. Stamping is what gives
# that check the ability to fail. Outside a repository, or before the
# first commit, name it yourself: make release VERSION=v0.1.0
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null)

.PHONY: build release version-check test vet fmt-check migration-check ci race browser

# build is the compile check: with more than one package matched, go
# build discards the output, so this catches a broken package without
# producing an artifact.
build:
	CGO_ENABLED=0 go build ./...

# release is what ships. -s -w drops the symbol table and DWARF, which on
# this family's apps is 23-31% off the binary and 45-53% off the
# compressed artifact — and compressed size is what actually gets
# transferred. carlos is built the same way (see platform's Makefile).
#
# What survives: panic traces keep their function names AND line numbers
# (Go's pclntab is untouched by these flags), debug.ReadBuildInfo works,
# and go version -m still reports module metadata. What you lose is
# source-level debugging with delve or gdb — build without the flags for
# that. rastrillo dev never strips, for the same reason.
release: version-check
	@mkdir -p releases
	CGO_ENABLED=0 GOOS=$(RELEASE_GOOS) GOARCH=$(RELEASE_GOARCH) \
		go build -ldflags="-s -w -X 'amadan.net/rastrillo/rastrillo.BuildVersion=$(VERSION)'" \
		-o $(RELEASE_BIN) ./cmd/$(APP)
	@echo
	@echo "built $(RELEASE_BIN) reporting version $(VERSION)"
	@echo "ship it with:"
	@echo "  carlos ship -app $(APP) -target $(RELEASE_GOOS)-$(RELEASE_GOARCH) $(RELEASE_BIN)"

version-check:
	@if [ -z "$(VERSION)" ]; then \
		echo "make release: no version to stamp."; \
		echo "  git describe found nothing here: no repository, or no commit yet."; \
		echo "  Commit, or name it:  make release VERSION=v0.1.0"; \
		exit 1; \
	fi
	@case "$(VERSION)" in *-dirty) \
		echo "make release: the tree is dirty, so $(VERSION) does not name what you are about to ship."; \
		echo "  Commit first, or name it:  make release VERSION=v0.1.0-wip"; \
		exit 1;; \
	esac

test:
	go test ./...

vet:
	go vet ./...

# The race gate, and the one place in this repo that sets CGO_ENABLED=1.
# `-race` refuses to build without cgo, and AGENTS.md's cgo-free rule is
# about what ships: this builds a test binary and never a release, so the
# exception costs the deployed artifact nothing. It is not folded into
# `ci` for that reason — `ci` must stay cgo-free — and runs as its own
# CI step (.amadan/ci.d/50-race) where the runner has a C compiler.
#
# Slice 2 is the first slice with real concurrency: a hub behind a mutex,
# a tick goroutine, one goroutine per stream and three atomics. Run this
# before every push that touches internal/calls/hub or the stream.
race:
	CGO_ENABLED=1 go test -race ./internal/...

fmt-check:
	@out=$$(gofmt -l .); if [ -n "$$out" ]; then echo "gofmt needed:"; echo "$$out"; exit 1; fi

# A step of its own, not folded into another target's recipe: ci.d/
# steps each exec one make target, so migration-check needs its own
# name to get its own reported step on a runner with step support.
#
# go run, not a bare "rastrillo": go.mod already requires rastrillo, so
# this needs nothing on PATH but the Go toolchain, and works offline
# after go mod download. A bare "rastrillo migration check" failed a
# fresh clone's very first CI run with "make: rastrillo: Command not
# found", and nothing in the scaffold said what to install.
migration-check:
	go run amadan.net/rastrillo/rastrillo/cmd/rastrillo migration check

# ci is the one gate: what a runner executes and what you run before
# pushing are the same definition. amadan's runner falls back to this
# target when .amadan/ci is absent; ci.d/ carries the same targets,
# reported one by one, for a runner with step support. migration-check
# fails the build the moment models.go and migrations/ disagree,
# instead of at boot on whatever machine notices next. If the app
# declares manifest resources, also add: rastrillo generate --check
ci: vet fmt-check test migration-check

# The browser gate: a real call between two real Chromium contexts, with
# fake camera and microphone. The scenarios, and the order they run in,
# are the `scenarios` slice in internal/callstest/browser_test.go — that
# list is the one that has to be right, so it is not copied here.
#
# Deliberately NOT part of `ci`. It needs node and `npm install`, and the
# one gate the team runs on every push must not depend on a toolchain the
# app itself does not use. Run it before every review that touches a page
# or the client.
#
# The vet runs first because the whole gate is behind a build tag, so
# `make ci` never compiles browser_test.go: without this line a typo in
# it is found by nobody until the day someone runs this target.
#
# `npm install` fetches the driver, not the browser — playwright 1.62.1
# has no postinstall step — so `playwright install` is a separate line,
# and without it this target passes only on a machine whose Playwright
# cache someone has already warmed. `--with-deps` is deliberately not
# used: it installs system packages and needs root, which is the
# operator's call and not a test target's.
#
# It also needs UDP. Inside the Claude Code bash sandbox Chromium gathers
# zero ICE candidates and every call fails with frames stuck at 0 — run
# this target with the sandbox off.
#
# -count=1 because call.mjs is not an input to Go's test cache: node
# opens it, not the test, so an edited browser script would otherwise be
# answered with a cached pass from before the edit.
# The .test.mjs files run first because they need no browser at
# all: they lift functions out of room.js and chat.js by name — pure
# ones for their answers, whole loops for what they do — and check
# §5.1's glare decision against the W3C truth table; §6's DOM safety,
# the log cap and §4.4's byte counter; §5.4's camera-or-screen judgement
# with §4.3's fan-out wire shape; and the two sequencing contracts,
# every sender on the broadcast lane answered exactly once and one
# gesture taking one capture; and the gate's own observation helper,
# which is the one piece of call.mjs that can be wrong in a way that
# makes the gate quieter rather than louder. Under a second between
# them. Named explicitly rather than by directory — node's test runner treats
# everything under test/ as a test file, and call.mjs is not one.
browser:
	go vet -tags browser ./internal/callstest/
	node --test test/browser/negotiation.test.mjs
	node --test test/browser/chat.test.mjs
	node --test test/browser/room-share.test.mjs
	node --test test/browser/room-mute.test.mjs
	node --test test/browser/room-space.test.mjs
	node --test test/browser/room-hearing.test.mjs
	node --test test/browser/room-screen.test.mjs
	node --test test/browser/room-reconnect.test.mjs
	node --test test/browser/room-connecting.test.mjs
	node --test test/browser/room-glow.test.mjs
	node --test test/browser/room-layout.test.mjs
	node --test test/browser/room-window.test.mjs
	node --test test/browser/room-lane.test.mjs
	node --test test/browser/room-quality.test.mjs
	node --test test/browser/media.test.mjs
	node --test test/browser/audio.test.mjs
	node --test test/browser/devices.test.mjs
	npm install --no-save playwright@1.62.1
	npx playwright install chromium
	go test -tags browser -count=1 -timeout 20m -run TestCallInARealBrowser -v ./internal/callstest/

# Native builds isolate gomobile's cgo bridge from the cgo-free server.
.PHONY: native-mac native-test native-browser native-pair
native-mac:
	./native/scripts/build-mac.sh

native-test:
	CGO_ENABLED=0 go test ./callscore ./protocol
	cd native && CGO_ENABLED=0 go test ./bridge
	CGO_ENABLED=1 go test -race ./callscore

native-browser: native-mac
	# The gate runs CallsInterop, which native-mac does not build; without
	# this it runs whatever binary a previous build left behind.
	swift build --package-path native/apple --product CallsInterop
	CALLS_DEEPFILTER_MODEL=$(CURDIR)/native/build/DeepFilterNet3_onnx.tar.gz CGO_ENABLED=0 go test -tags browser -count=1 -timeout 25m -run 'TestNative(MacBarLayout|MacCallsBrowser|MacChat|MacScreenMotion|MacScreenRecovery|BrowserAuthentication|BrowserAppLink|MacAudioSelfTest|MacAudioStartStall)' -v ./internal/callstest/

# The Mac app in a call with Playwright's WebKit (not iOS Safari; see
# test/browser/native-webkit.mjs): codecs, Opus FEC, a thin path, and the
# tab getting its camera and microphone back. Apart from native-browser
# because it needs WebKit installed.
.PHONY: native-webkit
native-webkit: native-mac
	swift build --package-path native/apple --product CallsInterop
	npm install --no-save playwright@1.62.1
	npx playwright install webkit
	CALLS_DEEPFILTER_MODEL=$(CURDIR)/native/build/DeepFilterNet3_onnx.tar.gz CGO_ENABLED=0 go test -tags browser -count=1 -timeout 10m -run 'TestNativeMacWebKit' -v ./internal/callstest/

# Two apps in one call, sharing screens to each other. Separate from
# native-browser because two unsigned apps on one Mac do not reliably
# connect, which looks like the macOS firewall: see TestNativeMacScreenShare.
# It runs CallsInterop too, so it builds it for the same reason.
native-pair: native-mac
	swift build --package-path native/apple --product CallsInterop
	CALLS_DEEPFILTER_MODEL=$(CURDIR)/native/build/DeepFilterNet3_onnx.tar.gz CGO_ENABLED=0 go test -tags browser -count=1 -timeout 20m -run 'TestNativeMacScreen(Share|PairRecovery)' -v ./internal/callstest/

.PHONY: iphone iphone-device iphone-browser
iphone:
	./native/scripts/build-iphone.sh simulator
iphone-device:
	./native/scripts/build-iphone.sh device
iphone-browser:
	xcodegen generate --spec native/ios/project.yml
	CGO_ENABLED=0 go test -tags browser -count=1 -timeout 10m -run 'TestIPhone(SafariAuthentication|NativeMedia)' -v ./internal/callstest/

.PHONY: credits
credits:
	@set -eu; notices_tmp=$$(mktemp -d); trap 'rm -rf "$$notices_tmp"' EXIT; \
		GOOS=$$(go env GOHOSTOS) GOARCH=$$(go env GOHOSTARCH) CGO_ENABLED=0 \
		go build -o "$$notices_tmp/notices" amadan.net/oficina/estilo.git/cmd/oficina-notices; \
		GOOS=linux GOARCH=arm64 CGO_ENABLED=0 "$$notices_tmp/notices" \
		-package ./cmd/calls -lucide -onest \
		-exclude amadan.net/oficina/estilo.git,amadan.net/oficina/home/connect,amadan.net/oficina/home/handoff \
		> "$$notices_tmp/notices.json"; \
		mv "$$notices_tmp/notices.json" internal/calls/credits/notices.json
