# Fenris Makefile
# Spec: §10.1-10.6

SHELL := /bin/bash
PYTHON := python3
VENV_DIR := /opt/fenris
VENDOR_DIR := $(VENV_DIR)/vendor
BIN_DIR := /usr/local/bin
LIBEXEC_DIR := /usr/libexec/fenris
UNIT_DIR := /etc/systemd/system
POLKIT_DIR := /usr/share/polkit-1/actions
CONF_DIR := /etc/fenris
DATA_DIR := /var/lib/fenris

# Placement manifest
MANIFEST := $(DATA_DIR)/manifest.txt

# Legacy history path (IN-4)
LEGACY_HISTORY := ./data/history.jsonl

.PHONY: help install upgrade uninstall purge update-deps test lint check-python check-smartctl import-legacy stage package-deb package-rpm package-xbps package generate-test-key sign-rpm sign-xbps checksums clearsign release release-run release-dry-run xbps-publish xbps-publish-dry-run clean

help:
	@echo "Fenris NVMe endurance monitor"
	@echo ""
	@echo "Targets:"
	@echo "  install      - Install Fenris (builds wheel, installs to /opt/fenris)"
	@echo "  upgrade      - Upgrade Fenris (reinstall wheel, sync units)"
	@echo "  uninstall    - Uninstall Fenris (preserves config and store)"
	@echo "  purge        - Remove everything including config and store"
	@echo "  test         - Run tests"
	@echo "  lint         - Run linter"
	@echo "  update-deps  - Update dependency pins"
	@echo "  stage        - Stage packaging tree for nfpm"
	@echo "  package      - Build deb + rpm packages"
	@echo "  package-deb  - Build deb package only"
	@echo "  package-rpm  - Build rpm package only"
	@echo "  package-xbps - Build XBPS package only"
	@echo "  sign-xbps    - Sign XBPS package with signing key"
	@echo "  generate-test-key  - Create throwaway GPG key for CI/testing"
	@echo "  sign-rpm     - Sign RPM payload with packaging key"
	@echo "  checksums    - Generate SHA256SUMS manifest"
	@echo "  clearsign    - Clearsign SHA256SUMS with packaging key"
	@echo "  release      - Full release (build, sign, checksum, print upload steps)"
	@echo "  release-run  - Execute the full release flow via scripts/release.sh"
	@echo "  release-dry-run - Dry-run of the release flow (prints commands only)"
	@echo "  xbps-publish - Publish XBPS package to distribution repository"
	@echo "  xbps-publish-dry-run - Dry-run of XBPS publication (prints commands only)"
	@echo "  clean        - Remove build artifacts"

# ─── Pre-install gates ──────────────────────────────────────────────────────

check-python:
	@echo "=== Verifying Python ≥ 3.10 ==="
	@$(PYTHON) -c "import sys; v=sys.version_info; exit(0 if (v>=(3,10)) else 1)" || { echo "Error: Python 3.10+ required (found $$($(PYTHON) --version 2>&1))"; exit 1; }

check-smartctl:
	@echo "=== Verifying smartctl ==="
	@smartctl --version 2>/dev/null | head -1 || { echo "Error: smartctl not found (install smartmontools)"; exit 1; }

# ─── Build ──────────────────────────────────────────────────────────────────

dist/fenris-*.whl: pyproject.toml src/fenris/*.py
	@mkdir -p dist
	$(PYTHON) -m pip wheel --no-deps --wheel-dir dist .

# ─── Install ────────────────────────────────────────────────────────────────

install: check-python check-smartctl dist/fenris-*.whl
	@echo "=== Creating directories ==="
	@sudo mkdir -p $(LIBEXEC_DIR)
	@sudo mkdir -p $(CONF_DIR)
	@sudo mkdir -p $(POLKIT_DIR)
	
	@echo "=== Creating data directory (root-written, group-read) ==="
	@sudo groupadd -f fenris
	@sudo install -d -o root -g fenris -m 2750 $(DATA_DIR)
	
	@echo "=== Installing version-neutral runtime packages ==="
	@sudo rm -rf $(VENV_DIR)
	@sudo install -d -m 0755 $(VENDOR_DIR)
	@sudo $(PYTHON) -m pip install --disable-pip-version-check --no-compile --target $(VENDOR_DIR) -r requirements.txt dist/fenris-*.whl --quiet
	
	@echo "=== Installing wrapper ==="
	@sudo install -m 0755 scripts/fenris $(BIN_DIR)/fenris
	
	@echo "=== Installing helpers ==="
	@sudo install -m 0755 src/fenris/monitor.py $(LIBEXEC_DIR)/fenris-monitor
	@sudo install -m 0755 src/fenris/collect.py $(LIBEXEC_DIR)/fenris-collect
	
	@echo "=== Installing systemd units (dormant — not enabled/started) ==="
	@sudo install -m 0644 units/fenris-collect.timer $(UNIT_DIR)/
	@sudo install -m 0644 units/fenris-collect.service $(UNIT_DIR)/
	@sudo systemctl daemon-reload
	
	@echo "=== Installing runit service files (dormant — not enabled) ==="
	@sudo install -d -m 0755 /etc/sv/fenris-collect/log
	@sudo install -m 0755 units/runit/fenris-collect/run /etc/sv/fenris-collect/run
	@sudo install -m 0755 units/runit/fenris-collect/log/run /etc/sv/fenris-collect/log/run
	@sudo touch /etc/sv/fenris-collect/down
	
	@echo "=== Installing polkit policy ==="
	@sudo install -m 0644 polkit/com.bongbetic.fenris.monitor.policy $(POLKIT_DIR)/
	
	@echo "=== Recording manifest (IN-2, IN-10) ==="
	@echo "# Fenris placement manifest — do not edit" | sudo tee $(MANIFEST) > /dev/null
	@echo "# Generated by: sudo make install" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "# Timestamp: $$(date -u +%%Y-%%m-%%dT%%H:%%M:%%SZ)" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(BIN_DIR)/fenris" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(LIBEXEC_DIR)/fenris-monitor" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(LIBEXEC_DIR)/fenris-collect" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(UNIT_DIR)/fenris-collect.timer" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(UNIT_DIR)/fenris-collect.service" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(POLKIT_DIR)/com.bongbetic.fenris.monitor.policy" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(VENV_DIR)" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(DATA_DIR)" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(CONF_DIR)" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(MANIFEST)" | sudo tee -a $(MANIFEST) > /dev/null
	
	@echo "=== Install complete ==="
	@echo "Units installed but NOT enabled or started (dormant — IN-3)."
	@echo "To start monitoring: fenris monitor resume"
	
	@$(MAKE) --no-print-directory import-legacy

# ─── Legacy import (IN-4, ST-7) ────────────────────────────────────────────

import-legacy:
	@if [ -f "$(LEGACY_HISTORY)" ]; then \
		echo "=== Detected legacy history: $(LEGACY_HISTORY) ==="; \
		echo "Running idempotent import..."; \
		PYTHONPATH=$(VENDOR_DIR) $(PYTHON) -c "from fenris.legacy import import_legacy_history; from fenris.store import init_store; from pathlib import Path; conn = init_store(Path('$(DATA_DIR)/observations.db')); r = import_legacy_history(conn, Path('$(LEGACY_HISTORY)')); conn.close(); print(f'  Samples imported: {r.get(\"samples_imported\", 0)}'); print(f'  Hours imported: {r.get(\"hours_imported\", 0)}'); print(f'  Malformed lines: {r.get(\"malformed_lines\", 0)}') if not r.get('skipped') else print('  Skipped: already imported')" || echo "  Warning: import failed (non-fatal)"; \
	else \
		echo "=== No legacy history found at $(LEGACY_HISTORY) ==="; \
	fi

# ─── Upgrade ────────────────────────────────────────────────────────────────

upgrade: dist/fenris-*.whl
	@echo "=== Upgrading Fenris ==="
	@echo "=== Snapshotting database (IN-6) ==="
	@sudo cp $(DATA_DIR)/observations.db $(DATA_DIR)/observations.db.bak 2>/dev/null || true
	
	@echo "=== Installing new version-neutral runtime packages ==="
	@sudo rm -rf $(VENDOR_DIR)
	@sudo install -d -m 0755 $(VENDOR_DIR)
	@sudo $(PYTHON) -m pip install --disable-pip-version-check --no-compile --target $(VENDOR_DIR) -r requirements.txt dist/fenris-*.whl --quiet
	
	@echo "=== Syncing units against manifest ==="
	@sudo install -m 0644 units/fenris-collect.timer $(UNIT_DIR)/
	@sudo install -m 0644 units/fenris-collect.service $(UNIT_DIR)/
	@sudo install -d -m 0755 /etc/sv/fenris-collect/log
	@sudo install -m 0755 units/runit/fenris-collect/run /etc/sv/fenris-collect/run
	@sudo install -m 0755 units/runit/fenris-collect/log/run /etc/sv/fenris-collect/log/run
	@sudo install -m 0644 polkit/com.bongbetic.fenris.monitor.policy $(POLKIT_DIR)/
	@sudo install -m 0755 scripts/fenris $(BIN_DIR)/fenris
	@sudo install -m 0755 src/fenris/monitor.py $(LIBEXEC_DIR)/fenris-monitor
	@sudo install -m 0755 src/fenris/collect.py $(LIBEXEC_DIR)/fenris-collect
	@sudo systemctl daemon-reload
	
	@echo "=== Updating manifest ==="
	@echo "# Fenris placement manifest — do not edit" | sudo tee $(MANIFEST) > /dev/null
	@echo "# Generated by: sudo make upgrade" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "# Timestamp: $$(date -u +%%Y-%%m-%%dT%%H:%%M:%%SZ)" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(BIN_DIR)/fenris" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(LIBEXEC_DIR)/fenris-monitor" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(LIBEXEC_DIR)/fenris-collect" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(UNIT_DIR)/fenris-collect.timer" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(UNIT_DIR)/fenris-collect.service" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(POLKIT_DIR)/com.bongbetic.fenris.monitor.policy" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(VENV_DIR)" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(DATA_DIR)" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(CONF_DIR)" | sudo tee -a $(MANIFEST) > /dev/null
	@echo "$(MANIFEST)" | sudo tee -a $(MANIFEST) > /dev/null
	
	@echo "=== Restarting timer only if contents changed and active (IN-5) ==="
	@for unit in fenris-collect.timer fenris-collect.service; do \
		TMPFILE=$$(mktemp); \
		sudo systemctl cat $$unit > $$TMPFILE 2>/dev/null || true; \
		if ! diff -q $$TMPFILE $(UNIT_DIR)/$$unit > /dev/null 2>&1; then \
			if systemctl is-active --quiet $$unit; then \
				echo "  $$unit changed and active — restarting"; \
				sudo systemctl restart $$unit; \
			fi; \
		fi; \
		rm -f $$TMPFILE; \
	done
	
	@echo "=== Applying forward-only schema migrations (IN-5, IN-6) ==="
	@sudo env PYTHONPATH=$(VENDOR_DIR) $(PYTHON) -c "from fenris.store import migrate_to_latest; from pathlib import Path; n = migrate_to_latest(Path('$(DATA_DIR)/observations.db')); print(f'  Migration steps applied: {n}') if n else print('  Schema already current')"
	
	@echo "=== Upgrade complete ==="

# ─── Uninstall (IN-7) ──────────────────────────────────────────────────────

uninstall:
	@echo "=== Uninstalling Fenris ==="
	
	@if [ -x $(LIBEXEC_DIR)/fenris-monitor ]; then \
		echo "=== Performing sanctioned disable (§10.4) ==="; \
		sudo $(LIBEXEC_DIR)/fenris-monitor disable --now || true; \
	fi
	
	@echo "=== Stopping units ==="
	@sudo systemctl stop fenris-collect.timer 2>/dev/null || true
	@sudo systemctl disable fenris-collect.timer 2>/dev/null || true
	@sudo systemctl daemon-reload
	@-sudo rm -f /var/service/fenris-collect 2>/dev/null || true
	@-sudo rm -rf /etc/sv/fenris-collect 2>/dev/null || true
	@-sudo rm -rf /var/log/fenris-collect 2>/dev/null || true
	
	@echo "=== Removing installed files (preserving config and store) ==="
	@-rm -f $(BIN_DIR)/fenris
	@-rm -f $(LIBEXEC_DIR)/fenris-monitor
	@-rm -f $(LIBEXEC_DIR)/fenris-collect
	@-rmdir $(LIBEXEC_DIR) 2>/dev/null || true
	@-rm -f $(UNIT_DIR)/fenris-collect.timer
	@-rm -f $(UNIT_DIR)/fenris-collect.service
	@-rm -f $(POLKIT_DIR)/com.bongbetic.fenris.monitor.policy
	@sudo rm -rf $(VENV_DIR)
	@-sudo rm -f $(MANIFEST)
	
	@echo "=== Uninstall complete ==="
	@echo "Config preserved at $(CONF_DIR)"
	@echo "Store preserved at $(DATA_DIR)"

# ─── Purge ──────────────────────────────────────────────────────────────────

purge: uninstall
	@echo "=== Purging Fenris ==="
	@-sudo rm -rf $(CONF_DIR)
	@-sudo rm -rf $(DATA_DIR)
	@echo "=== Purge complete ==="

# ─── Test ───────────────────────────────────────────────────────────────────

test:
	$(PYTHON) -m pytest tests/ -v

# ─── Lint ───────────────────────────────────────────────────────────────────

lint:
	$(PYTHON) -m ruff check src/ tests/

# ─── Dependencies (IN-8) ───────────────────────────────────────────────────

update-deps:
	$(PYTHON) -m pip compile pyproject.toml -o requirements.txt

# ─── Packaging (spec §3, §4, §5) ────────────────────────────────────────────

# Version is sourced from pyproject.toml for both formats
FENRIS_VERSION := $(shell sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml)

# GPG signing — packaging key UID (spec §4)
PACKAGING_KEY ?= packaging@bongbetic.com

stage:
	@echo "=== Staging packaging tree (v$(FENRIS_VERSION)) ==="
	$(PYTHON) -m pip wheel --no-deps --wheel-dir dist .
	bash packaging/stage.sh "$(FENRIS_VERSION)"

package-deb: stage
	@echo "=== Building deb package ==="
	VERSION="$(FENRIS_VERSION)" nfpm pkg -f packaging/nfpm.yaml -p deb -t dist/
	@echo "=== deb package built: dist/fenris_$(FENRIS_VERSION)_amd64.deb ==="

package-rpm: stage
	@echo "=== Building rpm package ==="
	VERSION="$(FENRIS_VERSION)" nfpm pkg -f packaging/nfpm.yaml -p rpm -t dist/
	@echo "=== rpm package built: dist/fenris-$(FENRIS_VERSION)-1.x86_64.rpm ==="

package: package-deb package-rpm
	@echo "=== Both packages built in dist/ ==="

# ─── XBPS packaging (ADR 0008) ─────────────────────────────────────────────

# XBPS signing key (separate from SSH authentication key)
XBPS_SIGNING_KEY ?= $(HOME)/.ssh/id_xbps

package-xbps: stage
	@echo "=== Building XBPS package ==="
	xbps-create -A x86_64 \
		-n fenris-$(FENRIS_VERSION)_1 \
		-s "Fenris NVMe wear monitor" \
		-S "NVMe wear monitor with persistent TUI" \
		-m "Fenris Packaging <packaging@bongbetic.com>" \
		-H "https://git.bongbetic.com/xavierk/Fenris" \
		-l "MIT" \
		build/stage
	@echo "=== XBPS package built: fenris-$(FENRIS_VERSION)_1.x86_64.xbps ==="

sign-xbps: package-xbps
	@echo "=== Signing XBPS package ==="
	xbps-rindex --sign-pkg --privkey $(XBPS_SIGNING_KEY) \
		fenris-$(FENRIS_VERSION)_1.x86_64.xbps
	@echo "=== XBPS package signed ==="

xbps-publish:
	bash scripts/xbps-publish.sh --publish

xbps-publish-dry-run:
	bash scripts/xbps-publish.sh --dry-run

# ─── GPG key management ─────────────────────────────────────────────────────

generate-test-key:
	@echo "=== Generating throwaway test GPG key ==="
	@echo "This key is for CI/testing only — never use for real releases."
	printf '%%no-protection\nKey-Type: RSA\nKey-Length: 3072\nName-Real: Fenris Packaging (TESTING ONLY)\nName-Email: packaging-test@bongbetic.com\nExpire-Date: 0\n%%commit\n' | \
		gpg --batch --gen-key
	@echo "=== Test key created. Fingerprint: ==="
	@gpg --fingerprint packaging-test@bongbetic.com

# ─── Signing ────────────────────────────────────────────────────────────────

sign-rpm: package-rpm
	@echo "=== Signing RPM payload ==="
	@rpm --import packaging/keys/fenris-packaging.asc 2>/dev/null || true
	rpmsign --addsign --define "_gpg_name $(PACKAGING_KEY)" \
		dist/fenris-$(FENRIS_VERSION)-1.x86_64.rpm
	@echo "=== RPM signed ==="
	@rpm -Kv dist/fenris-$(FENRIS_VERSION)-1.x86_64.rpm

checksums: package
	@echo "=== Generating SHA256SUMS ==="
	cd dist && sha256sum fenris_$(FENRIS_VERSION)_amd64.deb \
		fenris-$(FENRIS_VERSION)-1.x86_64.rpm > SHA256SUMS
	@echo "=== SHA256SUMS written ==="
	@cat dist/SHA256SUMS

clearsign: checksums
	@echo "=== Clearsigning SHA256SUMS ==="
	gpg --batch --yes --clearsign --local-user $(PACKAGING_KEY) \
		dist/SHA256SUMS
	@echo "=== SHA256SUMS.asc written ==="

# ─── Release (spec §5) ──────────────────────────────────────────────────────

release: package sign-rpm clearsign
	@echo ""
	@echo "=== Release v$(FENRIS_VERSION) ==="
	@echo ""
	@echo "Artifacts:"
	@ls -la dist/fenris_$(FENRIS_VERSION)_amd64.deb \
		dist/fenris-$(FENRIS_VERSION)-1.x86_64.rpm \
		dist/SHA256SUMS.asc 2>/dev/null
	@echo ""
	@echo "Verify signing (manual):"
	@echo "  rpm -Kv dist/fenris-$(FENRIS_VERSION)-1.x86_64.rpm"
	@echo "  gpg --verify dist/SHA256SUMS.asc dist/SHA256SUMS"
	@echo ""
	@echo "Upload to registry:"
	@echo "  curl -X PUT -u user:token -T dist/fenris_$(FENRIS_VERSION)_amd64.deb \\"
	@echo "    'https://git.bongbetic.com/api/packages/xavierk/debian/pool/bookworm/main/upload'"
	@echo "  curl -X PUT -u user:token -T dist/fenris_$(FENRIS_VERSION)_amd64.deb \\"
	@echo "    'https://git.bongbetic.com/api/packages/xavierk/debian/pool/jammy/main/upload'"
	@echo "  curl -X PUT -u user:token -T dist/fenris_$(FENRIS_VERSION)_amd64.deb \\"
	@echo "    'https://git.bongbetic.com/api/packages/xavierk/debian/pool/noble/main/upload'"
	@echo "  curl -X PUT -u user:token -T dist/fenris-$(FENRIS_VERSION)-1.x86_64.rpm \\"
	@echo "    'https://git.bongbetic.com/api/packages/xavierk/rpm/fenris/upload'"
	@echo ""
	@echo "Create Gitea release with notes and attach:"
	@echo "  dist/fenris_$(FENRIS_VERSION)_amd64.deb"
	@echo "  dist/fenris-$(FENRIS_VERSION)-1.x86_64.rpm"
	@echo "  dist/SHA256SUMS.asc"
	@echo ""
	@echo "Key ceremony: delete the private key after upload."
	@echo "  See docs/install/signing-key-ceremony.md"

# ─── Automated release flow (issue #52) ──────────────────────────────────────

release-run:
	bash scripts/release.sh --publish

release-dry-run:
	bash scripts/release.sh --dry-run

clean:
	@echo "=== Cleaning build artifacts ==="
	rm -rf build/stage dist/fenris-*.deb dist/fenris-*.rpm dist/SHA256SUMS*
