# Personal Internet Cell - Makefile
# Provides easy commands for managing the cell

.PHONY: help start stop restart status logs clean setup check-deps init-peers \
        update reinstall uninstall install \
        build build-api build-webui \
        start-core start-dns start-api start-wg start-webui \
        backup restore \
        test test-all test-unit test-coverage test-api test-cli \
        test-phase1 test-phase2 test-phase3 test-phase4 test-all-phases \
        test-integration test-integration-readonly \
        test-e2e-deps test-e2e-api test-e2e-ui test-e2e-wg test-e2e \
        reset-test-admin-pass \
        show-admin-password reset-admin-password \
        show-routes add-peer list-peers

# Detect docker compose command (v2 plugin preferred, fallback to v1 standalone)
DC := $(shell docker compose version >/dev/null 2>&1 && echo "docker compose" || echo "docker-compose")

# Default target
help:
	@echo "Personal Internet Cell - Management Commands"
	@echo ""
	@echo "First install:"
	@echo "  check-deps     - Install all required system packages (python3, docker, etc.)"
	@echo "  setup          - Generate keys, write configs, create data dirs"
	@echo "                   Env vars: CELL_NAME=mycell  CELL_DOMAIN=cell  VPN_ADDRESS=10.0.0.1/24  WG_PORT=51820"
	@echo "  init-peers     - Reset peer list to empty"
	@echo ""
	@echo "Lifecycle:"
	@echo "  start          - Start all services"
	@echo "  stop           - Stop all services"
	@echo "  restart        - Restart all services"
	@echo "  status         - Show container status + API health"
	@echo "  logs           - Follow logs from all services"
	@echo "  logs-<svc>     - Follow logs for one service  (e.g. make logs-api)"
	@echo "  shell-<svc>    - Open shell in a container   (e.g. make shell-api)"
	@echo ""
	@echo "Updates & reinstall:"
	@echo "  update         - git pull + rebuild + restart (deploy latest code)"
	@echo "  reinstall      - Full wipe and fresh install from current git checkout"
	@echo "  uninstall      - Stop + remove containers; prompts whether to also delete data"
	@echo ""
	@echo "Build:"
	@echo "  build          - Rebuild API image"
	@echo "  build-api      - Rebuild API image (no cache)"
	@echo "  build-webui    - Rebuild Web UI image (no cache)"
	@echo ""
	@echo "Individual services:"
	@echo "  start-dns      - Start DNS only"
	@echo "  start-api      - Start API only"
	@echo "  start-wg       - Start WireGuard only"
	@echo ""
	@echo "Maintenance:"
	@echo "  backup         - Backup config + data to backups/"
	@echo "  restore        - List available backups"
	@echo "  clean          - Remove containers and volumes (keeps config/data dirs)"
	@echo "  show-admin-password  - Print the admin password (reads setup file or prompts to reset)"
	@echo "  reset-admin-password - Generate a new random admin password and print it"
	@echo ""
	@echo "Tests:"
	@echo "  test           - Run all tests"
	@echo "  test-coverage          - Run tests with HTML coverage report"
	@echo "  test-integration       - Full integration tests (needs running stack)"
	@echo "  test-integration-readonly - Read-only integration tests (safe to run anytime)"
	@echo ""
	@echo "Peers:"
	@echo "  list-peers     - List configured WireGuard peers"
	@echo "  show-routes    - Show WireGuard routing table"

# ── Dependencies & setup ──────────────────────────────────────────────────────

check-deps:
	@sudo sh scripts/check_deps.sh

setup: check-deps
	@echo "Setting up Personal Internet Cell..."
	@sudo chown -R $$(id -u):$$(id -g) config/ data/ 2>/dev/null || true
	CELL_NAME=$(or $(CELL_NAME),mycell) \
	CELL_DOMAIN=$(or $(CELL_DOMAIN),cell) \
	VPN_ADDRESS=$(or $(VPN_ADDRESS),10.0.0.1/24) \
	WG_PORT=$(or $(WG_PORT),51820) \
	WG_PRIVATE_KEY="$(WG_PRIVATE_KEY)" \
	WG_PUBLIC_KEY="$(WG_PUBLIC_KEY)" \
	python3 scripts/setup_cell.py

init-peers:
	@echo "Initializing peer configuration..."
	@echo '[]' > data/api/peers.json
	@echo "Peer configuration initialized."

# ── Lifecycle ─────────────────────────────────────────────────────────────────

start:
	@echo "Starting Personal Internet Cell..."
	PUID=$$(id -u) PGID=$$(id -g) $(DC) --profile full up -d --build
	@echo "Services started. Check status with 'make status'"

stop:
	@echo "Stopping Personal Internet Cell..."
	PUID=$$(id -u) PGID=$$(id -g) $(DC) --profile full down
	@echo "Services stopped."

restart:
	@echo "Restarting Personal Internet Cell..."
	PUID=$$(id -u) PGID=$$(id -g) $(DC) restart
	@echo "Services restarted."

status:
	@echo "Personal Internet Cell Status:"
	@echo "================================"
	$(DC) ps
	@echo ""
	@echo "API Status:"
	@curl -s http://localhost:3000/health || echo "API not responding"

logs:
	$(DC) logs -f

logs-%:
	$(DC) logs -f $*

shell-%:
	docker exec -it cell-$* /bin/bash 2>/dev/null || docker exec -it cell-$* /bin/sh

# ── Updates & reinstall ───────────────────────────────────────────────────────

update:
	@echo "Pulling latest code..."
	@git stash --include-untracked --quiet 2>/dev/null || true
	git pull
	@git stash pop --quiet 2>/dev/null || true
	@if [ ! -f config/mail/mailserver.env ]; then \
	  echo "Config missing — running setup first..."; \
	  $(MAKE) setup; \
	fi
	@echo "Rebuilding and restarting services..."
	PUID=$$(id -u) PGID=$$(id -g) $(DC) --profile full up -d --build
	@echo "Update complete. Run 'make status' to verify."

reinstall:
	@echo "Reinstalling Personal Internet Cell from scratch..."
	PUID=$$(id -u) PGID=$$(id -g) $(DC) --profile full down -v 2>/dev/null || true
	@sudo rm -rf config/ data/
	@$(MAKE) setup
	@$(MAKE) start
	@echo "Reinstall complete."

install:
	@if [ -f /opt/pic/.installed ] && [ "$(FORCE)" != "1" ]; then \
	  echo "Already installed. Run 'make update' to update, or 'make install FORCE=1' to reinstall."; \
	  exit 0; \
	fi
	@echo "Running setup..."
	@$(MAKE) setup
	@echo "Installing systemd unit..."
	@sudo cp scripts/pic.service /etc/systemd/system/pic.service
	@-sudo systemctl daemon-reload && sudo systemctl enable pic
	@sudo mkdir -p /opt/pic
	@sudo touch /opt/pic/.installed
	@echo "Installation complete. Run 'make start-core' to start core services."

uninstall:
	@echo ""
	@echo "This will stop and remove all containers."
	@echo ""
	@printf "Also delete config/ and data/? This cannot be undone. [y/N/cancel]: "; \
	read ans; \
	case "$$ans" in \
	  y|Y) \
	    echo "Stopping containers and removing images..."; \
	    PUID=$$(id -u) PGID=$$(id -g) $(DC) --profile full down -v --rmi all 2>/dev/null || true; \
	    echo "Deleting config/ and data/..."; \
	    sudo rm -rf config/ data/; \
	    echo "Uninstall complete. Git repo and scripts remain."; \
	    ;; \
	  n|N|"") \
	    echo "Stopping and removing containers (keeping images and data)..."; \
	    PUID=$$(id -u) PGID=$$(id -g) $(DC) --profile full down 2>/dev/null || true; \
	    echo "Done. Images, config/ and data/ are untouched. Run 'make start' to bring it back up."; \
	    ;; \
	  *) \
	    echo "Cancelled."; \
	    ;; \
	esac
	@-sudo systemctl disable pic 2>/dev/null || true
	@-sudo rm -f /etc/systemd/system/pic.service
	@-sudo rm -f /opt/pic/.installed
	@echo "Note: Data volumes were not deleted. To remove all data, manually delete config/ and data/."

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

build:
	@echo "Building API service..."
	$(DC) build api

build-api:
	@echo "Rebuilding API (no cache)..."
	$(DC) build --no-cache api
	$(DC) up -d api

build-webui:
	@echo "Rebuilding Web UI (no cache)..."
	$(DC) build --no-cache webui
	$(DC) up -d webui

# ── Individual services ───────────────────────────────────────────────────────

start-core:
	@echo "Starting core services (caddy, dns, wireguard, api, webui)..."
	PUID=$$(id -u) PGID=$$(id -g) $(DC) --profile core up -d --build
	@echo "Core services started. Run 'make start' to also bring up optional services."

start-dns:
	$(DC) --profile core up -d dns

start-api:
	$(DC) --profile core up -d api

start-wg:
	$(DC) --profile core up -d wireguard

start-webui:
	$(DC) --profile core up -d webui

# ── Maintenance ───────────────────────────────────────────────────────────────

clean:
	@echo "Removing containers and volumes..."
	$(DC) down -v
	docker system prune -f
	@echo "Done. config/ and data/ are untouched."

backup:
	@echo "Creating backup..."
	@mkdir -p backups
	@sudo tar -czf backups/cell-backup-$(shell date +%Y%m%d-%H%M%S).tar.gz \
		config/ data/ docker-compose.yml Makefile README.md
	@sudo chown $$(id -u):$$(id -g) backups/cell-backup-*.tar.gz
	@echo "Backup created in backups/."

restore:
	@echo "Available backups:"
	@ls -lh backups/cell-backup-*.tar.gz 2>/dev/null || echo "No backups found."
	@echo ""
	@echo "To restore: tar -xzf backups/cell-backup-YYYYMMDD-HHMMSS.tar.gz"

# ── Tests ─────────────────────────────────────────────────────────────────────

test:
	@echo "Running unit tests..."
	python3 -m pytest tests/ --ignore=tests/e2e --ignore=tests/integration -q

test-all: test test-integration test-e2e-api test-e2e-ui
	@echo "All test suites complete."

test-unit:
	python3 -m pytest tests/ --ignore=tests/e2e --ignore=tests/integration -q

test-coverage:
	python3 -m pytest tests/ --ignore=tests/e2e --ignore=tests/integration \
		--cov=api --cov-report=html --cov-report=term-missing --cov-fail-under=70 -v

test-integration:
	@echo "Running full integration tests (requires running PIC stack)..."
	PIC_HOST=$${PIC_HOST:-localhost} python3 -m pytest tests/integration/ -v

test-webui:
	@echo "Running webui unit tests (requires node; builds a disposable container)..."
	docker run --rm -v "$(PWD)/webui:/app" -w /app node:18-alpine \
		sh -c "npm install --silent && npm test"

test-integration-readonly:
	@echo "Running read-only integration tests (no peer creation)..."
	PIC_HOST=$${PIC_HOST:-localhost} python3 -m pytest tests/integration/test_live_api.py tests/integration/test_webui.py -v

test-api:
	python3 -m pytest tests/test_api_endpoints.py -v

test-cli:
	python3 -m pytest tests/test_cli_tool.py -v

# ── E2E tests ─────────────────────────────────────────────────────────────────
# Run `make test-e2e-deps` once to install dependencies, then use the other targets.
# Admin password is read from data/api/.test_admin_pass (written by reset-test-admin-pass).
# Override: make test-e2e-api PIC_ADMIN_PASS=mypassword

test-e2e-deps:
	sudo pip3 install --break-system-packages -r tests/e2e/requirements.txt
	sudo python3 -m playwright install --with-deps chromium

test-e2e-api:
	@PIC_HOST=$${PIC_HOST:-localhost} python3 -m pytest tests/e2e/api -v

test-e2e-ui:
	@PIC_HOST=$${PIC_HOST:-localhost} python3 -m pytest tests/e2e/ui -v

test-e2e-wg:
	@PIC_HOST=$${PIC_HOST:-localhost} sudo -E python3 -m pytest tests/e2e/wg -v -p no:xdist

test-e2e: test-e2e-api test-e2e-ui test-e2e-wg

reset-test-admin-pass:
ifndef PIC_TEST_ADMIN_PASS
	$(error Usage: make reset-test-admin-pass PIC_TEST_ADMIN_PASS=newpassword)
endif
	python3 scripts/reset_admin_password.py "$(PIC_TEST_ADMIN_PASS)"

# ── Admin password management ──────────────────────────────────────────────────

show-admin-password:
	@sudo python3 scripts/reset_admin_password.py --show

reset-admin-password:
	@docker exec cell-api python3 /app/scripts/reset_admin_password.py --generate


# ── Network / peers ───────────────────────────────────────────────────────────

show-routes:
	@docker exec cell-wireguard wg show 2>/dev/null || echo "WireGuard not running"

list-peers:
	@curl -s http://localhost:3000/api/peers | python3 -m json.tool || echo "API not responding"

add-peer:
	@if [ -n "$(PEER_NAME)" ] && [ -n "$(PEER_IP)" ] && [ -n "$(PEER_KEY)" ]; then \
		curl -X POST http://localhost:3000/api/peers \
		-H "Content-Type: application/json" \
		-d '{"name":"$(PEER_NAME)","ip":"$(PEER_IP)","public_key":"$(PEER_KEY)"}'; \
	else \
		echo "Usage: make add-peer PEER_NAME=name PEER_IP=10.0.0.x PEER_KEY=<pubkey>"; \
	fi

# ── Dev ───────────────────────────────────────────────────────────────────────

dev:
	$(DC) -f docker-compose.yml -f docker-compose.dev.yml up -d
