//! The stdlib stub registration table mapping OTP `module:function/arity`
//! names to native BIF implementations.

use crate::atom::{Atom, AtomTable};
use crate::native::{BifRegistryImpl, Capability, NativeFn, NativeRegistrationError};
use crate::scheduler::dirty::DirtySchedulerKind;

use super::bitwise_bifs::{bif_band, bif_bnot, bif_bor, bif_bsl, bif_bsr, bif_bxor};
use super::collection_bifs::{
    bif_lists_reverse, bif_maps_from_list, bif_maps_map, bif_maps_merge, bif_maps_remove,
    bif_timer_sleep,
};
use super::encoding_bifs::{
    bif_base64_decode, bif_base64_encode, bif_binary_decode_hex, bif_binary_encode_hex,
};
use super::gleam_stdlib_ffi2::{bif_print, bif_print_error, bif_println, bif_println_error};
use super::io_bifs::{
    bif_io_format_2, bif_io_format_3, bif_io_get_line_1, bif_io_lib_format_2, bif_io_put_chars_1,
    bif_io_put_chars_2, bif_io_setopts_2,
};
use super::json_bifs::{
    bif_json_decode, bif_json_encode, bif_json_encode_binary, bif_json_encode_float,
    bif_json_encode_integer,
};
use super::lists_bifs::{
    bif_lists_append_1, bif_lists_append_2, bif_lists_flatten, bif_lists_join, bif_lists_keydelete,
    bif_lists_keyfind, bif_lists_keysort, bif_lists_keystore, bif_lists_last, bif_lists_member,
    bif_lists_nth, bif_lists_reverse_2, bif_lists_seq, bif_lists_sort, bif_lists_unzip,
    bif_lists_zip,
};
use super::lists_hof_bifs::{
    bif_lists_filter, bif_lists_filtermap, bif_lists_foreach, bif_lists_map,
};
use super::maps_bifs::{
    bif_maps_filter, bif_maps_find, bif_maps_fold, bif_maps_keys, bif_maps_merge_with,
    bif_maps_put, bif_maps_to_list, bif_maps_update_with, bif_maps_values, bif_maps_with,
    bif_maps_without,
};
use super::math_bifs::{bif_ceil, bif_exp, bif_floor, bif_log, bif_pow};
use super::misc_bifs::{
    bif_binary_part, bif_characters_to_binary, bif_characters_to_list, bif_debug_options,
    bif_fun_info, bif_fwrite_g, bif_init_stop, bif_logger_warning, bif_rand_uniform,
};
use super::string_bifs::{
    bif_equal as bif_string_equal, bif_find as bif_string_find,
    bif_is_empty as bif_string_is_empty, bif_length as bif_string_length,
    bif_lowercase as bif_string_lowercase, bif_next_grapheme as bif_string_next_grapheme,
    bif_pad as bif_string_pad, bif_replace as bif_string_replace4,
    bif_reverse as bif_string_reverse, bif_slice as bif_string_slice,
    bif_split as bif_string_split, bif_trim as bif_string_trim,
    bif_uppercase as bif_string_uppercase,
};
use super::type_conversion_bifs::{
    bif_binary_to_float, bif_binary_to_integer, bif_binary_to_integer_radix, bif_float,
    bif_integer_to_binary, bif_integer_to_binary_radix, bif_integer_to_list, bif_iolist_to_binary,
    bif_list_to_bitstring, bif_list_to_tuple, bif_tuple_to_list,
};
use super::uri_bifs::{
    bif_maps_get_2, bif_maps_get_3, bif_uri_string_dissect_query, bif_uri_string_parse,
};

/// A stub BIF entry: (module_name, function_name, arity, capability, dirty_kind, implementation).
type StubBif = (
    &'static str,
    &'static str,
    u8,
    Capability,
    Option<DirtySchedulerKind>,
    NativeFn,
);

const STDLIB_STUBS: &[StubBif] = &[
    (
        "erlang",
        "binary_to_float",
        1,
        Capability::Pure,
        None,
        bif_binary_to_float,
    ),
    (
        "erlang",
        "binary_to_integer",
        1,
        Capability::Pure,
        None,
        bif_binary_to_integer,
    ),
    (
        "erlang",
        "binary_to_integer",
        2,
        Capability::Pure,
        None,
        bif_binary_to_integer_radix,
    ),
    ("erlang", "float", 1, Capability::Pure, None, bif_float),
    (
        "erlang",
        "integer_to_binary",
        1,
        Capability::Pure,
        None,
        bif_integer_to_binary,
    ),
    (
        "erlang",
        "integer_to_binary",
        2,
        Capability::Pure,
        None,
        bif_integer_to_binary_radix,
    ),
    (
        "erlang",
        "integer_to_list",
        1,
        Capability::Pure,
        None,
        bif_integer_to_list,
    ),
    (
        "erlang",
        "iolist_to_binary",
        1,
        Capability::Pure,
        None,
        bif_iolist_to_binary,
    ),
    (
        "erlang",
        "list_to_bitstring",
        1,
        Capability::Pure,
        None,
        bif_list_to_bitstring,
    ),
    (
        "erlang",
        "list_to_tuple",
        1,
        Capability::Pure,
        None,
        bif_list_to_tuple,
    ),
    (
        "erlang",
        "tuple_to_list",
        1,
        Capability::Pure,
        None,
        bif_tuple_to_list,
    ),
    ("erlang", "band", 2, Capability::Pure, None, bif_band),
    ("erlang", "bnot", 1, Capability::Pure, None, bif_bnot),
    ("erlang", "bor", 2, Capability::Pure, None, bif_bor),
    ("erlang", "bsl", 2, Capability::Pure, None, bif_bsl),
    ("erlang", "bsr", 2, Capability::Pure, None, bif_bsr),
    ("erlang", "bxor", 2, Capability::Pure, None, bif_bxor),
    ("math", "ceil", 1, Capability::Pure, None, bif_ceil),
    ("math", "floor", 1, Capability::Pure, None, bif_floor),
    ("math", "exp", 1, Capability::Pure, None, bif_exp),
    ("math", "log", 1, Capability::Pure, None, bif_log),
    ("math", "pow", 2, Capability::Pure, None, bif_pow),
    (
        "rand",
        "uniform",
        0,
        Capability::Entropy,
        None,
        bif_rand_uniform,
    ),
    (
        "logger",
        "warning",
        2,
        Capability::ExternalIo,
        None,
        bif_logger_warning,
    ),
    (
        "unicode",
        "characters_to_list",
        1,
        Capability::Pure,
        None,
        bif_characters_to_list,
    ),
    (
        "unicode",
        "characters_to_binary",
        1,
        Capability::Pure,
        None,
        bif_characters_to_binary,
    ),
    (
        "sys",
        "debug_options",
        1,
        Capability::Pure,
        None,
        bif_debug_options,
    ),
    (
        "gleam_stdlib",
        "print",
        1,
        Capability::ExternalIo,
        None,
        bif_print,
    ),
    (
        "gleam_stdlib",
        "print_error",
        1,
        Capability::ExternalIo,
        None,
        bif_print_error,
    ),
    (
        "gleam_stdlib",
        "println",
        1,
        Capability::ExternalIo,
        None,
        bif_println,
    ),
    (
        "gleam_stdlib",
        "println_error",
        1,
        Capability::ExternalIo,
        None,
        bif_println_error,
    ),
    (
        "uri_string",
        "parse",
        1,
        Capability::Pure,
        None,
        bif_uri_string_parse,
    ),
    (
        "uri_string",
        "dissect_query",
        1,
        Capability::Pure,
        None,
        bif_uri_string_dissect_query,
    ),
    (
        "string",
        "length",
        1,
        Capability::Pure,
        None,
        bif_string_length,
    ),
    (
        "string",
        "reverse",
        1,
        Capability::Pure,
        None,
        bif_string_reverse,
    ),
    (
        "string",
        "lowercase",
        1,
        Capability::Pure,
        None,
        bif_string_lowercase,
    ),
    (
        "string",
        "uppercase",
        1,
        Capability::Pure,
        None,
        bif_string_uppercase,
    ),
    ("string", "trim", 2, Capability::Pure, None, bif_string_trim),
    (
        "string",
        "split",
        3,
        Capability::Pure,
        None,
        bif_string_split,
    ),
    ("string", "find", 2, Capability::Pure, None, bif_string_find),
    (
        "string",
        "next_grapheme",
        1,
        Capability::Pure,
        None,
        bif_string_next_grapheme,
    ),
    ("string", "pad", 4, Capability::Pure, None, bif_string_pad),
    (
        "string",
        "replace",
        4,
        Capability::Pure,
        None,
        bif_string_replace4,
    ),
    (
        "string",
        "slice",
        3,
        Capability::Pure,
        None,
        bif_string_slice,
    ),
    (
        "string",
        "equal",
        2,
        Capability::Pure,
        None,
        bif_string_equal,
    ),
    (
        "string",
        "is_empty",
        1,
        Capability::Pure,
        None,
        bif_string_is_empty,
    ),
    ("binary", "part", 3, Capability::Pure, None, bif_binary_part),
    (
        "binary",
        "encode_hex",
        1,
        Capability::Pure,
        None,
        bif_binary_encode_hex,
    ),
    (
        "binary",
        "decode_hex",
        1,
        Capability::Pure,
        None,
        bif_binary_decode_hex,
    ),
    (
        "base64",
        "encode",
        2,
        Capability::Pure,
        None,
        bif_base64_encode,
    ),
    (
        "base64",
        "decode",
        1,
        Capability::Pure,
        None,
        bif_base64_decode,
    ),
    (
        "io",
        "put_chars",
        1,
        Capability::ExternalIo,
        None,
        bif_io_put_chars_1,
    ),
    (
        "io",
        "put_chars",
        2,
        Capability::ExternalIo,
        None,
        bif_io_put_chars_2,
    ),
    (
        "io",
        "format",
        2,
        Capability::ExternalIo,
        None,
        bif_io_format_2,
    ),
    (
        "io",
        "format",
        3,
        Capability::ExternalIo,
        None,
        bif_io_format_3,
    ),
    (
        "io",
        "get_line",
        1,
        Capability::ExternalIo,
        None,
        bif_io_get_line_1,
    ),
    (
        "io",
        "setopts",
        2,
        Capability::ExternalIo,
        None,
        bif_io_setopts_2,
    ),
    (
        "io_lib",
        "format",
        2,
        Capability::ExternalIo,
        None,
        bif_io_lib_format_2,
    ),
    (
        "init",
        "stop",
        1,
        Capability::ExternalIo,
        None,
        bif_init_stop,
    ),
    // Non-higher-order collection BIFs (B-028a):
    (
        "maps",
        "from_list",
        1,
        Capability::Pure,
        None,
        bif_maps_from_list,
    ),
    ("maps", "merge", 2, Capability::Pure, None, bif_maps_merge),
    ("maps", "remove", 2, Capability::Pure, None, bif_maps_remove),
    // maps:map/2 uses continuation trampolines for closure calls.
    ("maps", "map", 2, Capability::Pure, None, bif_maps_map),
    ("maps", "put", 3, Capability::Pure, None, bif_maps_put),
    ("maps", "find", 2, Capability::Pure, None, bif_maps_find),
    ("maps", "get", 2, Capability::Pure, None, bif_maps_get_2),
    ("maps", "get", 3, Capability::Pure, None, bif_maps_get_3),
    ("maps", "keys", 1, Capability::Pure, None, bif_maps_keys),
    ("maps", "values", 1, Capability::Pure, None, bif_maps_values),
    (
        "maps",
        "to_list",
        1,
        Capability::Pure,
        None,
        bif_maps_to_list,
    ),
    ("maps", "fold", 3, Capability::Pure, None, bif_maps_fold),
    ("maps", "filter", 2, Capability::Pure, None, bif_maps_filter),
    (
        "maps",
        "merge_with",
        3,
        Capability::Pure,
        None,
        bif_maps_merge_with,
    ),
    (
        "maps",
        "update_with",
        4,
        Capability::Pure,
        None,
        bif_maps_update_with,
    ),
    ("maps", "with", 2, Capability::Pure, None, bif_maps_with),
    (
        "maps",
        "without",
        2,
        Capability::Pure,
        None,
        bif_maps_without,
    ),
    (
        "lists",
        "reverse",
        1,
        Capability::Pure,
        None,
        bif_lists_reverse,
    ),
    (
        "lists",
        "append",
        1,
        Capability::Pure,
        None,
        bif_lists_append_1,
    ),
    (
        "lists",
        "append",
        2,
        Capability::Pure,
        None,
        bif_lists_append_2,
    ),
    ("lists", "join", 2, Capability::Pure, None, bif_lists_join),
    ("lists", "nth", 2, Capability::Pure, None, bif_lists_nth),
    (
        "lists",
        "member",
        2,
        Capability::Pure,
        None,
        bif_lists_member,
    ),
    (
        "lists",
        "keyfind",
        3,
        Capability::Pure,
        None,
        bif_lists_keyfind,
    ),
    ("lists", "last", 1, Capability::Pure, None, bif_lists_last),
    ("lists", "sort", 1, Capability::Pure, None, bif_lists_sort),
    (
        "lists",
        "flatten",
        1,
        Capability::Pure,
        None,
        bif_lists_flatten,
    ),
    ("lists", "zip", 2, Capability::Pure, None, bif_lists_zip),
    ("lists", "unzip", 1, Capability::Pure, None, bif_lists_unzip),
    (
        "lists",
        "filter",
        2,
        Capability::Pure,
        None,
        bif_lists_filter,
    ),
    (
        "lists",
        "filtermap",
        2,
        Capability::Pure,
        None,
        bif_lists_filtermap,
    ),
    ("lists", "map", 2, Capability::Pure, None, bif_lists_map),
    (
        "lists",
        "reverse",
        2,
        Capability::Pure,
        None,
        bif_lists_reverse_2,
    ),
    ("lists", "seq", 2, Capability::Pure, None, bif_lists_seq),
    (
        "lists",
        "keystore",
        4,
        Capability::Pure,
        None,
        bif_lists_keystore,
    ),
    (
        "lists",
        "keysort",
        2,
        Capability::Pure,
        None,
        bif_lists_keysort,
    ),
    (
        "lists",
        "keydelete",
        3,
        Capability::Pure,
        None,
        bif_lists_keydelete,
    ),
    (
        "lists",
        "foreach",
        2,
        Capability::Pure,
        None,
        bif_lists_foreach,
    ),
    (
        "timer",
        "sleep",
        1,
        Capability::Clock,
        Some(DirtySchedulerKind::Io),
        bif_timer_sleep,
    ),
    ("json", "decode", 1, Capability::Pure, None, bif_json_decode),
    ("json", "encode", 1, Capability::Pure, None, bif_json_encode),
    (
        "json",
        "encode_integer",
        1,
        Capability::Pure,
        None,
        bif_json_encode_integer,
    ),
    (
        "json",
        "encode_float",
        1,
        Capability::Pure,
        None,
        bif_json_encode_float,
    ),
    (
        "json",
        "encode_binary",
        1,
        Capability::Pure,
        None,
        bif_json_encode_binary,
    ),
    (
        "erlang",
        "fun_info",
        2,
        Capability::Pure,
        None,
        bif_fun_info,
    ),
    (
        "io_lib_format",
        "fwrite_g",
        1,
        Capability::Pure,
        None,
        bif_fwrite_g,
    ),
];

/// Registers all stdlib stub BIFs under their OTP module names.
pub fn register_stdlib_stubs(
    registry: &BifRegistryImpl,
    atom_table: &AtomTable,
) -> Result<(), NativeRegistrationError> {
    for &(module_name, function_name, arity, capability, dirty_kind, native_function) in
        STDLIB_STUBS
    {
        let module = atom_table.intern(module_name);
        let function = atom_table.intern(function_name);
        register_stub(
            registry,
            module,
            function,
            arity,
            native_function,
            capability,
            dirty_kind,
        )?;
    }

    Ok(())
}

fn register_stub(
    registry: &BifRegistryImpl,
    module: Atom,
    function: Atom,
    arity: u8,
    native_function: NativeFn,
    capability: Capability,
    dirty_kind: Option<DirtySchedulerKind>,
) -> Result<(), NativeRegistrationError> {
    if let Some(kind) = dirty_kind {
        registry.register_dirty(module, function, arity, native_function, kind, capability)
    } else {
        registry.register(module, function, arity, native_function, capability)
    }
}
