set dotenv-load := true
set shell := ["bash", "-euo", "pipefail", "-c"]

local_url := "http://localhost:" + env_var_or_default("PORT", "8010")
production_url := env_var_or_default("URL", "https://chart.amow.com")

# List available project commands.
default:
    @just --list

# Start the local stack in the background.
up:
    docker compose up -d

# Build images and run the local stack in the foreground.
dev:
    docker compose up --build

# Build or rebuild local images.
build:
    docker compose build

# Recreate the API container after environment or dependency changes.
restart:
    docker compose up -d --force-recreate api

# Stop and remove the local stack.
down:
    docker compose down

# Show local service state.
ps:
    docker compose ps

# Validate and print the resolved Compose configuration.
compose-config:
    docker compose config

# Follow service logs; e.g. `just logs api 20m`.
logs service="api" since="30m":
    docker compose logs --follow --since "{{since}}" "{{service}}"

# Open a shell in the API container.
shell:
    docker compose exec api sh

# Copy .env.example only when .env does not exist.
env:
    @if [[ -e .env ]]; then echo ".env already exists"; else cp .env.example .env && echo "created .env"; fi

# Install local non-Docker development dependencies into .venv.
venv:
    python3 -m venv .venv
    .venv/bin/pip install -r requirements.txt -r requirements-dev.txt

# Run the app without Docker after `just venv`.
serve:
    .venv/bin/uvicorn main:app --reload

# Run all backend tests, or one pytest path/selector.
test selector="":
    @if [[ -n "{{selector}}" ]]; then docker compose exec -T api python -m pytest -q "{{selector}}"; else docker compose exec -T api python -m pytest -q; fi

# Run all browser tests, or files matching a filter such as `trendline`.
e2e filter="":
    ./bin/e2e "{{filter}}"

# Run complete backend and browser suites.
test-all: test e2e

# Check patches for whitespace errors.
check:
    git diff --check

# Require a clean committed worktree.
clean:
    @if [[ -n "$(git status --porcelain)" ]]; then echo "worktree is not clean; commit intended changes first" >&2; exit 1; fi

# Run every required pre-deploy check.
predeploy: check test-all

# Capture the local chart through the Playwright service.
screenshot file="chart.png":
    docker compose exec -T playwright playwright screenshot --lang en-US --wait-for-timeout 5000 http://api:8000 "/artifacts/{{file}}"

# Show local API status.
status:
    curl -fsS "{{local_url}}/api/status"

# Replay Yahoo history through the alert calibration sweep.
calibrate:
    docker compose exec -T api python -m scripts.calibrate_alerts

# Validate existing Schwab credentials, or print the authorization URL.
schwab-check redirect_url="":
    @if [[ -n "{{redirect_url}}" ]]; then docker compose exec -T api python -m scripts.check_schwab --redirect-url "{{redirect_url}}"; else docker compose exec -T api python -m scripts.check_schwab; fi

# Confirm Schwab stream messages for a duration and symbol.
stream seconds="60" symbol="/ES":
    docker compose exec -T api python -m scripts.check_stream "{{seconds}}" "{{symbol}}"

# Wait until production serves local HEAD.
wait-deploy url=production_url timeout="300":
    URL="{{url}}" TIMEOUT="{{timeout}}" ./bin/wait-deploy

# Check public production health and deployed version.
smoke url=production_url:
    curl -fsS "{{url}}/api/health"
    curl -fsS "{{url}}/api/version"

# Check authenticated production status using TOKEN from the environment.
production-status url=production_url:
    @if [[ -z "${TOKEN:-}" ]]; then echo "TOKEN is required" >&2; exit 1; fi; curl -fsS -H "X-Chart-Token: $TOKEN" "{{url}}/api/status"

# Require a clean tree, run all tests, push main, wait, and smoke test.
deploy: clean predeploy
    git push origin main
    ./bin/wait-deploy
    just smoke
