#!/system/bin/sh

PACKAGE="${1:-auto}"
SO_PATH="${2:-/data/local/tmp/libomnisight_android.so}"
LOADER="${3:-/data/local/tmp/omnisight_ptrace_loader}"
WAIT_SECS="${OMNISIGHT_WAIT_SECS:-8}"
START_IF_MISSING="${OMNISIGHT_START_IF_MISSING:-1}"
REQUIRE_READY="${OMNISIGHT_REQUIRE_READY:-0}"
RANDOM_SO="${OMNISIGHT_RANDOM_SO:-1}"
CONFIG_PATH="${OMNISIGHT_CONFIG:-/data/local/tmp/omnisight_android.cfg}"

echo "[OmniSight] package=${PACKAGE}"
echo "[OmniSight] so=${SO_PATH}"
echo "[OmniSight] loader=${LOADER}"
echo "[OmniSight] wait=${WAIT_SECS}s start_if_missing=${START_IF_MISSING} require_ready=${REQUIRE_READY}"
echo "[OmniSight] random_so=${RANDOM_SO}"
echo "[OmniSight] external config=${CONFIG_PATH}"

if [ "$(id -u)" != "0" ]; then
    echo "[OmniSight] this script must run as root, use: su -c sh /data/local/tmp/inject_omnisight.sh"
    exit 1
fi

if [ ! -f "${SO_PATH}" ]; then
    echo "[OmniSight] missing so: ${SO_PATH}"
    exit 2
fi

if [ ! -f "${LOADER}" ]; then
    echo "[OmniSight] missing loader: ${LOADER}"
    exit 3
fi

chmod 755 "${LOADER}" "${SO_PATH}" 2>/dev/null
chcon u:object_r:app_data_file:s0:c234,c260,c512,c768 "${SO_PATH}" 2>/dev/null

resolve_package() {
    if [ "${PACKAGE}" != "auto" ]; then
        echo "${PACKAGE}"
        return
    fi

    pkg="$(ps -A 2>/dev/null | awk '/netease\.dwrg/ {print $NF; exit}')"
    if [ -n "${pkg}" ]; then
        echo "${pkg}"
        return
    fi

    for pkg in com.netease.idv.googleplay com.netease.idv_tw.googleplay; do
        if pidof "${pkg}" >/dev/null 2>&1; then
            echo "${pkg}"
            return
        fi
    done

    for pkg in com.netease.idv.googleplay com.netease.idv_tw.googleplay com.netease.dwrg; do
        if cmd package path "${pkg}" >/dev/null 2>&1; then
            echo "${pkg}"
            return
        fi
    done

    echo "com.netease.idv_tw.googleplay"
}

PACKAGE="$(resolve_package)"
echo "[OmniSight] resolved package=${PACKAGE}"

apply_external_config() {
    src="$1"
    app_root="/data/user/0/${PACKAGE}"
    cfg_dir="${app_root}/files/omnisight_android"
    cfg_file="${cfg_dir}/ui.cfg"
    if [ ! -f "${src}" ]; then
        echo "[OmniSight] external config not found, skip"
        return
    fi
    if [ ! -d "${app_root}" ]; then
        echo "[OmniSight] warning: app data dir missing, cannot apply external config"
        return
    fi
    if ! mkdir -p "${cfg_dir}" 2>/dev/null; then
        echo "[OmniSight] warning: cannot create config dir: ${cfg_dir}"
        return
    fi
    # ui.cfg uses last-write-wins key parsing. Appending the small external file
    # lets users override only selected keys without understanding app-private paths.
    {
        echo ""
        echo "# external config applied from ${src}"
        cat "${src}"
    } >> "${cfg_file}" 2>/dev/null || {
        echo "[OmniSight] warning: failed to append external config"
        return
    }
    app_owner="$(stat -c '%u:%g' "${app_root}" 2>/dev/null)"
    if [ -n "${app_owner}" ]; then
        chown "${app_owner}" "${cfg_dir}" "${cfg_file}" 2>/dev/null || true
    fi
    chmod 700 "${cfg_dir}" 2>/dev/null || true
    chmod 600 "${cfg_file}" 2>/dev/null || true
    restorecon -R "${cfg_dir}" 2>/dev/null || true
    echo "[OmniSight] external config applied to ${cfg_file}"
}

apply_external_config "${CONFIG_PATH}"

start_game() {
    MAIN_ACTIVITY="$(cmd package resolve-activity --brief "${PACKAGE}" 2>/dev/null | tail -n 1)"
    case "${MAIN_ACTIVITY}" in
        */*)
            echo "[OmniSight] starting activity: ${MAIN_ACTIVITY}"
            am start -W -n "${MAIN_ACTIVITY}" >/dev/null 2>&1 || true
            ;;
        *)
            echo "[OmniSight] starting package through monkey"
            monkey -p "${PACKAGE}" -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || true
            ;;
    esac
}

first_pid() {
    set -- $(pidof "${PACKAGE}" 2>/dev/null)
    echo "$1"
}

has_python_host() {
    [ -n "$1" ] && [ -r "/proc/$1/maps" ] && grep -Eq "libclient\.so|split_config\.arm64_v8a\.apk" "/proc/$1/maps" 2>/dev/null
}

print_target_diag() {
    pid="$1"
    echo "[OmniSight] diag pid=${pid:-none}"
    if [ -z "${pid}" ] || [ ! -d "/proc/${pid}" ]; then
        echo "[OmniSight] diag: process not found"
        return
    fi
    echo "[OmniSight] diag cmdline=$(tr '\0' ' ' < "/proc/${pid}/cmdline" 2>/dev/null)"
    echo "[OmniSight] diag readable_maps=$([ -r "/proc/${pid}/maps" ] && echo 1 || echo 0)"
    echo "[OmniSight] diag native host candidates:"
    grep -E "libclient\.so|split_config\.arm64_v8a\.apk|libpython|neox|client" "/proc/${pid}/maps" 2>/dev/null | head -n 12 || true
}

random_hex() {
    hex="$(od -An -N4 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n')"
    if [ -n "${hex}" ]; then
        echo "${hex}"
        return
    fi
    echo "$$$(date +%s)"
}

is_loaded_path() {
    pid="$1"
    path="$2"
    [ -n "${pid}" ] && [ -n "${path}" ] && grep -Fq "${path}" "/proc/${pid}/maps" 2>/dev/null
}

loaded_omnisight_path() {
    pid="$1"
    [ -z "${pid}" ] && return
    grep "/omnisight_bridge/.*\.so" "/proc/${pid}/maps" 2>/dev/null |
        head -n 1 |
        sed -n 's/.* \(\/.*omnisight_bridge\/[^ ]*\.so\).*/\1/p'
}

stage_so_for_target() {
    src="$1"
    app_root="/data/user/0/${PACKAGE}"
    files_dir="${app_root}/files"
    stage_dir="${files_dir}/omnisight_bridge"
    if [ "${RANDOM_SO}" = "1" ]; then
        stage_so="${stage_dir}/libomni_$(random_hex).so"
    else
        stage_so="${stage_dir}/$(basename "${src}")"
    fi

    if [ ! -d "${app_root}" ]; then
        echo "[OmniSight] warning: app data dir missing, using original so path" >&2
        echo "${src}"
        return
    fi

    if ! mkdir -p "${stage_dir}" 2>/dev/null; then
        echo "[OmniSight] warning: cannot create stage dir, using original so path" >&2
        echo "${src}"
        return
    fi
    if ! cp -f "${src}" "${stage_so}" 2>/dev/null; then
        echo "[OmniSight] warning: cannot copy so into app dir, using original so path" >&2
        echo "${src}"
        return
    fi

    app_owner="$(stat -c '%u:%g' "${app_root}" 2>/dev/null)"
    if [ -n "${app_owner}" ]; then
        chown "${app_owner}" "${files_dir}" "${stage_dir}" "${stage_so}" 2>/dev/null || true
    fi
    chmod 700 "${files_dir}" 2>/dev/null || true
    chmod 755 "${stage_dir}" "${stage_so}" 2>/dev/null || true
    # Some ROMs hide /data/local/tmp from app linker namespaces. Loading from
    # the package data directory keeps dlopen visible to the target process.
    restorecon -R "${stage_dir}" 2>/dev/null || chcon u:object_r:app_data_file:s0 "${stage_so}" 2>/dev/null || true

    echo "${stage_so}"
}

wait_target_ready() {
    deadline=$(( $(date +%s) + WAIT_SECS ))
    last_pid=""
    stable_count=0

    while [ "$(date +%s)" -le "${deadline}" ]; do
        pid="$(first_pid)"
        if [ -z "${pid}" ]; then
            stable_count=0
            sleep 1
            continue
        fi

        if [ "${pid}" = "${last_pid}" ]; then
            stable_count=$((stable_count + 1))
        else
            stable_count=0
            last_pid="${pid}"
            echo "[OmniSight] observed pid=${pid}" >&2
        fi

        if [ "${stable_count}" -ge 1 ]; then
            if [ "${REQUIRE_READY}" = "1" ] && ! has_python_host "${pid}"; then
                sleep 1
                continue
            fi
            echo "${pid}"
            return 0
        fi
        sleep 1
    done

    return 1
}

PID="$(first_pid)"
if [ -z "${PID}" ]; then
    if [ "${START_IF_MISSING}" = "1" ]; then
        start_game
    else
        echo "[OmniSight] target package is not running: ${PACKAGE}"
        exit 4
    fi
fi

PID="$(wait_target_ready)"
if [ -z "${PID}" ]; then
    echo "[OmniSight] target pid did not stabilize in ${WAIT_SECS}s: ${PACKAGE}"
    print_target_diag "$(first_pid)"
    exit 4
fi

echo "[OmniSight] target pid=${PID}"
if has_python_host "${PID}"; then
    echo "[OmniSight] python host detected"
else
    echo "[OmniSight] warning: python host not detected, injecting anyway"
    print_target_diag "${PID}"
fi

LOADED_SO="$(loaded_omnisight_path "${PID}")"
if [ -n "${LOADED_SO}" ] && [ -f "${LOADED_SO}" ]; then
    INJECT_SO="${LOADED_SO}"
    echo "[OmniSight] reusing loaded so=${INJECT_SO}"
else
    INJECT_SO="$(stage_so_for_target "${SO_PATH}")"
fi
echo "[OmniSight] staged so=${INJECT_SO}"
if is_loaded_path "${PID}" "${INJECT_SO}"; then
    echo "[OmniSight] exact so already mapped, calling start on existing handle"
fi
"${LOADER}" "${PID}" "${INJECT_SO}"
RC="$?"
echo "[OmniSight] loader rc=${RC}"
exit "${RC}"
