#!/usr/bin/env bash
# scripts/checks/codeception-scoped.sh — run only the tests mapped to staged files.
# Called by lefthook (pre-commit) and CI (pr-base.yml unscoped runs full suite separately).
#
# Strategy: map changed source files to candidate test files using path conventions,
# run unit suite by default, add functional only when a functional test is mapped.
# Exit 0 with a note when no tests map (the policy's five-category checklist enforces
# coverage at the PR level, not here).
#
# Args:
#   $1 — space- or newline-separated list of PHP file paths, OR empty.
#
# Exit: 0 = pass / no mapped tests, 1 = test failures.
#
# Bash 3.2 compatible — no associative arrays. macOS ships Bash 3.2 by default
# (GPLv2; never updated by Apple) so the hook must run there without `brew install bash`.

set -uo pipefail

files="${1:-}"
[ -z "$files" ] && exit 0

# Newline-separated list of test paths to run; deduplicated via `sort -u` at the end.
tests_to_run=""
need_functional=0

# Helper: append a test path to the list if the file exists.
add_test() {
    local candidate="$1"
    if [ -f "$candidate" ]; then
        if [ -z "$tests_to_run" ]; then
            tests_to_run="$candidate"
        else
            tests_to_run="${tests_to_run}
${candidate}"
        fi
    fi
}

for file in $(echo "$files" | tr ' ' '\n' | grep -E '\.php$'); do
    base=$(basename "$file" .php)

    case "$file" in
        # Form models (subdir) — match before the broader models/* case.
        backend/models/form/*)
            add_test "backend/tests/unit/models/form/${base}Test.php"
            ;;

        # Search models (subdir) — match before the broader models/* case.
        backend/models/search/*)
            add_test "backend/tests/unit/models/search/${base}Test.php"
            ;;

        # Models (web + common)
        backend/models/*|common/models/*)
            add_test "backend/tests/unit/models/${base}Test.php"
            add_test "backend/tests/unit/${base}Test.php"
            add_test "common/tests/unit/models/${base}Test.php"
            add_test "common/tests/unit/${base}Test.php"
            ;;

        # API module controllers — match before the broader controllers/* case.
        backend/modules/api/v*/controllers/*)
            controller_base="${base%Controller}"
            for c in backend/tests/api/"${controller_base}"*Cest.php \
                     backend/tests/functional/api/"${controller_base}"*Cest.php; do
                if [ -f "$c" ]; then
                    add_test "$c"
                    need_functional=1
                fi
            done
            ;;

        # Controllers → functional Cest
        backend/controllers/*)
            controller_base="${base%Controller}"
            for c in backend/tests/functional/"${controller_base}"*Cest.php \
                     backend/tests/functional/*/"${controller_base}"*Cest.php; do
                if [ -f "$c" ]; then
                    add_test "$c"
                    need_functional=1
                fi
            done
            ;;

        # Services
        backend/services/*)
            add_test "backend/tests/unit/services/${base}Test.php"
            ;;

        # Jobs
        backend/jobs/*)
            add_test "backend/tests/unit/jobs/${base}Test.php"
            ;;

        # Helpers — glob patterns inside the loop so empty matches don't error.
        backend/helpers/*|common/helpers/*)
            for c in backend/tests/unit/helpers/*"${base}"*Test.php \
                     common/tests/unit/helpers/*"${base}"*Test.php; do
                add_test "$c"
            done
            ;;

        # Components
        backend/components/*|common/components/*)
            add_test "backend/tests/unit/components/${base}Test.php"
            add_test "common/tests/unit/components/${base}Test.php"
            ;;

        # Filters
        backend/filters/*)
            add_test "backend/tests/unit/filters/${base}Test.php"
            ;;

        # Traits — touching a trait can affect every class using it. Expensive to
        # be precise; log a warning and run the whole unit suite.
        backend/traits/*|common/traits/*)
            echo "  ! Trait $file edited — falling back to full unit suite (expensive)"
            vendor/bin/codecept run unit
            exit $?
            ;;

        # Tests themselves — run them
        */tests/*Test.php|*/tests/*Cest.php)
            add_test "$file"
            case "$file" in
                */functional/*) need_functional=1 ;;
            esac
            ;;
    esac
done

if [ -z "$tests_to_run" ]; then
    echo "  (no scoped tests for staged changes — PR checklist enforces coverage)"
    exit 0
fi

# Build a regex of test class names for Codeception's --filter.
# Deduplicate with sort -u so the same test isn't passed twice.
classes=$(printf '%s\n' "$tests_to_run" | sort -u | while IFS= read -r f; do
    [ -n "$f" ] && basename "$f" .php
done | paste -sd '|' -)

if [ "$need_functional" -eq 1 ]; then
    suites="unit,functional"
else
    suites="unit"
fi

echo "  Running scoped tests on suites '${suites}': ${classes}"
vendor/bin/codecept run "$suites" --filter "$classes"
