# Lint Lua/TypeScript source and validate markdown links.
check: check-luacheck check-lychee npm-check
    luacheck --quiet .
    @just lint-markdown

# Auto-fix package version bumps derived from changed package files.
fix:
    @just check-packages --fix

# Validate package descriptors and require version bumps for changed package files.
check-packages *args: check-jq
    #!/usr/bin/env bash
    set -euo pipefail
    repo='{{justfile_directory()}}'
    cd "$repo"

    fix=0
    for arg in {{args}}; do
        case "$arg" in
            --fix) fix=1 ;;
            *)
                printf '%s\n' "FAIL: unknown check-packages option: $arg" >&2
                exit 2
                ;;
        esac
    done

    semver_gt() {
        local a_major a_minor a_patch b_major b_minor b_patch
        IFS=. read -r a_major a_minor a_patch <<<"$1"
        IFS=. read -r b_major b_minor b_patch <<<"$2"
        if [ "${a_major:-0}" -gt "${b_major:-0}" ]; then return 0; fi
        if [ "${a_major:-0}" -lt "${b_major:-0}" ]; then return 1; fi
        if [ "${a_minor:-0}" -gt "${b_minor:-0}" ]; then return 0; fi
        if [ "${a_minor:-0}" -lt "${b_minor:-0}" ]; then return 1; fi
        [ "${a_patch:-0}" -gt "${b_patch:-0}" ]
    }

    bump_patch() {
        local version major minor patch
        version="$1"
        IFS=. read -r major minor patch <<<"$version"
        printf '%s.%s.%s\n' "${major:-0}" "${minor:-0}" "$(( ${patch:-0} + 1 ))"
    }

    set_json_version() {
        local path version tmp
        path="$1"
        version="$2"
        tmp="$(mktemp)"
        while IFS= read -r line; do
            if [[ "$line" =~ ^([[:space:]]*\"version\"[[:space:]]*:[[:space:]]*\")[^\"]*(\".*)$ ]]; then
                printf '%s%s%s\n' "${BASH_REMATCH[1]}" "$version" "${BASH_REMATCH[2]}"
            else
                printf '%s\n' "$line"
            fi
        done <"$path" >"$tmp"
        mv "$tmp" "$path"
    }

    set_index_version() {
        local name version tmp
        name="$1"
        version="$2"
        tmp="$(mktemp)"
        jq --arg name "$name" --arg version "$version" '.packages[$name] = $version' packages/index.json >"$tmp"
        mv "$tmp" packages/index.json
    }

    bump_package() {
        local name desc old_version current_version new_version
        name="$1"
        desc="$2"
        old_version="$3"
        current_version="$(jq -r '.version // empty' "$desc")"
        if semver_gt "$current_version" "$old_version"; then
            return 0
        fi
        new_version="$(bump_patch "$old_version")"
        set_json_version "$desc" "$new_version"
        set_index_version "$name" "$new_version"
        if [ "$name" = trapos ]; then
            set_json_version manifest.json "$new_version"
        fi
        printf '%s\n' "FIX: bumped $name from $current_version to $new_version"
    }

    fail=0
    packages=()
    while IFS= read -r name; do packages+=("$name"); done < <(jq -r '.packages | keys[]' packages/index.json | sort)

    for name in "${packages[@]}"; do
        desc="packages/$name/ccpm.json"
        if [ ! -f "$desc" ]; then
            printf '%s\n' "FAIL: packages/index.json lists missing descriptor $desc" >&2
            fail=1
            continue
        fi

        desc_name="$(jq -r '.name // empty' "$desc")"
        desc_version="$(jq -r '.version // empty' "$desc")"
        index_version="$(jq -r --arg name "$name" '.packages[$name] // empty' packages/index.json)"
        if [ "$desc_name" != "$name" ]; then
            printf '%s\n' "FAIL: $desc has name '$desc_name', expected '$name'" >&2
            fail=1
        fi
        if [ "$desc_version" != "$index_version" ]; then
            printf '%s\n' "FAIL: $name version differs between descriptor ($desc_version) and packages/index.json ($index_version)" >&2
            fail=1
        fi

        last_desc_commit="$(git log -n 1 --format=%H -- "$desc" || true)"
        if [ -z "$last_desc_commit" ]; then
            continue
        fi
        old_version="$(git show "$last_desc_commit:$desc" | jq -r '.version // empty')"

        files=()
        while IFS= read -r file; do files+=("packages/$name/$file"); done < <(jq -r '.files[]?' "$desc")
        if [ "${#files[@]}" -gt 0 ] && ! git diff --quiet "$last_desc_commit" -- "${files[@]}"; then
            if ! semver_gt "$desc_version" "$old_version"; then
                if [ "$fix" -eq 1 ]; then
                    bump_package "$name" "$desc" "$old_version"
                    desc_version="$(jq -r '.version // empty' "$desc")"
                    index_version="$(jq -r --arg name "$name" '.packages[$name] // empty' packages/index.json)"
                else
                    printf '%s\n' "FAIL: $name package files changed since $desc was last bumped ($old_version); bump $desc and packages/index.json" >&2
                    git diff --name-only "$last_desc_commit" -- "${files[@]}" >&2
                    fail=1
                fi
            fi
        fi
    done

    for desc in packages/*/ccpm.json; do
        [ -e "$desc" ] || continue
        name="$(jq -r '.name // empty' "$desc")"
        if ! jq -e --arg name "$name" '.packages[$name] != null' packages/index.json >/dev/null; then
            printf '%s\n' "FAIL: descriptor $desc is missing from packages/index.json" >&2
            fail=1
        fi
    done

    trapos_desc='packages/trapos/ccpm.json'
    trapos_commit="$(git log -n 1 --format=%H -- "$trapos_desc" || true)"
    if [ -n "$trapos_commit" ]; then
        trapos_version="$(jq -r '.version // empty' "$trapos_desc")"
        old_trapos_version="$(git show "$trapos_commit:$trapos_desc" | jq -r '.version // empty')"
        deps=()
        while IFS= read -r dep; do deps+=("packages/$dep/ccpm.json"); done < <(jq -r '.dependencies[]?' "$trapos_desc")
        if [ "${#deps[@]}" -gt 0 ] && ! git diff --quiet "$trapos_commit" -- "${deps[@]}"; then
            if ! semver_gt "$trapos_version" "$old_trapos_version"; then
                if [ "$fix" -eq 1 ]; then
                    bump_package trapos "$trapos_desc" "$old_trapos_version"
                else
                    printf '%s\n' "FAIL: trapos dependencies changed since trapos was last bumped ($old_trapos_version); bump $trapos_desc and packages/index.json" >&2
                    git diff --name-only "$trapos_commit" -- "${deps[@]}" >&2
                    fail=1
                fi
            fi
        fi
    fi

    trapos_version="$(jq -r '.version // empty' "$trapos_desc")"
    manifest_version="$(jq -r '.version // empty' manifest.json)"
    if [ "$manifest_version" != "$trapos_version" ]; then
        if [ "$fix" -eq 1 ]; then
            set_json_version manifest.json "$trapos_version"
            printf '%s\n' "FIX: synced manifest.json from $manifest_version to $trapos_version"
        else
            printf '%s\n' "FAIL: manifest.json version ($manifest_version) differs from trapos package ($trapos_version)" >&2
            fail=1
        fi
    fi

    if [ "$fail" -ne 0 ]; then exit 1; fi
    printf '%s\n' 'OK: package versions aligned'

# Validate local markdown links and heading anchors with lychee.
lint-markdown: check-lychee
    lychee --config lychee.toml .
