[tox]
envlist = lint, unit, integration
no_package = true

# ── Base ─────────────────────────────────────────────────────────────────────
[testenv]
allowlist_externals = timeout, perl, docker, mkdir, bash, grep, ruff, pytest, mypy, uvicorn
sitepackages = true
commands_pre = pip install -e ".[dev]" --quiet

passenv = OLLAMA_URL

setenv =
    TIMMY_TEST_MODE = 1
    TIMMY_DISABLE_CSRF = 1
    TIMMY_SKIP_EMBEDDINGS = 1

# ── Lint & Format ────────────────────────────────────────────────────────────

[testenv:lint]
description = Check formatting, imports, security (ruff), no inline CSS
commands_pre =
deps =
    ruff>=0.8.0
commands =
    ruff check src/ tests/
    ruff format --check src/ tests/
    bash -c 'files=$(grep -rl "<style" src/dashboard/templates/ --include="*.html" 2>/dev/null); if [ -n "$files" ]; then echo "ERROR: inline <style> blocks found — move CSS to static/css/mission-control.css:"; echo "$files"; exit 1; fi; echo "No inline CSS — OK"'

[testenv:format]
description = Auto-format code with ruff
commands_pre =
deps =
    ruff>=0.8.0
commands =
    ruff check --fix src/ tests/
    ruff format src/ tests/

[testenv:typecheck]
description = Static type checking with mypy
commands_pre =
deps =
    mypy>=1.0.0
commands =
    mypy src --ignore-missing-imports --no-error-summary

# ── Test Environments ────────────────────────────────────────────────────────

[testenv:unit]
description = Fast unit tests — only tests marked @pytest.mark.unit
commands =
    pytest tests/ -q --tb=short \
        -m "unit and not ollama and not docker and not selenium and not external_api and not skip_ci and not slow" \
        -n auto --dist worksteal

[testenv:integration]
description = Integration tests (marked with @pytest.mark.integration)
commands =
    pytest tests/ -q --tb=short \
        -m "integration and not ollama and not docker and not selenium and not external_api and not slow" \
        -n auto --dist worksteal

[testenv:functional]
description = Functional tests — real HTTP, no mocking (excl slow + selenium)
commands =
    pytest tests/functional/ -q --tb=short -n0 \
        -m "not slow and not selenium"

[testenv:e2e]
description = End-to-end tests — full system, may be slow
commands =
    pytest tests/e2e/ -q --tb=short -n0

[testenv:fast]
description = All tests except e2e, functional, and external
commands =
    pytest tests/ -q --tb=short \
        --ignore=tests/e2e \
        --ignore=tests/functional \
        -m "not ollama and not docker and not selenium and not external_api and not slow" \
        -n auto --dist worksteal

[testenv:ollama]
description = Live LLM tests via Ollama (requires running Ollama)
commands =
    pytest tests/ -q --tb=short -m ollama --timeout=120

[testenv:benchmark]
description = Agent performance regression benchmark suite
commands =
    python scripts/run_benchmarks.py {posargs}

# ── CI / Coverage ────────────────────────────────────────────────────────────

[testenv:ci]
description = CI test suite with coverage + JUnit XML (mirrors Gitea Actions)
commands =
    mkdir -p reports
    pytest tests/ \
        --cov=src \
        --cov-report=term-missing \
        --cov-report=xml:reports/coverage.xml \
        --cov-fail-under=73 \
        --junitxml=reports/junit.xml \
        -p no:xdist \
        -m "not ollama and not docker and not selenium and not external_api and not skip_ci and not slow"

[testenv:coverage]
description = Full coverage report (terminal + XML)
commands =
    pytest tests/ -q --tb=short \
        --cov=src \
        --cov-report=term-missing \
        --cov-report=xml \
        --cov-fail-under=73 \
        -p no:xdist \
        -m "not ollama and not docker and not selenium and not external_api and not slow"

[testenv:coverage-html]
description = Coverage with HTML report
commands =
    pytest tests/ -q --tb=short \
        --cov=src \
        --cov-report=term-missing \
        --cov-report=html \
        --cov-fail-under=73 \
        -p no:xdist \
        -m "not ollama and not docker and not selenium and not external_api and not slow"

# ── Pre-push (mirrors CI exactly) ────────────────────────────────────────────

[testenv:pre-push]
description = Local gate — lint + full CI suite (same as Gitea Actions)
deps =
    ruff>=0.8.0
commands =
    ruff check src/ tests/
    ruff format --check src/ tests/
    bash -c 'files=$(grep -rl "<style" src/dashboard/templates/ --include="*.html" 2>/dev/null); if [ -n "$files" ]; then echo "ERROR: inline <style> blocks found — move CSS to static/css/mission-control.css:"; echo "$files"; exit 1; fi; echo "No inline CSS — OK"'
    mkdir -p reports
    pytest tests/ \
        --cov=src \
        --cov-report=term-missing \
        --cov-report=xml:reports/coverage.xml \
        --cov-fail-under=73 \
        --junitxml=reports/junit.xml \
        -p no:xdist \
        -m "not ollama and not docker and not selenium and not external_api and not skip_ci and not slow"

# ── Pre-commit (fast local gate) ────────────────────────────────────────────

[testenv:pre-commit]
description = Fast pre-commit gate — format check + unit tests (30s budget)
deps =
    ruff>=0.8.0
commands =
    ruff check src/ tests/
    ruff format --check src/ tests/
    pytest tests/ -q --tb=short \
        --ignore=tests/e2e \
        --ignore=tests/functional \
        -m "not ollama and not docker and not selenium and not external_api and not skip_ci and not slow" \
        -n auto --dist worksteal

# ── Dev Server ───────────────────────────────────────────────────────────────

[testenv:dev]
description = Start dashboard with auto-reload (local development)
setenv =
    {[testenv]setenv}
    TIMMY_TEST_MODE = 0
commands =
    python {toxinidir}/scripts/dev_server.py {posargs}

# ── All Tests (parallel) ─────────────────────────────────────────────────────

[testenv:all]
description = Run all tests in parallel
commands =
    pytest tests/ -q --tb=short -n auto --dist worksteal
