#!/system/bin/sh

MODDIR=${0%/*}
MODULE_TAG='cloudphone-magisk'
BIN_PATH="$MODDIR/bin/cloudphone_daemon"
CONFIG_FILE="$MODDIR/cloudphone.conf"
RUNTIME_DIR_DEFAULT='/data/local/cloudphone'
DISPLAY_HELPER_RUNTIME_DIR_DEFAULT='/data/local/tmp/cloudphone'
ACCOUNT_CLEANUP_MODULE_JAR_PATH="$MODDIR/bin/account-cleanup.jar"
DISPLAY_HELPER_MODULE_JAR_PATH="$MODDIR/bin/cloudphone-display-helper.jar"
DISPLAY_HELPER_MODULE_SO_PATH="$MODDIR/bin/libcloudphone_display_helper.so"
STATUSBAR_HELPER_MODULE_JAR_PATH="$MODDIR/bin/cloudphone-statusbar-helper.jar"
ANXINCHONG_MODULE_APK_PATH="$MODDIR/bin/anxinchong-lsposed.apk"
ANXINCHONG_INSTALL_APK_PATH="/data/local/tmp/anxinchong-lsposed.apk"
ANXINCHONG_PACKAGE_NAME="com.anxinchong.lsposed"
MANAGER_MODULE_APK_PATH="$MODDIR/bin/cloudphone-manager.apk"
MANAGER_PACKAGE_NAME="com.cloudphone.daemon.manager"

trim_cr() {
    printf '%s' "$1" | tr -d '\r'
}

getprop_value() {
    trim_cr "$(/system/bin/getprop "$1" 2>/dev/null)"
}

normalize_bool() {
    case "$1" in
        1|true|TRUE|yes|YES|on|ON) return 0 ;;
        *) return 1 ;;
    esac
}

load_config() {
    if [ -f "$CONFIG_FILE" ]; then
        # shellcheck disable=SC1090
        . "$CONFIG_FILE"
    fi

    : "${WS_URL:=}"
    : "${DEVICE_ID:=AUTO}"
    : "${RUNTIME_DIR:=$RUNTIME_DIR_DEFAULT}"
    : "${BOOT_DELAY_SEC:=10}"
    : "${SOCK_FILE:=$RUNTIME_DIR/cloudphone.sock}"
    : "${PID_FILE:=$RUNTIME_DIR/cloudphone.pid}"
    : "${RUNTIME_LOG_DIR:=$RUNTIME_DIR/log}"
    : "${DAEMON_LOG_FILE:=$RUNTIME_LOG_DIR/cloudphone.log}"
    : "${SERVICE_LOG_FILE:=$RUNTIME_DIR/magisk-service.log}"
    : "${HEARTBEAT_SEC:=60}"
    : "${WS_CA_FILE:=}"
    : "${WS_NO_HOST_VERIFY:=false}"
    : "${WS_DUMP:=false}"
    : "${WS_BINARY_PREVIEW_BYTES:=}"
    : "${FRAME_WIRE_PROTOCOL:=legacy}"
    : "${RECORDING_CODEC:=h264}"
    : "${RECORDING_PROFILE:=balanced}"
    : "${RECORDING_BITRATE_KBPS:=}"
    : "${RECORDING_SIZE:=}"
    : "${RECORDING_TIME_LIMIT_SEC:=}"
    : "${DISABLE_SELINUX_FOR_TESTING:=false}"
    : "${DISPLAY_HELPER_RUNTIME_DIR:=$DISPLAY_HELPER_RUNTIME_DIR_DEFAULT}"
    : "${ACCOUNT_CLEANUP_JAR_FILE:=$DISPLAY_HELPER_RUNTIME_DIR/account-cleanup.jar}"
    : "${DISPLAY_HELPER_JAR_FILE:=$DISPLAY_HELPER_RUNTIME_DIR/cloudphone-display-helper.jar}"
    : "${DISPLAY_HELPER_SO_FILE:=$DISPLAY_HELPER_RUNTIME_DIR/libcloudphone_display_helper.so}"
    : "${STATUSBAR_HELPER_JAR_FILE:=$DISPLAY_HELPER_RUNTIME_DIR/cloudphone-statusbar-helper.jar}"
}

ensure_service_log_ready() {
    load_config
    mkdir -p "$RUNTIME_DIR" 2>/dev/null || true
    touch "$SERVICE_LOG_FILE" 2>/dev/null || true
}

log_note() {
    ensure_service_log_ready
    line="[$MODULE_TAG] $*"
    printf '%s\n' "$line"
    printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo unknown-time)" "$line" >>"$SERVICE_LOG_FILE" 2>/dev/null || true
    log -t "$MODULE_TAG" "$*" 2>/dev/null || true
}

resolve_device_id() {
    if [ -n "$DEVICE_ID" ] && [ "$DEVICE_ID" != 'AUTO' ]; then
        printf '%s' "$DEVICE_ID"
        return 0
    fi

    value=$(getprop_value ro.serialno)
    if [ -n "$value" ]; then
        printf '%s' "$value"
        return 0
    fi

    value=$(getprop_value ro.boot.serialno)
    if [ -n "$value" ]; then
        printf '%s' "$value"
        return 0
    fi

    value=$(getprop_value ro.product.vendor.device)
    if [ -n "$value" ]; then
        printf '%s' "$value"
        return 0
    fi

    printf '%s' 'android-device'
}

wait_for_boot_completed() {
    while [ "$(getprop_value sys.boot_completed)" != '1' ]; do
        sleep 5
    done

    if [ "${BOOT_DELAY_SEC:-0}" -gt 0 ] 2>/dev/null; then
        sleep "$BOOT_DELAY_SEC"
    fi
}

ensure_runtime_dir() {
    mkdir -p "$RUNTIME_DIR" || return 1
    mkdir -p "$RUNTIME_LOG_DIR" || return 1

    # Manager APK 通过 Android LocalSocket 的 FILESYSTEM namespace 连接
    # /data/local/cloudphone/cloudphone.sock。除了 sock_file 自身 0666，
    # 普通 APK 还必须能穿透父目录；否则 connect 会直接返回 Permission denied，
    # 且不一定产生 avc denied。这里仅补 search/execute 位，不开放写权限。
    chmod 0711 /data/local 2>/dev/null || true
    chmod 0755 "$RUNTIME_DIR" 2>/dev/null || true
    chmod 0755 "$RUNTIME_LOG_DIR" 2>/dev/null || true
    return 0
}

ensure_display_helper_runtime_dir() {
    mkdir -p "$DISPLAY_HELPER_RUNTIME_DIR" || return 1
    chmod 0755 "$DISPLAY_HELPER_RUNTIME_DIR" 2>/dev/null || true
    return 0
}

sync_display_helper_runtime_files() {
    load_config

    ensure_display_helper_runtime_dir || {
        log_note "failed to create display helper runtime dir: $DISPLAY_HELPER_RUNTIME_DIR"
        return 1
    }

    if [ ! -r "$DISPLAY_HELPER_MODULE_JAR_PATH" ]; then
        log_note "missing display helper jar in module: $DISPLAY_HELPER_MODULE_JAR_PATH"
        return 1
    fi
    if [ ! -r "$ACCOUNT_CLEANUP_MODULE_JAR_PATH" ]; then
        log_note "missing account cleanup jar in module: $ACCOUNT_CLEANUP_MODULE_JAR_PATH"
        return 1
    fi
    if [ ! -r "$DISPLAY_HELPER_MODULE_SO_PATH" ]; then
        log_note "missing display helper so in module: $DISPLAY_HELPER_MODULE_SO_PATH"
        return 1
    fi
    if [ ! -r "$STATUSBAR_HELPER_MODULE_JAR_PATH" ]; then
        log_note "missing statusbar helper jar in module: $STATUSBAR_HELPER_MODULE_JAR_PATH"
        return 1
    fi

    cp -f "$ACCOUNT_CLEANUP_MODULE_JAR_PATH" "$ACCOUNT_CLEANUP_JAR_FILE" || return 1
    cp -f "$DISPLAY_HELPER_MODULE_JAR_PATH" "$DISPLAY_HELPER_JAR_FILE" || return 1
    cp -f "$DISPLAY_HELPER_MODULE_SO_PATH" "$DISPLAY_HELPER_SO_FILE" || return 1
    cp -f "$STATUSBAR_HELPER_MODULE_JAR_PATH" "$STATUSBAR_HELPER_JAR_FILE" || return 1
    chmod 0644 "$ACCOUNT_CLEANUP_JAR_FILE" 2>/dev/null || true
    chmod 0644 "$DISPLAY_HELPER_JAR_FILE" 2>/dev/null || true
    chmod 0755 "$DISPLAY_HELPER_SO_FILE" 2>/dev/null || true
    chmod 0644 "$STATUSBAR_HELPER_JAR_FILE" 2>/dev/null || true
    return 0
}

install_manager_apk() {
    if [ ! -r "$MANAGER_MODULE_APK_PATH" ]; then
        log_note "missing Manager apk in module: $MANAGER_MODULE_APK_PATH"
        return 1
    fi

    pm install -r "$MANAGER_MODULE_APK_PATH" >>"$SERVICE_LOG_FILE" 2>&1 || {
        log_note "failed to install Manager apk via pm install -r $MANAGER_MODULE_APK_PATH"
        return 1
    }
    log_note "Manager apk installed: $MANAGER_PACKAGE_NAME"
    return 0
}

uninstall_package_if_present() {
    package_name="$1"
    display_name="$2"

    if [ -z "$package_name" ]; then
        return 0
    fi

    if pm path "$package_name" >/dev/null 2>&1; then
        log_note "uninstalling $display_name package: $package_name"
        am force-stop "$package_name" >/dev/null 2>&1 || true
        pm uninstall "$package_name" >>"$SERVICE_LOG_FILE" 2>&1 || \
            log_note "failed to uninstall $display_name package: $package_name"
    else
        log_note "$display_name package not installed: $package_name"
    fi
}

cleanup_runtime_artifacts() {
    load_config
    log_note "cleanup_runtime_artifacts invoked"

    rm -f "$PID_FILE" "$SOCK_FILE" \
        "$RUNTIME_DIR/manager-helper.pid" \
        "$ANXINCHONG_INSTALL_APK_PATH" \
        "$ACCOUNT_CLEANUP_JAR_FILE" \
        "$DISPLAY_HELPER_JAR_FILE" \
        "$DISPLAY_HELPER_SO_FILE" \
        "$STATUSBAR_HELPER_JAR_FILE" \
        2>/dev/null || true

    # 保留 log 目录便于卸载后排查；只清理运行控制文件和临时 helper 文件。
    rm -f "$RUNTIME_DIR/cloudphone.pid" "$RUNTIME_DIR/cloudphone.sock" 2>/dev/null || true
}

install_anxinchong_apk() {
    if [ ! -r "$ANXINCHONG_MODULE_APK_PATH" ]; then
        log_note "missing AnXinChong apk in module: $ANXINCHONG_MODULE_APK_PATH"
        return 1
    fi

    cp -f "$ANXINCHONG_MODULE_APK_PATH" "$ANXINCHONG_INSTALL_APK_PATH" || return 1
    chmod 0644 "$ANXINCHONG_INSTALL_APK_PATH" 2>/dev/null || true

    pm install -r -d "$ANXINCHONG_INSTALL_APK_PATH" >>"$SERVICE_LOG_FILE" 2>&1 || {
        log_note "failed to install AnXinChong apk via pm install -r -d $ANXINCHONG_INSTALL_APK_PATH"
        return 1
    }
    log_note "AnXinChong apk installed: $ANXINCHONG_PACKAGE_NAME"
    return 0
}

stop_daemon() {
    load_config
    log_note "stop_daemon invoked"

    if [ -f "$PID_FILE" ]; then
        pid=$(cat "$PID_FILE" 2>/dev/null)
        if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
            log_note "stopping pid $pid"
            kill "$pid" 2>/dev/null || true
            sleep 1
            if kill -0 "$pid" 2>/dev/null; then
                kill -9 "$pid" 2>/dev/null || true
            fi
        fi
    fi

    if command -v pkill >/dev/null 2>&1; then
        pkill -f "$BIN_PATH" 2>/dev/null || true
    fi

    rm -f "$PID_FILE" "$SOCK_FILE"
}

start_daemon() {
    load_config
    log_note "start_daemon invoked"

    if [ ! -x "$BIN_PATH" ]; then
        log_note "missing binary: $BIN_PATH"
        return 1
    fi

    if [ -z "$WS_URL" ] || [ "$WS_URL" = 'wss://example.com/device' ]; then
        log_note "WS_URL is not configured in $CONFIG_FILE"
        return 2
    fi

    ensure_runtime_dir || {
        log_note "failed to create runtime dir: $RUNTIME_DIR"
        return 3
    }
    sync_display_helper_runtime_files || {
        log_note "failed to sync display helper runtime files to $DISPLAY_HELPER_RUNTIME_DIR"
        return 4
    }
    if ! install_anxinchong_apk; then
        # AnXinChong 是业务 Hook APK，安装失败不应阻断 daemon 管理接口启动。
        # 典型场景：设备上已有更高 versionCode，pm install 未允许 downgrade 或 APK 签名/版本冲突。
        log_note "failed to install AnXinChong apk, continue starting daemon"
    fi

    stop_daemon

    device_id=$(resolve_device_id)

    {
        printf '[%s] launching cloudphone_daemon\n' "$MODULE_TAG"
        printf '[%s] ws=%s device_id=%s runtime_dir=%s\n' "$MODULE_TAG" "$WS_URL" "$device_id" "$RUNTIME_DIR"
    } >>"$SERVICE_LOG_FILE"

    set -- "$BIN_PATH" \
        --daemon \
        --pid-file "$PID_FILE" \
        --sock "$SOCK_FILE" \
        --log-file "$DAEMON_LOG_FILE" \
        --ws-url "$WS_URL" \
        --device-id "$device_id"

    if [ -n "$HEARTBEAT_SEC" ]; then
        set -- "$@" --heartbeat-sec "$HEARTBEAT_SEC"
    fi
    if [ -n "$WS_CA_FILE" ]; then
        set -- "$@" --ws-ca-file "$WS_CA_FILE"
    fi
    if normalize_bool "$WS_NO_HOST_VERIFY"; then
        set -- "$@" --ws-no-host-verify
    fi
    if normalize_bool "$WS_DUMP"; then
        set -- "$@" --ws-dump
    fi
    if [ -n "$WS_BINARY_PREVIEW_BYTES" ]; then
        set -- "$@" --ws-binary-preview-bytes "$WS_BINARY_PREVIEW_BYTES"
    fi
    if [ -n "$FRAME_WIRE_PROTOCOL" ]; then
        set -- "$@" --frame-wire-protocol "$FRAME_WIRE_PROTOCOL"
    fi
    if [ -n "$RECORDING_CODEC" ]; then
        set -- "$@" --recording-codec "$RECORDING_CODEC"
    fi
    if [ -n "$RECORDING_PROFILE" ]; then
        set -- "$@" --recording-profile "$RECORDING_PROFILE"
    fi
    if [ -n "$RECORDING_BITRATE_KBPS" ]; then
        set -- "$@" --recording-bitrate-kbps "$RECORDING_BITRATE_KBPS"
    fi
    if [ -n "$RECORDING_SIZE" ]; then
        set -- "$@" --recording-size "$RECORDING_SIZE"
    fi
    if [ -n "$RECORDING_TIME_LIMIT_SEC" ]; then
        set -- "$@" --recording-time-limit-sec "$RECORDING_TIME_LIMIT_SEC"
    fi
    if normalize_bool "$DISABLE_SELINUX_FOR_TESTING"; then
        set -- "$@" --disable-selinux-for-testing
    fi

    "$@" >>"$SERVICE_LOG_FILE" 2>&1
    # daemon bind 后理论上会 chmod 0666；这里作为模块脚本层兜底，兼容旧二进制或异常残留。
    chmod 0666 "$SOCK_FILE" 2>/dev/null || true
}

status_daemon() {
    load_config

    if [ -f "$PID_FILE" ]; then
        pid=$(cat "$PID_FILE" 2>/dev/null)
        if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
            log_note "running pid=$pid sock=$SOCK_FILE log=$DAEMON_LOG_FILE"
            return 0
        fi
    fi

    log_note "not running"
    return 1
}
