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

# shellcheck disable=SC1090
source "$LLM_PROMPT_UTILS_FILE"
source "$LLM_ROOT_DIR/agents/.shared/utils.sh"

# @env LLM_OUTPUT=/dev/stdout
# @env LLM_AGENT_VAR_PROJECT_DIR=.
# @describe Code review orchestrator tools

_project_dir() {
  local dir="${LLM_AGENT_VAR_PROJECT_DIR:-.}"
  (cd "${dir}" 2>/dev/null && pwd) || echo "${dir}"
}

# @cmd Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean.
# @option --base Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA)
get_diff() {
  local project_dir
  project_dir=$(_project_dir)
  # shellcheck disable=SC2154
  local base="${argc_base:-}"

  local diff_output=""

  if [[ -n "${base}" ]]; then
    diff_output=$(cd "${project_dir}" && git diff "${base}" 2>&1) || true
  else
    diff_output=$(cd "${project_dir}" && git diff --cached 2>&1) || true

    if [[ -z "${diff_output}" ]]; then
      diff_output=$(cd "${project_dir}" && git diff 2>&1) || true
    fi

    if [[ -z "${diff_output}" ]]; then
      diff_output=$(cd "${project_dir}" && git diff HEAD~1 2>&1) || true
    fi
  fi

  if [[ -z "${diff_output}" ]]; then
    warn "No changes found to review" >> "$LLM_OUTPUT"
    return 0
  fi

  local file_count
  file_count=$(echo "${diff_output}" | grep -c '^diff --git' || true)

  {
    info "Diff contains changes to ${file_count} file(s)"
    echo ""
    echo "${diff_output}"
  } >> "$LLM_OUTPUT"
}

# @cmd Get list of changed files with stats
# @option --base Optional base ref to diff against
get_changed_files() {
  local project_dir
  project_dir=$(_project_dir)
  local base="${argc_base:-}"

  local stat_output=""

  if [[ -n "${base}" ]]; then
    stat_output=$(cd "${project_dir}" && git diff --stat "${base}" 2>&1) || true
  else
    stat_output=$(cd "${project_dir}" && git diff --cached --stat 2>&1) || true

    if [[ -z "${stat_output}" ]]; then
      stat_output=$(cd "${project_dir}" && git diff --stat 2>&1) || true
    fi

    if [[ -z "${stat_output}" ]]; then
      stat_output=$(cd "${project_dir}" && git diff --stat HEAD~1 2>&1) || true
    fi
  fi

  if [[ -z "${stat_output}" ]]; then
    warn "No changes found" >> "$LLM_OUTPUT"
    return 0
  fi

  {
    info "Changed files:"
    echo ""
    echo "${stat_output}"
  } >> "$LLM_OUTPUT"
}

# @cmd Get project structure and type information
get_project_info() {
  local project_dir
  project_dir=$(_project_dir)

  local project_info
  project_info=$(detect_project "${project_dir}")

  {
    info "Project: ${project_dir}"
    echo "Type: $(echo "${project_info}" | jq -r '.type')"
    echo ""
    get_tree "${project_dir}" 2
  } >> "$LLM_OUTPUT"
}

# ARGC-BUILD {
# This block was generated by argc (https://github.com/sigoden/argc).
# Modifying it manually is not recommended

_argc_run() {
    if [[ "${1:-}" == "___internal___" ]]; then
        _argc_die "error: unsupported ___internal___ command"
    fi
    if [[ "${OS:-}" == "Windows_NT" ]] && [[ -n "${MSYSTEM:-}" ]]; then
        set -o igncr
    fi
    argc__args=("$(basename "$0" .sh)" "$@")
    argc__positionals=()
    _argc_index=1
    _argc_len="${#argc__args[@]}"
    _argc_tools=()
    _argc_parse
    if [ -n "${argc__fn:-}" ]; then
        $argc__fn "${argc__positionals[@]}"
    fi
}

_argc_usage() {
    cat <<-'EOF'
Code review orchestrator tools

USAGE:  <COMMAND>

COMMANDS:
  get_diff           Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean. [aliases: get-diff]
  get_changed_files  Get list of changed files with stats [aliases: get-changed-files]
  get_project_info   Get project structure and type information [aliases: get-project-info]

ENVIRONMENTS:
  LLM_OUTPUT                 [default: /dev/stdout]
  LLM_AGENT_VAR_PROJECT_DIR  [default: .]
EOF
    exit
}

_argc_version() {
    echo  0.0.0
    exit
}

_argc_parse() {
    local _argc_key _argc_action
    local _argc_subcmds="get_diff, get-diff, get_changed_files, get-changed-files, get_project_info, get-project-info"
    while [[ $_argc_index -lt $_argc_len ]]; do
        _argc_item="${argc__args[_argc_index]}"
        _argc_key="${_argc_item%%=*}"
        case "$_argc_key" in
        --help | -help | -h)
            _argc_usage
            ;;
        --version | -version | -V)
            _argc_version
            ;;
        --)
            _argc_dash="${#argc__positionals[@]}"
            argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}")
            _argc_index=$_argc_len
            break
            ;;
        get_diff | get-diff)
            _argc_index=$((_argc_index + 1))
            _argc_action=_argc_parse_get_diff
            break
            ;;
        get_changed_files | get-changed-files)
            _argc_index=$((_argc_index + 1))
            _argc_action=_argc_parse_get_changed_files
            break
            ;;
        get_project_info | get-project-info)
            _argc_index=$((_argc_index + 1))
            _argc_action=_argc_parse_get_project_info
            break
            ;;
        help)
            local help_arg="${argc__args[$((_argc_index + 1))]:-}"
            case "$help_arg" in
            get_diff | get-diff)
                _argc_usage_get_diff
                ;;
            get_changed_files | get-changed-files)
                _argc_usage_get_changed_files
                ;;
            get_project_info | get-project-info)
                _argc_usage_get_project_info
                ;;
            "")
                _argc_usage
                ;;
            *)
                _argc_die "error: invalid value \`$help_arg\` for \`<command>\`"$'\n'"  [possible values: $_argc_subcmds]"
                ;;
            esac
            ;;
        *)
            _argc_die "error: \`\` requires a subcommand but one was not provided"$'\n'"  [subcommands: $_argc_subcmds]"
            ;;
        esac
    done
    if [[ -n "${_argc_action:-}" ]]; then
        $_argc_action
    else
        _argc_usage
    fi
}

_argc_usage_get_diff() {
    cat <<-'EOF'
Get git diff for code review. Returns staged changes, or unstaged if nothing is staged, or HEAD~1 diff if working tree is clean.

USAGE:  get_diff [OPTIONS]

OPTIONS:
      --base <BASE>  Optional base ref to diff against (e.g., "main", "HEAD~3", a commit SHA)
  -h, --help         Print help

ENVIRONMENTS:
  LLM_OUTPUT                 [default: /dev/stdout]
  LLM_AGENT_VAR_PROJECT_DIR  [default: .]
EOF
    exit
}

_argc_parse_get_diff() {
    local _argc_key _argc_action
    local _argc_subcmds=""
    while [[ $_argc_index -lt $_argc_len ]]; do
        _argc_item="${argc__args[_argc_index]}"
        _argc_key="${_argc_item%%=*}"
        case "$_argc_key" in
        --help | -help | -h)
            _argc_usage_get_diff
            ;;
        --)
            _argc_dash="${#argc__positionals[@]}"
            argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}")
            _argc_index=$_argc_len
            break
            ;;
        --base)
            _argc_take_args "--base <BASE>" 1 1 "-" ""
            _argc_index=$((_argc_index + _argc_take_args_len + 1))
            if [[ -z "${argc_base:-}" ]]; then
                argc_base="${_argc_take_args_values[0]:-}"
            else
                _argc_die "error: the argument \`--base\` cannot be used multiple times"
            fi
            ;;
        *)
            if _argc_maybe_flag_option "-" "$_argc_item"; then
                _argc_die "error: unexpected argument \`$_argc_key\` found"
            fi
            argc__positionals+=("$_argc_item")
            _argc_index=$((_argc_index + 1))
            ;;
        esac
    done
    if [[ -n "${_argc_action:-}" ]]; then
        $_argc_action
    else
        argc__fn=get_diff
        if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then
            _argc_usage_get_diff
        fi
        if [[ -z "${LLM_OUTPUT:-}" ]]; then
            export LLM_OUTPUT=/dev/stdout
        fi
        if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then
            export LLM_AGENT_VAR_PROJECT_DIR=.
        fi
    fi
}

_argc_usage_get_changed_files() {
    cat <<-'EOF'
Get list of changed files with stats

USAGE:  get_changed_files [OPTIONS]

OPTIONS:
      --base <BASE>  Optional base ref to diff against
  -h, --help         Print help

ENVIRONMENTS:
  LLM_OUTPUT                 [default: /dev/stdout]
  LLM_AGENT_VAR_PROJECT_DIR  [default: .]
EOF
    exit
}

_argc_parse_get_changed_files() {
    local _argc_key _argc_action
    local _argc_subcmds=""
    while [[ $_argc_index -lt $_argc_len ]]; do
        _argc_item="${argc__args[_argc_index]}"
        _argc_key="${_argc_item%%=*}"
        case "$_argc_key" in
        --help | -help | -h)
            _argc_usage_get_changed_files
            ;;
        --)
            _argc_dash="${#argc__positionals[@]}"
            argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}")
            _argc_index=$_argc_len
            break
            ;;
        --base)
            _argc_take_args "--base <BASE>" 1 1 "-" ""
            _argc_index=$((_argc_index + _argc_take_args_len + 1))
            if [[ -z "${argc_base:-}" ]]; then
                argc_base="${_argc_take_args_values[0]:-}"
            else
                _argc_die "error: the argument \`--base\` cannot be used multiple times"
            fi
            ;;
        *)
            if _argc_maybe_flag_option "-" "$_argc_item"; then
                _argc_die "error: unexpected argument \`$_argc_key\` found"
            fi
            argc__positionals+=("$_argc_item")
            _argc_index=$((_argc_index + 1))
            ;;
        esac
    done
    if [[ -n "${_argc_action:-}" ]]; then
        $_argc_action
    else
        argc__fn=get_changed_files
        if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then
            _argc_usage_get_changed_files
        fi
        if [[ -z "${LLM_OUTPUT:-}" ]]; then
            export LLM_OUTPUT=/dev/stdout
        fi
        if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then
            export LLM_AGENT_VAR_PROJECT_DIR=.
        fi
    fi
}

_argc_usage_get_project_info() {
    cat <<-'EOF'
Get project structure and type information

USAGE:  get_project_info

ENVIRONMENTS:
  LLM_OUTPUT                 [default: /dev/stdout]
  LLM_AGENT_VAR_PROJECT_DIR  [default: .]
EOF
    exit
}

_argc_parse_get_project_info() {
    local _argc_key _argc_action
    local _argc_subcmds=""
    while [[ $_argc_index -lt $_argc_len ]]; do
        _argc_item="${argc__args[_argc_index]}"
        _argc_key="${_argc_item%%=*}"
        case "$_argc_key" in
        --help | -help | -h)
            _argc_usage_get_project_info
            ;;
        --)
            _argc_dash="${#argc__positionals[@]}"
            argc__positionals+=("${argc__args[@]:$((_argc_index + 1))}")
            _argc_index=$_argc_len
            break
            ;;
        *)
            argc__positionals+=("$_argc_item")
            _argc_index=$((_argc_index + 1))
            ;;
        esac
    done
    if [[ -n "${_argc_action:-}" ]]; then
        $_argc_action
    else
        argc__fn=get_project_info
        if [[ "${argc__positionals[0]:-}" == "help" ]] && [[ "${#argc__positionals[@]}" -eq 1 ]]; then
            _argc_usage_get_project_info
        fi
        if [[ -z "${LLM_OUTPUT:-}" ]]; then
            export LLM_OUTPUT=/dev/stdout
        fi
        if [[ -z "${LLM_AGENT_VAR_PROJECT_DIR:-}" ]]; then
            export LLM_AGENT_VAR_PROJECT_DIR=.
        fi
    fi
}

_argc_take_args() {
    _argc_take_args_values=()
    _argc_take_args_len=0
    local param="$1" min="$2" max="$3" signs="$4" delimiter="$5"
    if [[ "$min" -eq 0 ]] && [[ "$max" -eq 0 ]]; then
        return
    fi
    local _argc_take_index=$((_argc_index + 1)) _argc_take_value
    if [[ "$_argc_item" == *=* ]]; then
        _argc_take_args_values=("${_argc_item##*=}")
    else
        while [[ $_argc_take_index -lt $_argc_len ]]; do
            _argc_take_value="${argc__args[_argc_take_index]}"
            if _argc_maybe_flag_option "$signs" "$_argc_take_value"; then
                if [[ "${#_argc_take_value}" -gt 1 ]]; then
                    break
                fi
            fi
            _argc_take_args_values+=("$_argc_take_value")
            _argc_take_args_len=$((_argc_take_args_len + 1))
            if [[ "$_argc_take_args_len" -ge "$max" ]]; then
                break
            fi
            _argc_take_index=$((_argc_take_index + 1))
        done
    fi
    if [[ "${#_argc_take_args_values[@]}" -lt "$min" ]]; then
        _argc_die "error: incorrect number of values for \`$param\`"
    fi
    if [[ -n "$delimiter" ]] && [[ "${#_argc_take_args_values[@]}" -gt 0 ]]; then
        local item values arr=()
        for item in "${_argc_take_args_values[@]}"; do
            IFS="$delimiter" read -r -a values <<<"$item"
            arr+=("${values[@]}")
        done
        _argc_take_args_values=("${arr[@]}")
    fi
}

_argc_maybe_flag_option() {
    local signs="$1" arg="$2"
    if [[ -z "$signs" ]]; then
        return 1
    fi
    local cond=false
    if [[ "$signs" == *"+"* ]]; then
        if [[ "$arg" =~ ^\+[^+].* ]]; then
            cond=true
        fi
    elif [[ "$arg" == -* ]]; then
        if (( ${#arg} < 3 )) || [[ ! "$arg" =~ ^---.* ]]; then
            cond=true
        fi
    fi
    if [[ "$cond" == "false" ]]; then
        return 1
    fi
    local value="${arg%%=*}"
    if [[ "$value" =~ [[:space:]] ]]; then
        return 1
    fi
    return 0
}

_argc_die() {
    if [[ $# -eq 0 ]]; then
        cat
    else
        echo "$*" >&2
    fi
    exit 1
}

_argc_run "$@"

# ARGC-BUILD }