#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# shellcheck source=../action/action.sh
source "$PROJECT_ROOT/scripts/action/action.sh"
LIBRARY_GIT_SOURCED=0

# INTERNAL: library_source_git()
# 作用: 按需加载 Git 操作函数。
# 参数: 无。
# 返回: 无。
# 注意: list-ops/list-cmake-ops/list-git-ops/--help 不需要加载 git.sh。Windows 下如果 git.sh 或其换行存在问题，不能影响操作枚举。
library_source_git() {
    if [[ "$LIBRARY_GIT_SOURCED" == "0" ]]; then
        # shellcheck source=../git/git.sh
        source "$PROJECT_ROOT/scripts/git/git.sh"
        LIBRARY_GIT_SOURCED=1
    fi
}

library_usage() {
    cat <<'USAGE'
usage:
  library.sh list-ops
  library.sh list-cmake-ops
  library.sh list-git-ops
  library.sh <op> --data-dir <dir> [--config <config>] [target...]

operations are implemented in Bash. CMake only prepares registry/global data and invokes this script.
USAGE
}

library_list_cmake_self_ops() {
    # 每个 echo 独立一行，不使用 here-doc 或反斜杠续行，避免 Windows/Git Bash 下 CRLF 影响操作枚举输出。
    # 这些是不递归依赖的单对象操作；传入哪个库 id，就只操作哪个库 id。
    echo 'source'
    echo 'build'
    echo 'install'
    echo 'auto'
    echo 'rebuild'
    echo 'reinstall'
    echo 'reset'
    echo 'clean-source'
    echo 'clean-build'
    echo 'clean-install'
    echo 'clean-all'
}

library_list_git_self_ops() {
    # Git 操作默认也是单对象操作，不递归依赖。
    echo 'git-fetch'
    echo 'git-pull'
    echo 'git-checkout'
    echo 'git-branches'
    echo 'git-current-branch'
}

library_list_cmake_ops() {
    # 查询操作不递归执行，不生成 with_rely_ 版本。
    echo 'list-targets'
    echo 'deps'
    library_list_cmake_self_ops
    library_list_cmake_self_ops | action_list_with_rely_ops_from
}

library_list_git_ops() {
    library_list_git_self_ops
    library_list_git_self_ops | action_list_with_rely_ops_from
}

library_list_ops() {
    library_list_cmake_ops
    library_list_git_ops
}

# INTERNAL: library_strip_line_cr(<value>)
# 作用: 去掉从 Windows CRLF 文本文件读取时残留在行尾的 CR。
# 参数: value 是原始行或原始字段。
# 返回: stdout 输出去掉一个行尾 CR 后的值。
# 注意: 只清理文件行尾 CR，不等同于 codec 解码；编码值里的 %0D 会保留给 library_codec_decode 还原。
library_strip_line_cr() {
    local value="$1"
    # Windows/Git Bash may read CMake-generated files with CRLF.
    # Some test/workflow paths can rewrite CRLF repeatedly and produce CRCRLF.
    # Strip every trailing CR so values never become `cmake.exe\r`, `core\r`, or `%00\r`.
    while [[ "$value" == *$'\r' ]]; do
        value="${value%$'\r'}"
    done
    printf '%s' "$value"
}

library_codec_decode() {
    local value="$1"
    value="$(library_strip_line_cr "$value")"
    if [[ "$value" == "%00" ]]; then
        printf ''
        return 0
    fi

    value="${value//%0D/$'\r'}"
    value="${value//%0A/$'\n'}"
    value="${value//%7C/|}"
    value="${value//%3B/;}"
    value="${value//%5C/\\}"
    value="${value//%25/%}"
    printf '%s' "$value"
}

library_map_get_raw() {
    local file="$1"
    local key="$2"
    local line rest line_key value

    while IFS= read -r line || [[ -n "$line" ]]; do
        line="$(library_strip_line_cr "$line")"
        [[ "$line" == K\|* ]] || continue
        rest="${line#K|}"
        line_key="${rest%%|*}"
        value="${rest#*|}"
        if [[ "$line_key" == "$key" ]]; then
            printf '%s' "$value"
            return 0
        fi
    done < "$file"

    printf ''
}

library_map_get() {
    local file="$1"
    local key="$2"
    local raw
    raw="$(library_map_get_raw "$file" "$key")"
    library_codec_decode "$raw"
}

library_global_get() {
    local key="$1"
    library_map_get "$LIBRARY_DATA_DIR/globals.txt" "$key"
}

library_registry_graph() {
    printf '%s/registry/graph.txt' "$LIBRARY_DATA_DIR"
}

library_registry_map_id() {
    local target="$1"
    local line rest node data

    while IFS= read -r line || [[ -n "$line" ]]; do
        line="$(library_strip_line_cr "$line")"
        [[ "$line" == N\|* ]] || continue
        rest="${line#N|}"
        node="${rest%%|*}"
        data="${rest#*|}"
        if [[ "$node" == "$target" ]]; then
            printf '%s' "$data"
            return 0
        fi
    done < "$(library_registry_graph)"

    printf ''
}

library_target_node_dir() {
    local target="$1"
    local map_id
    map_id="$(library_registry_map_id "$target")"
    printf '%s/registry/nodes/%s' "$LIBRARY_DATA_DIR" "$map_id"
}

library_target_map_file() {
    local target="$1"
    printf '%s/map.txt' "$(library_target_node_dir "$target")"
}

library_registry_get() {
    local target="$1"
    local key="$2"
    library_map_get "$(library_target_map_file "$target")" "$key"
}

library_target_get_or_global() {
    local target="$1"
    local key="$2"
    local value
    value="$(library_registry_get "$target" "$key")"
    if [[ -z "$value" ]]; then
        value="$(library_global_get "$key")"
    fi
    printf '%s' "$value"
}

library_all_targets() {
    local line
    while IFS= read -r line || [[ -n "$line" ]]; do
        line="$(library_strip_line_cr "$line")"
        [[ -n "$line" ]] && printf '%s\n' "$line"
    done < "$LIBRARY_DATA_DIR/build_order.txt"
}

library_direct_deps() {
    local target="$1"
    local line rest from to

    while IFS= read -r line || [[ -n "$line" ]]; do
        line="$(library_strip_line_cr "$line")"
        [[ "$line" == E\|* ]] || continue
        rest="${line#E|}"
        from="${rest%%|*}"
        rest="${rest#*|}"
        to="${rest%%|*}"
        if [[ "$from" == "$target" ]]; then
            printf '%s\n' "$to"
        fi
    done < "$(library_registry_graph)"
}

library_resolve_targets() {
    if [[ "$#" -eq 0 ]]; then
        library_all_targets
        return 0
    fi

    local requested=()
    local target dep line
    declare -A want=()

    requested=("$@")
    for target in "${requested[@]}"; do
        want["$target"]=1
        if [[ -f "$LIBRARY_DATA_DIR/dependencies/$target.txt" ]]; then
            while IFS= read -r dep || [[ -n "$dep" ]]; do
                dep="$(library_strip_line_cr "$dep")"
                [[ -n "$dep" ]] && want["$dep"]=1
            done < "$LIBRARY_DATA_DIR/dependencies/$target.txt"
        fi
    done

    while IFS= read -r line || [[ -n "$line" ]]; do
        line="$(library_strip_line_cr "$line")"
        [[ -n "$line" ]] || continue
        if [[ -n "${want[$line]+x}" ]]; then
            printf '%s\n' "$line"
        fi
    done < "$LIBRARY_DATA_DIR/build_order.txt"
}

library_resolve_self_targets() {
    if [[ "$#" -eq 0 ]]; then
        library_all_targets
        return 0
    fi

    local target
    for target in "$@"; do
        target="$(library_strip_line_cr "$target")"
        [[ -n "$target" ]] && printf '%s\n' "$target"
    done
}

library_resolve_dependencies() {
    if [[ "$#" -eq 0 ]]; then
        library_all_targets
        return 0
    fi

    local target dep line
    declare -A want=()

    for target in "$@"; do
        if [[ -f "$LIBRARY_DATA_DIR/dependencies/$target.txt" ]]; then
            while IFS= read -r dep || [[ -n "$dep" ]]; do
                dep="$(library_strip_line_cr "$dep")"
                [[ -n "$dep" ]] && want["$dep"]=1
            done < "$LIBRARY_DATA_DIR/dependencies/$target.txt"
        fi
    done

    while IFS= read -r line || [[ -n "$line" ]]; do
        line="$(library_strip_line_cr "$line")"
        [[ -n "$line" ]] || continue
        if [[ -n "${want[$line]+x}" ]]; then
            printf '%s\n' "$line"
        fi
    done < "$LIBRARY_DATA_DIR/build_order.txt"
}

library_cache_root() {
    local key="$1"
    local value
    value="$(library_global_get "$key")"
    printf '%s' "$value"
}

library_source_dir() {
    printf '%s/%s' "$(library_cache_root SOURCE_CACHE_ROOT)" "$1"
}

library_build_dir() {
    printf '%s/%s' "$(library_cache_root BUILD_CACHE_ROOT)" "$1"
}

library_install_dir() {
    printf '%s/%s' "$(library_cache_root INSTALL_CACHE_ROOT)" "$1"
}

library_lock_file() {
    local step="$1"
    local target="$2"
    local dir
    case "$step" in
        source) dir="$(library_source_dir "$target")" ;;
        build) dir="$(library_build_dir "$target")" ;;
        install) dir="$(library_install_dir "$target")" ;;
        *) dir="$(library_target_node_dir "$target")" ;;
    esac
    printf '%s/%s.success' "$dir" "$step"
}

library_log_file() {
    local step="$1"
    local target="$2"
    printf '%s/%s.log' "$(library_target_node_dir "$target")" "$step"
}

library_log() {
    local step="$1"
    local target="$2"
    local message="$3"
    local log
    log="$(library_log_file "$step" "$target")"
    mkdir -p "$(dirname "$log")"
    printf '[%s][%s][%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$target" "$step" "$message" | tee -a "$log"
}

library_mark_success() {
    local step="$1"
    local target="$2"
    local lock
    lock="$(library_lock_file "$step" "$target")"
    mkdir -p "$(dirname "$lock")"
    printf 'success %s %s\n' "$step" "$(date '+%Y-%m-%d %H:%M:%S')" > "$lock"
}

library_is_success() {
    local step="$1"
    local target="$2"
    [[ -f "$(library_lock_file "$step" "$target")" ]]
}

library_print_command() {
    printf '[command]'
    printf ' %q' "$@"
    printf '\n'
}

library_run_logged() {
    local step="$1"
    local target="$2"
    shift 2
    local log rc
    log="$(library_log_file "$step" "$target")"
    mkdir -p "$(dirname "$log")"
    library_print_command "$@" | tee -a "$log"
    set +e
    "$@" 2>&1 | tee -a "$log"
    rc=${PIPESTATUS[0]}
    set -e
    if [[ "$rc" -ne 0 ]]; then
        library_log "$step" "$target" "command failed with exit code $rc"
        return "$rc"
    fi
    return 0
}

library_run_logged_in_dir() {
    local step="$1"
    local target="$2"
    local dir="$3"
    shift 3
    local log rc
    log="$(library_log_file "$step" "$target")"
    mkdir -p "$(dirname "$log")"
    printf '[cwd] %s\n' "$dir" | tee -a "$log"
    library_print_command "$@" | tee -a "$log"
    set +e
    (cd "$dir" && "$@") 2>&1 | tee -a "$log"
    rc=${PIPESTATUS[0]}
    set -e
    if [[ "$rc" -ne 0 ]]; then
        library_log "$step" "$target" "command failed with exit code $rc"
        return "$rc"
    fi
    return 0
}

library_source_one() {
    local target="$1"
    local dest local_source git_url git_branch
    dest="$(library_source_dir "$target")"

    if library_is_success source "$target"; then
        library_log source "$target" 'source cache already successful, skip'
        return 0
    fi

    mkdir -p "$dest"
    local_source="$(library_registry_get "$target" LOCAL_SOURCE_DIR)"
    git_url="$(library_registry_get "$target" GIT_URL)"
    git_branch="$(library_registry_get "$target" GIT_BRANCH)"

    library_log source "$target" "source start: dest=$dest"

    if [[ -n "$local_source" ]]; then
        mkdir -p "$dest"
        library_run_logged source "$target" cp -a "$local_source/." "$dest/"
    elif [[ -n "$git_url" ]]; then
        library_source_git
        library_run_logged source "$target" library_git_clone_or_update "$git_url" "$git_branch" "$dest"
    else
        library_log source "$target" 'no LOCAL_SOURCE_DIR or GIT_URL; created empty source cache'
    fi

    library_mark_success source "$target"
}

library_split_semicolon_words() {
    local value="$1"
    local old_ifs="$IFS"
    IFS=';'
    # shellcheck disable=SC2206
    LIBRARY_SPLIT_RESULT=($value)
    IFS="$old_ifs"
}

library_cmake_command() {
    local value
    value="$(library_global_get CMAKE_COMMAND)"
    if [[ -z "$value" ]]; then
        value="cmake"
    fi
    printf '%s' "$value"
}

library_build_config() {
    local target="$1"
    local value
    value="$LIBRARY_CLI_CONFIG"
    if [[ -z "$value" ]]; then
        value="$(library_target_get_or_global "$target" CMAKE_BUILD_CONFIG)"
    fi
    if [[ -z "$value" ]]; then
        value="$(library_target_get_or_global "$target" CMAKE_BUILD_TYPE)"
    fi
    printf '%s' "$value"
}

library_build_one() {
    local target="$1"
    local source source_root source_subdir build install cmake_cmd generator platform toolset toolchain build_type options configure_preset build_preset config dep dep_options
    source_root="$(library_source_dir "$target")"
    source_subdir="$(library_registry_get "$target" CMAKE_SOURCE_SUBDIR)"
    source="$source_root"
    if [[ -n "$source_subdir" ]]; then
        source="$source_root/$source_subdir"
    fi
    build="$(library_build_dir "$target")"
    install="$(library_install_dir "$target")"
    cmake_cmd="$(library_cmake_command)"

    if library_is_success build "$target"; then
        library_log build "$target" 'build cache already successful, skip'
        return 0
    fi

    mkdir -p "$build" "$install"
    generator="$(library_target_get_or_global "$target" CMAKE_GENERATOR)"
    platform="$(library_target_get_or_global "$target" CMAKE_GENERATOR_PLATFORM)"
    toolset="$(library_target_get_or_global "$target" CMAKE_GENERATOR_TOOLSET)"
    toolchain="$(library_target_get_or_global "$target" CMAKE_TOOLCHAIN_FILE)"
    build_type="$(library_target_get_or_global "$target" CMAKE_BUILD_TYPE)"
    options="$(library_registry_get "$target" CMAKE_OPTIONS)"
    configure_preset="$(library_target_get_or_global "$target" CMAKE_CONFIGURE_PRESET)"
    build_preset="$(library_target_get_or_global "$target" CMAKE_BUILD_PRESET)"
    config="$(library_build_config "$target")"

    local configure_cmd
    if [[ -n "$configure_preset" ]]; then
        configure_cmd=("$cmake_cmd" -S "$source" -B "$build" --preset "$configure_preset" -DCMAKE_INSTALL_PREFIX="$install")
    else
        configure_cmd=("$cmake_cmd" -S "$source" -B "$build" -DCMAKE_INSTALL_PREFIX="$install")
        [[ -n "$generator" ]] && configure_cmd+=(-G "$generator")
        [[ -n "$platform" ]] && configure_cmd+=(-A "$platform")
        [[ -n "$toolset" ]] && configure_cmd+=(-T "$toolset")
        [[ -n "$toolchain" ]] && configure_cmd+=(-DCMAKE_TOOLCHAIN_FILE="$toolchain")
        [[ -n "$build_type" ]] && configure_cmd+=(-DCMAKE_BUILD_TYPE="$build_type")
    fi

    # OTHER_USE_OPT 是依赖库提供给“使用者”的 CMake configure 参数。
    # 构建当前 target 时，先按 dependencies/<target>.txt 的依赖闭包顺序追加依赖库 OTHER_USE_OPT，
    # 再追加当前 target 自己的 CMAKE_OPTIONS，让当前库的显式配置可以覆盖依赖库给出的默认使用参数。
    while IFS= read -r dep || [[ -n "$dep" ]]; do
        dep_options="$(library_registry_get "$dep" OTHER_USE_OPT)"
        library_split_semicolon_words "$dep_options"
        configure_cmd+=("${LIBRARY_SPLIT_RESULT[@]}")
    done < <(library_resolve_dependencies "$target")

    if [[ -n "$options" ]]; then
        library_split_semicolon_words "$options"
        configure_cmd+=("${LIBRARY_SPLIT_RESULT[@]}")
    fi

    library_log build "$target" "configure/build start: source_root=$source_root source_subdir=$source_subdir source=$source build=$build install=$install configure_preset=$configure_preset build_preset=$build_preset config=$config"
    library_run_logged build "$target" "${configure_cmd[@]}"

    local build_cmd
    if [[ -n "$build_preset" ]]; then
        build_cmd=("$cmake_cmd" --build --preset "$build_preset")
        [[ -n "$config" ]] && build_cmd+=(--config "$config")
        library_run_logged_in_dir build "$target" "$source" "${build_cmd[@]}"
    else
        build_cmd=("$cmake_cmd" --build "$build")
        [[ -n "$config" ]] && build_cmd+=(--config "$config")
        library_run_logged build "$target" "${build_cmd[@]}"
    fi

    library_mark_success build "$target"
}

library_install_one() {
    local target="$1"
    local build install cmake_cmd install_preset config source_root source_subdir source
    build="$(library_build_dir "$target")"
    install="$(library_install_dir "$target")"
    cmake_cmd="$(library_cmake_command)"
    install_preset="$(library_target_get_or_global "$target" CMAKE_INSTALL_PRESET)"
    config="$(library_build_config "$target")"
    source_root="$(library_source_dir "$target")"
    source_subdir="$(library_registry_get "$target" CMAKE_SOURCE_SUBDIR)"
    source="$source_root"
    if [[ -n "$source_subdir" ]]; then
        source="$source_root/$source_subdir"
    fi

    if library_is_success install "$target"; then
        library_log install "$target" 'install cache already successful, skip'
        return 0
    fi

    mkdir -p "$install"
    library_log install "$target" "install start: build=$build install=$install install_preset=$install_preset config=$config"

    local install_cmd
    if [[ -n "$install_preset" ]]; then
        install_cmd=("$cmake_cmd" --install --preset "$install_preset")
        [[ -n "$config" ]] && install_cmd+=(--config "$config")
        library_run_logged_in_dir install "$target" "$source" "${install_cmd[@]}"
    else
        install_cmd=("$cmake_cmd" --install "$build" --prefix "$install")
        [[ -n "$config" ]] && install_cmd+=(--config "$config")
        library_run_logged install "$target" "${install_cmd[@]}"
    fi

    library_mark_success install "$target"
}

library_auto_one() {
    local target="$1"
    if library_is_success install "$target"; then
        library_log install "$target" 'auto: install successful, skip target'
        return 0
    fi

    if ! library_is_success build "$target"; then
        if ! library_is_success source "$target"; then
            library_source_one "$target"
        fi
        library_build_one "$target"
    fi

    library_install_one "$target"
}

library_clean_one() {
    local step="$1"
    local target="$2"
    case "$step" in
        source) rm -rf "$(library_source_dir "$target")" ; rm -f "$(library_lock_file source "$target")" ;;
        build) rm -rf "$(library_build_dir "$target")" ; rm -f "$(library_lock_file build "$target")" ;;
        install) rm -rf "$(library_install_dir "$target")" ; rm -f "$(library_lock_file install "$target")" ;;
        all)
            rm -rf "$(library_source_dir "$target")" "$(library_build_dir "$target")" "$(library_install_dir "$target")"
            rm -f "$(library_lock_file source "$target")" "$(library_lock_file build "$target")" "$(library_lock_file install "$target")"
            ;;
    esac
}

library_git_repo_for_target() {
    local target="$1"
    printf '%s' "$(library_source_dir "$target")"
}

library_git_fetch_one() {
    library_source_git
    local target="$1"
    local repo
    repo="$(library_git_repo_for_target "$target")"
    library_log git-fetch "$target" "git fetch start: repo=$repo"
    library_run_logged git-fetch "$target" library_git_fetch "$repo"
    library_mark_success git-fetch "$target"
}

library_git_pull_one() {
    library_source_git
    local target="$1"
    local repo
    repo="$(library_git_repo_for_target "$target")"
    library_log git-pull "$target" "git pull start: repo=$repo"
    library_run_logged git-pull "$target" library_git_pull "$repo"
    library_mark_success git-pull "$target"
}

library_git_checkout_one() {
    library_source_git
    local target="$1"
    local repo branch
    repo="$(library_git_repo_for_target "$target")"
    branch="$(library_registry_get "$target" GIT_BRANCH)"
    if [[ -n "$branch" ]]; then
        library_log git-checkout "$target" "git checkout start: repo=$repo branch=$branch"
        library_run_logged git-checkout "$target" library_git_checkout "$repo" "$branch"
        library_mark_success git-checkout "$target"
    else
        library_log git-checkout "$target" 'GIT_BRANCH is empty, skip checkout'
        library_mark_success git-checkout "$target"
    fi
}

library_git_branches_one() {
    library_source_git
    local target="$1"
    local repo
    repo="$(library_git_repo_for_target "$target")"
    echo "[$target]"
    library_log git-branches "$target" "git branch list: repo=$repo"
    library_run_logged git-branches "$target" library_git_branch_list "$repo"
    library_mark_success git-branches "$target"
}

library_git_current_branch_one() {
    library_source_git
    local target="$1"
    local repo
    repo="$(library_git_repo_for_target "$target")"
    library_log git-current-branch "$target" "git current branch: repo=$repo"
    library_run_logged git-current-branch "$target" library_git_branch_current "$repo"
    library_mark_success git-current-branch "$target"
}

library_run_operation() {
    local op="$1"
    shift
    local targets=()
    local with_rely=0

    if action_is_with_rely "$op"; then
        with_rely=1
        op="$(action_strip_with_rely "$op")"
    fi

    case "$op" in
        list-targets)
            library_all_targets
            return 0
            ;;
        deps)
            library_resolve_dependencies "$@"
            return 0
            ;;
    esac

    if [[ "$with_rely" == "1" ]]; then
        mapfile -t targets < <(library_resolve_targets "$@")
    else
        mapfile -t targets < <(library_resolve_self_targets "$@")
    fi

    case "$op" in
        source)
            local t
            for t in "${targets[@]}"; do library_source_one "$t"; done
            ;;
        build)
            local t
            for t in "${targets[@]}"; do library_source_one "$t"; library_build_one "$t"; done
            ;;
        install)
            local t
            for t in "${targets[@]}"; do library_source_one "$t"; library_build_one "$t"; library_install_one "$t"; done
            ;;
        auto)
            local t
            for t in "${targets[@]}"; do library_auto_one "$t"; done
            ;;
        rebuild)
            local t
            for t in "${targets[@]}"; do library_clean_one build "$t"; library_clean_one install "$t"; library_source_one "$t"; library_build_one "$t"; library_install_one "$t"; done
            ;;
        reinstall)
            local t
            for t in "${targets[@]}"; do library_clean_one install "$t"; library_install_one "$t"; done
            ;;
        reset)
            local t
            for t in "${targets[@]}"; do library_clean_one all "$t"; library_source_one "$t"; library_build_one "$t"; library_install_one "$t"; done
            ;;
        clean-source)
            local t
            for t in "${targets[@]}"; do library_clean_one source "$t"; done
            ;;
        clean-build)
            local t
            for t in "${targets[@]}"; do library_clean_one build "$t"; done
            ;;
        clean-install)
            local t
            for t in "${targets[@]}"; do library_clean_one install "$t"; done
            ;;
        clean-all)
            local t
            for t in "${targets[@]}"; do library_clean_one all "$t"; done
            ;;
        git-fetch)
            local t
            for t in "${targets[@]}"; do library_git_fetch_one "$t"; done
            ;;
        git-pull)
            local t
            for t in "${targets[@]}"; do library_git_pull_one "$t"; done
            ;;
        git-checkout)
            local t
            for t in "${targets[@]}"; do library_git_checkout_one "$t"; done
            ;;
        git-branches)
            local t
            for t in "${targets[@]}"; do library_git_branches_one "$t"; done
            ;;
        git-current-branch)
            local t
            for t in "${targets[@]}"; do library_git_current_branch_one "$t"; done
            ;;
        *)
            echo "unknown operation: $op" >&2
            return 2
            ;;
    esac
}

main() {
    if [[ "$#" -eq 0 ]]; then
        library_usage >&2
        return 2
    fi

    local op="$1"
    shift

    case "$op" in
        list-ops)
            library_list_ops
            return 0
            ;;
        list-cmake-ops)
            library_list_cmake_ops
            return 0
            ;;
        list-git-ops)
            library_list_git_ops
            return 0
            ;;
        -h|--help|help)
            library_usage
            return 0
            ;;
    esac

    LIBRARY_DATA_DIR=""
    LIBRARY_CLI_CONFIG=""
    local targets=()
    while [[ "$#" -gt 0 ]]; do
        case "$1" in
            --data-dir)
                LIBRARY_DATA_DIR="$2"
                shift 2
                ;;
            --config)
                LIBRARY_CLI_CONFIG="$2"
                shift 2
                ;;
            --)
                shift
                while [[ "$#" -gt 0 ]]; do targets+=("$1"); shift; done
                ;;
            *)
                targets+=("$1")
                shift
                ;;
        esac
    done

    if [[ -z "$LIBRARY_DATA_DIR" ]]; then
        echo "--data-dir is required for operation: $op" >&2
        return 2
    fi

    if [[ -z "$LIBRARY_CLI_CONFIG" ]]; then
        LIBRARY_CLI_CONFIG="$(library_global_get CMAKE_BUILD_CONFIG)"
    fi

    library_run_operation "$op" "${targets[@]}"
}

main "$@"
exit $?
