.DEFAULT_GOAL := help

NODE_BIN := ./node_modules/.bin

define BROWSER_PYSCRIPT
import os, webbrowser, sys
try:
	from urllib import pathname2url
except:
	from urllib.request import pathname2url

webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
endef
export BROWSER_PYSCRIPT
BROWSER := python -c "$$BROWSER_PYSCRIPT"

# pylint depends on this environment variable.
export DJANGO_SETTINGS_MODULE = enterprise.settings.test

help: ## display this help message
	@echo "Please use \`make <target>' where <target> is one of"
	@perl -nle'print $& if m{^[\.a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m  %-25s\033[0m %s\n", $$1, $$2}'

clean: ## remove generated byte code, coverage reports, and build artifacts
	find . -name '*.pyc' -exec rm -f {} +
	find . -name '*.pyo' -exec rm -f {} +
	find . -name '__pycache__' -exec rm -rf {} +
	find . -name '*~' -exec rm -f {} +
	coverage erase
	rm -fr build/
	rm -fr dist/
	rm -fr *.egg-info

clean.static: ## remove all ignored generated static files
	rm -rf src/enterprise/assets/
	rm -rf src/enterprise/static/enterprise/bundles/*.js

static: ## render all static assets for production
	$(NODE_BIN)/webpack --config webpack.config.js --display-error-details --progress --optimize-minimize
	python manage.py collectstatic --noinput
	# TODO: Add the compression app for manage.py settings.
	#python manage.py compress -v3 --force

static.dev: ## gather all static assets for a development environment
	$(NODE_BIN)/webpack --config webpack.config.js --display-error-details --progress

static.watch: ## watch for static asset changes for a development environment
	$(NODE_BIN)/webpack --config webpack.config.js --display-error-details --progress --watch

compile_translations: ## compile translation files, outputting .po files for each supported language
	./manage.py compilemessages

dummy_translations: ## generate dummy translation (.po) files
	cd src/enterprise && i18n_tool dummy

extract_translations: ## extract strings to be translated, outputting .mo files
	rm -rf docs/_build
	i18n_tool extract --no-segment

fake_translations: extract_translations dummy_translations compile_translations ## generate and compile dummy translation files

pull_translations: ## Pull translations from openedx-translations via Atlas
	rm -rf src/enterprise/conf/locale src/consent/conf/locale
	mkdir -p src/enterprise/conf/locale src/consent/conf/locale
	atlas pull $(ATLAS_OPTIONS) \
		translations/edx-enterprise/enterprise/conf/locale:src/enterprise/conf/locale \
		translations/edx-enterprise/consent/conf/locale:src/consent/conf/locale \
		$(ATLAS_EXTRA_SOURCES)
	python manage.py compilemessages

push_translations: ## push source translation files (.po) to Transifex (deprecated)
	tx push -s

coverage: clean ## generate and view HTML coverage report
	py.test --cov-report html
	$(BROWSER) htmlcov/index.html

docs: ## generate Sphinx HTML documentation, including API docs
	tox -e docs
	$(BROWSER) docs/_build/html/index.html

compile-requirements: ## generate the uv.lock file without upgrading packages
	uv lock

# Special for edx-enterprise: treat production package versions from openedx-platform
# as local constraints for testing, so we only test with versions actually used in
# production. Layered on top of edx_lint's global + repo-specific constraints below.
upgrade: ## upgrade all packages in uv.lock and sync constraints from edx-lint and openedx-platform
	uv run --with edx-lint edx_lint write_uv_constraints pyproject.toml
	uv run --with tomlkit python requirements/sync_platform_constraints.py
	uv lock --upgrade

requirements.js: ## install JS requirements for local development
	npm ci

dev_requirements: requirements.js ## sync to requirements for local development
	uv sync --group dev

requirements: dev_requirements ## install development environment requirements

jshint: ## run Javascript linting
	@[ -x ./node_modules/jshint/bin/jshint ] || npm install jshint --no-save
	./node_modules/jshint/bin/jshint src/enterprise
	./node_modules/jshint/bin/jshint spec

test: clean ## run python tests
	py.test

jasmine: ## run javascript tests
	tox -e jasmine

diff_cover: test
	diff-cover coverage.xml

validate: clean static test jasmine quality ## run all tests and quality checks

quality: pylint pycodestyle isort-check jshint pii_check ## run all quality checks

pylint: ## Lint python code
	touch tests/__init__.py
	pylint -j 1 src/enterprise src/enterprise_learner_portal --clear-cache-post-run=y
	pylint -j 1 src/consent src/integrated_channels --clear-cache-post-run=y
	pylint -j 1 test_utils requirements/sync_platform_constraints.py --clear-cache-post-run=y
	pylint -j 1 tests -v --clear-cache-post-run=y
	rm tests/__init__.py

pycodestyle: ## Check python code style
	pycodestyle src/enterprise src/enterprise_learner_portal src/consent src/integrated_channels tests test_utils

pii_check: pii_clean
	tox -e pii_check

pii_clean:
	rm -rf pii_report
	mkdir -p pii_report

isort: ## call isort on packages/files that are checked in quality tests
	tox -e isort

isort-check: ## call isort on packages/files that are checked in quality tests
	tox -e isort-check

########################################################################
# Docker shortcuts for managing a local test/quality container.        #
# For full devstack (edxapp, workers, DB, etc.), see the edx/devstack  #
# repository.                                                          #
########################################################################

dev.pull: ## Pulls the docker image used by the test container (unsupported)
	@echo "ERROR: To re-build the enterprise test container, use `make dev.build` instead."

# Give each clone of this repo its own docker compose project name so that
# multiple clones don't fight over the same test-shell container. docker
# compose automatically reads .env from the project directory.
.env:
	echo "COMPOSE_PROJECT_NAME=edx-enterprise-$$(pwd | shasum | cut -c1-8)" > .env

dev.build: .env ## Builds the docker image used by the test container
	docker compose build test-shell

dev.up: .env ## Starts the test container
	docker compose up --remove-orphans -d test-shell

dev.down: ## Kills the test container and all its data that isn't in volumes
	docker compose down

dev.stop: ## Stops the test container so it can be restarted
	docker compose stop test-shell

dev.makemigrations: ## Create migrations via the test container
	docker compose exec test-shell python manage.py makemigrations

dev.shell: ## Launch a shell in the test container
	docker compose exec test-shell bash

dev.logs: ## View the logs of the test container
	docker compose logs -f --tail=500 test-shell

dev.restart-container: ## Restart the test container
	docker compose restart test-shell

dev.attach: ## Attach to the test container
	docker compose attach test-shell

test-shell-up: dev.up
test-shell-down: dev.down
test-shell-stop: dev.stop
test-shell-makemigrations: dev.makemigrations
test-shell-shell: dev.shell
test-shell-logs: dev.logs
test-shell-restart-container: dev.restart-container
test-shell-attach: dev.attach

dev.up.keycloak: ## start the Keycloak container for SAML IdP testing
	docker compose up --detach keycloak

dev.stop.keycloak: ## stop the Keycloak container
	docker compose stop keycloak

dev.provision.keycloak: ## provision Keycloak SAML IdP and LMS TPA records for testing
	docker compose run --rm keycloak-config-cli
	docker exec -i --env-file keycloak-devstack.env edx.devstack.lms \
		python manage.py lms --settings devstack shell < provision-tpa.py
	@echo ""
	@echo "NOTE: Your browser machine needs this /etc/hosts entry for SAML login to work:"
	@echo "  127.0.0.1 edx.devstack.keycloak"
	@echo ""
	@echo "If you are using a remote codespace, update /etc/hosts on your local machine"
	@echo "(where VS Code / the browser runs), not inside the codespace."
	@echo ""

.PHONY: clean clean.static compile-requirements compile_translations coverage docs dummy_translations extract_translations \
	fake_translations help pull_translations push_translations requirements dev_requirements test upgrade validate isort \
	isort-check static static.dev static.watch quality pylint pycodestyle pii_check pii_clean jasmine \
	dev.pull dev.up dev.down dev.stop dev.makemigrations dev.shell dev.logs dev.restart-container dev.attach \
	dev.up.keycloak dev.stop.keycloak dev.provision.keycloak
