# Makefile for notability-extractor
# Run `make help` to list targets.

.DEFAULT_GOAL := check
SHELL := bash

# pass CLI args through: make run ARGS="--list-tables"
ARGS ?=

.PHONY: help install install-dev smoke-test smoke-clean reset-shortcut \
        lock lint typecheck format format-check \
        release github-release pypi-release \
        test test-cov build run clean check

# where smoke-test drops its sandbox venv
SMOKE_VENV ?= .venv-smoke

help: ## show this help
	@awk 'BEGIN {FS = ":.*##"; printf "Targets:\n"} \
	      /^[a-zA-Z_-]+:.*?##/ { printf "  %-14s %s\n", $$1, $$2 }' $(MAKEFILE_LIST)

# `install` exists only to redirect anyone who runs the obvious target after
# cloning the repo. It does NOT install anything - it just tells the user
# what to do. The real dev workflow is `make install-dev`.
install: ## (end users) print install instructions - real target is install-dev
	@echo ""
	@echo "This repo's Makefile is for development only."
	@echo ""
	@echo "If you just want to USE notability-extractor, install it from PyPI:"
	@echo ""
	@echo "    pip3 install --user notability-extractor    # macOS"
	@echo "    pip  install --user notability-extractor    # Linux"
	@echo ""
	@echo "If you're hacking on the code, set up the dev environment with:"
	@echo ""
	@echo "    make install-dev"
	@echo ""
	@exit 1

install-dev: ## (devs) set up uv venv + put scripts on PATH (~/.local/bin)
	@if ! command -v uv >/dev/null 2>&1; then \
		echo ""; \
		echo "ERROR: 'uv' is not installed."; \
		echo ""; \
		echo "make install-dev is the developer workflow and needs uv."; \
		echo "Install uv first: https://docs.astral.sh/uv/getting-started/installation/"; \
		echo ""; \
		echo "If you only want to USE notability-extractor (not contribute to it),"; \
		echo "skip uv entirely and run:"; \
		echo ""; \
		echo "    pip3 install --user notability-extractor"; \
		echo ""; \
		exit 1; \
	fi
	uv sync
	uv tool install --editable --force .
	@echo ""
	@echo ">> installing into active pip environment ($(shell which pip))"
	@# This ensures the binary that 'which notability-extractor' resolves to
	@# (mise, pyenv, or system pip) picks up the latest source changes.
	@# Skip when pip is the system-managed one (Debian / Pop!OS / Ubuntu 23+
	@# block this via PEP 668). The uv tool install above already put the
	@# editable binary on PATH at ~/.local/bin, so this step is a no-op there.
	@if pip install -e . --quiet 2>/tmp/notext-pip-err; then \
		echo "   pip install -e . done"; \
	elif grep -q "externally-managed-environment" /tmp/notext-pip-err 2>/dev/null; then \
		echo "   skipping pip install -e: system pip is externally-managed (PEP 668)."; \
		echo "   ~/.local/bin/notability-extractor from uv tool install is on PATH already."; \
	else \
		cat /tmp/notext-pip-err >&2; \
		echo "   pip install -e . failed (non-PEP-668 error above) - continuing."; \
	fi; \
	rm -f /tmp/notext-pip-err
	@echo ""
	@echo ">> installing desktop launcher (so the app shows in your menu)"
	@notability-extractor --install-shortcut || \
		echo "   (shortcut install failed, you can run it manually later)"

lock: ## regenerate uv.lock
	uv lock

lint: ## run ruff check then pylint on src and tests
	uv run ruff check src tests
	uv run pylint src tests

typecheck: ## run mypy then pyright on src
	uv run mypy src
	uv run pyright

format: ## run black then autopep8 (writes changes in place)
	uv run black src tests
	uv run autopep8 --in-place --recursive src tests

format-check: ## verify formatting without writing
	uv run black --check src tests
	@diff_out=$$(uv run autopep8 --diff --recursive src tests); \
	if [ -n "$$diff_out" ]; then \
		echo "$$diff_out"; \
		echo "autopep8 would make changes - run 'make format'"; \
		exit 1; \
	fi

test: ## run pytest
	uv run pytest

test-cov: ## run pytest with coverage report
	uv run pytest --cov --cov-report=term-missing

build: ## build wheel and sdist into dist/
	uv build

# Smoke-test the full end-user pip-install round-trip locally. Mimics what
# `pip3 install notability-extractor` does from PyPI, but using your local
# wheel. Self-contained: builds, installs, verifies, uninstalls, and tears
# down the venv. Leaves nothing behind on success.
#
# What it proves: the wheel resolves all runtime deps, the entry point
# scripts get installed correctly, --version returns the right value, the
# GUI binary is on PATH, and pip uninstall removes everything cleanly.
#
# What it does NOT prove: that the GUI itself launches (that needs a real
# display) or that the desktop shortcut auto-installs (that's a separate
# flow - see `make reset-shortcut` + manual GUI launch).
smoke-test: ## build wheel + pip install + verify --version + pip uninstall
	@echo ">> cleaning dist/ (stale wheels collide with pip install dist/*.whl)"
	@rm -rf dist/
	@echo ">> building wheel"
	@uv build
	@echo ">> creating clean venv at $(SMOKE_VENV)"
	@rm -rf $(SMOKE_VENV)
	@# uv venv --seed bootstraps the venv with real pip + setuptools + wheel
	@# so we can use stdlib-equivalent pip to install (mimics end-user pip3
	@# install from PyPI). Avoids the system python3-venv apt dep.
	@uv venv --seed $(SMOKE_VENV) >/dev/null
	@$(SMOKE_VENV)/bin/pip install --upgrade pip >/dev/null
	@echo ""
	@echo ">> pip install dist/*.whl (mimics 'pip3 install notability-extractor' from PyPI)"
	@$(SMOKE_VENV)/bin/pip install dist/*.whl
	@echo ""
	@echo "version check:"
	@$(SMOKE_VENV)/bin/notability-extractor --version
	@test -x $(SMOKE_VENV)/bin/notability-extractor-gui && \
		echo "GUI entry point installed: $(SMOKE_VENV)/bin/notability-extractor-gui"
	@echo ""
	@echo ">> pip uninstall (mimics end-user removing the package)"
	@$(SMOKE_VENV)/bin/pip uninstall -y notability-extractor >/dev/null
	@echo ">> removing venv"
	@rm -rf $(SMOKE_VENV)
	@echo ""
	@echo "PASS: wheel installs, runs, and uninstalls cleanly"

# Recovery target for when smoke-test got interrupted (ctrl-C, crashed
# halfway, etc) and left state behind. Idempotent.
smoke-clean: ## recover from a half-finished smoke-test (remove venv + any shortcut)
	@echo ">> removing $(SMOKE_VENV)"
	@rm -rf $(SMOKE_VENV)
	@$(MAKE) -s reset-shortcut

# Wipe the desktop shortcut + the "we already tried" marker so the next GUI
# launch will auto-install fresh. Useful when iterating on the shortcut code.
reset-shortcut: ## remove desktop launcher and clear the first-launch marker
	@echo ">> removing desktop launcher (if installed)"
	@rm -f $$HOME/.local/share/applications/notability-extractor.desktop \
	       $$HOME/.local/share/icons/hicolor/scalable/apps/notability-extractor.svg
	@rm -rf "$$HOME/Applications/Notability Extractor.app"
	@echo ">> clearing shortcut_install_attempted marker in config"
	@if [ -f $$HOME/.notability_extractor/config.json ]; then \
		python3 -c "import json, pathlib; p=pathlib.Path.home()/'.notability_extractor'/'config.json'; \
cfg=json.loads(p.read_text()); cfg['shortcut_install_attempted']=False; \
p.write_text(json.dumps(cfg, indent=2)+chr(10))"; \
	fi
	@echo ">> done. next GUI launch will auto-install the shortcut again."

run: ## run the CLI - pass args via ARGS="..."
	uv run notability-extractor $(ARGS)

clean: ## remove build artifacts and tool caches
	rm -rf build/ dist/ *.egg-info src/*.egg-info \
	       .pytest_cache .coverage htmlcov/ \
	       .mypy_cache .ruff_cache
	find . -type d -name __pycache__ -exec rm -rf {} +

check: lint typecheck format-check test ## full gate: lint + typecheck + format-check + test

release: ## print release flow (run github-release + pypi-release explicitly)
	@echo ""
	@echo "Release is two explicit steps (github + pypi are decoupled):"
	@echo ""
	@echo "    make github-release   # tag, build, draft, notes (vim), publish"
	@echo "    make pypi-release     # uploads wheel to pypi via twine"
	@echo ""
	@echo "Run them in either order. CI no longer auto-tags or auto-drafts -"
	@echo "github-release.sh handles tag creation, push, wheel build, and"
	@echo "draft+publish in one shot. See README 'Releasing' for the full flow."
	@echo ""

github-release: ## tag, build wheel, create draft, edit notes, publish github release
	@./scripts/github-release.sh

pypi-release: ## upload the current version's wheel to pypi
	@./scripts/pypi-release.sh
