//! No-op stubs for stdlib functions that may be referenced by compiled code
//! but are only available when perry-stdlib is linked.
//!
//! These stubs allow binaries to link in runtime-only mode even when the source
//! code references stdlib features (e.g., WebSocket). The stubs return safe
//! default values (null pointers, 0.0, etc.) so the program links and runs,
//! though the stdlib features will be non-functional.
//!
//! When perry-stdlib IS linked, its real implementations are used instead
//! (the linker picks stdlib over runtime since only one is ever linked).
//!
//! Each stub funnels through `crate::stub_diag::perry_stub_warn` so a
//! call in runtime-only mode prints `[perry] warning: ...` once per
//! symbol per process — see issue #464 and `src/stub_diag.rs`.

use crate::promise::Promise;
use crate::string::StringHeader;
use crate::stub_diag::perry_stub_warn;
use std::ptr;

const WS_REASON: &str =
    "WebSocket symbol from perry-stdlib not linked into this binary (runtime-only build)";
const READLINE_REASON: &str =
    "readline symbol from perry-stdlib not linked into this binary (runtime-only build)";
const STDLIB_DISPATCH_REASON: &str =
    "stdlib dispatch symbol from perry-stdlib not linked into this binary (runtime-only build)";

// === WebSocket stubs ===
// On iOS, perry-stdlib provides the real WebSocket implementation (using
// NSURLSessionWebSocketTask). On Android, perry-ui-android provides a real
// WebSocket implementation using tungstenite+rustls. These stubs must NOT
// be compiled for either platform, otherwise the real implementations will
// be shadowed by the no-op stubs.
#[cfg(not(any(target_os = "ios", target_os = "android")))]
mod ws_stubs {
    use super::{perry_stub_warn, WS_REASON};
    use crate::promise::Promise;
    use crate::string::StringHeader;
    use std::ptr;

    #[no_mangle]
    pub extern "C" fn js_ws_connect(_url_ptr: *const StringHeader) -> *mut Promise {
        perry_stub_warn("js_ws_connect", WS_REASON, None);
        ptr::null_mut()
    }

    #[no_mangle]
    pub extern "C" fn js_ws_connect_start(_url_nanboxed: f64) -> f64 {
        perry_stub_warn("js_ws_connect_start", WS_REASON, None);
        0.0
    }

    #[no_mangle]
    pub extern "C" fn js_ws_send(_handle: i64, _message_ptr: *const StringHeader) {
        perry_stub_warn("js_ws_send", WS_REASON, None);
    }

    #[no_mangle]
    pub extern "C" fn js_ws_close(_handle: i64) {
        perry_stub_warn("js_ws_close", WS_REASON, None);
    }

    #[no_mangle]
    pub extern "C" fn js_ws_is_open(_handle: i64) -> f64 {
        perry_stub_warn("js_ws_is_open", WS_REASON, None);
        0.0
    }

    #[no_mangle]
    pub extern "C" fn js_ws_message_count(_handle: i64) -> f64 {
        perry_stub_warn("js_ws_message_count", WS_REASON, None);
        0.0
    }

    #[no_mangle]
    pub extern "C" fn js_ws_receive(_handle: i64) -> *mut StringHeader {
        perry_stub_warn("js_ws_receive", WS_REASON, None);
        ptr::null_mut()
    }

    #[no_mangle]
    pub extern "C" fn js_ws_wait_for_message(_handle: i64, _timeout_ms: f64) -> *mut Promise {
        perry_stub_warn("js_ws_wait_for_message", WS_REASON, None);
        ptr::null_mut()
    }

    #[no_mangle]
    pub extern "C" fn js_ws_on(
        _handle: i64,
        _event_name_ptr: *const StringHeader,
        _callback_ptr: i64,
    ) -> i64 {
        perry_stub_warn("js_ws_on", WS_REASON, None);
        0
    }

    #[no_mangle]
    pub extern "C" fn js_ws_server_new(_opts_f64: f64) -> i64 {
        perry_stub_warn("js_ws_server_new", WS_REASON, None);
        0
    }

    #[no_mangle]
    pub extern "C" fn js_ws_server_close(_handle: i64) {
        perry_stub_warn("js_ws_server_close", WS_REASON, None);
    }

    #[no_mangle]
    pub extern "C" fn js_ws_process_pending() -> i32 {
        // Hot-loop: drained every event-loop tick, so a first-call
        // warning would be misleading even if the symbol IS the no-op
        // stub variant — skip the warning here.
        0
    }
}

// === Stdlib dispatch stubs ===
// On Android, perry-ui-android provides a real js_stdlib_process_pending
// that processes WebSocket promise resolves.
#[cfg(not(target_os = "android"))]
#[no_mangle]
pub extern "C" fn js_stdlib_process_pending() -> i32 {
    // Hot-loop drain — see js_ws_process_pending above. Silent stub.
    0
}

#[cfg(not(target_os = "android"))]
#[no_mangle]
pub extern "C" fn js_stdlib_init_dispatch() {
    perry_stub_warn("js_stdlib_init_dispatch", STDLIB_DISPATCH_REASON, None);
}

// === readline (#347) stubs ===
// `process.stdin.setRawMode(...)` and `process.stdin.on(...)` always
// codegen direct extern calls to these symbols, even when the user's
// program doesn't `import 'readline'` and stdlib isn't linked. The
// stubs are no-ops so the program links cleanly; when stdlib IS
// linked, the real implementations from `perry-stdlib::readline`
// override these (linker picks stdlib over runtime). Android stdlib
// stubs cover those targets independently.
#[cfg(not(target_os = "android"))]
#[no_mangle]
pub extern "C" fn js_readline_set_raw_mode(_enabled: f64) -> f64 {
    perry_stub_warn("js_readline_set_raw_mode", READLINE_REASON, Some("#347"));
    f64::from_bits(0x7FFC_0000_0000_0001) // TAG_UNDEFINED
}
#[cfg(not(target_os = "android"))]
#[no_mangle]
pub extern "C" fn js_readline_stdin_on(_event_ptr: i64, _callback: i64) -> f64 {
    perry_stub_warn("js_readline_stdin_on", READLINE_REASON, Some("#347"));
    f64::from_bits(0x7FFC_0000_0000_0001)
}
