//! Implementation of the `errno` module.
//!
//! This module exposes Darwin/POSIX errno constants and the `errorcode` mapping
//! used by CPython on macOS. The values are intentionally fixed to Darwin values
//! because Ouros reports `sys.platform == "darwin"`.

use crate::{
    heap::{Heap, HeapData, HeapId},
    intern::{Interns, StaticStrings},
    resource::{ResourceError, ResourceTracker},
    types::{Dict, Module, Str},
    value::Value,
};

/// `(name, value)` errno constants for Darwin.
const DARWIN_ERRNO_CONSTANTS: &[(&str, i64)] = &[
    ("EPERM", 1),
    ("ENOENT", 2),
    ("ESRCH", 3),
    ("EINTR", 4),
    ("EIO", 5),
    ("ENXIO", 6),
    ("E2BIG", 7),
    ("ENOEXEC", 8),
    ("EBADF", 9),
    ("ECHILD", 10),
    ("EDEADLK", 11),
    ("ENOMEM", 12),
    ("EACCES", 13),
    ("EFAULT", 14),
    ("ENOTBLK", 15),
    ("EBUSY", 16),
    ("EEXIST", 17),
    ("EXDEV", 18),
    ("ENODEV", 19),
    ("ENOTDIR", 20),
    ("EISDIR", 21),
    ("EINVAL", 22),
    ("ENFILE", 23),
    ("EMFILE", 24),
    ("ENOTTY", 25),
    ("ETXTBSY", 26),
    ("EFBIG", 27),
    ("ENOSPC", 28),
    ("ESPIPE", 29),
    ("EROFS", 30),
    ("EMLINK", 31),
    ("EPIPE", 32),
    ("EDOM", 33),
    ("ERANGE", 34),
    ("EAGAIN", 35),
    ("EWOULDBLOCK", 35),
    ("EINPROGRESS", 36),
    ("EALREADY", 37),
    ("ENOTSOCK", 38),
    ("EDESTADDRREQ", 39),
    ("EMSGSIZE", 40),
    ("EPROTOTYPE", 41),
    ("ENOPROTOOPT", 42),
    ("EPROTONOSUPPORT", 43),
    ("ESOCKTNOSUPPORT", 44),
    ("ENOTSUP", 45),
    ("EPFNOSUPPORT", 46),
    ("EAFNOSUPPORT", 47),
    ("EADDRINUSE", 48),
    ("EADDRNOTAVAIL", 49),
    ("ENETDOWN", 50),
    ("ENETUNREACH", 51),
    ("ENETRESET", 52),
    ("ECONNABORTED", 53),
    ("ECONNRESET", 54),
    ("ENOBUFS", 55),
    ("EISCONN", 56),
    ("ENOTCONN", 57),
    ("ESHUTDOWN", 58),
    ("ETOOMANYREFS", 59),
    ("ETIMEDOUT", 60),
    ("ECONNREFUSED", 61),
    ("ELOOP", 62),
    ("ENAMETOOLONG", 63),
    ("EHOSTDOWN", 64),
    ("EHOSTUNREACH", 65),
    ("ENOTEMPTY", 66),
    ("EPROCLIM", 67),
    ("EUSERS", 68),
    ("EDQUOT", 69),
    ("ESTALE", 70),
    ("EREMOTE", 71),
    ("EBADRPC", 72),
    ("ERPCMISMATCH", 73),
    ("EPROGUNAVAIL", 74),
    ("EPROGMISMATCH", 75),
    ("EPROCUNAVAIL", 76),
    ("ENOLCK", 77),
    ("ENOSYS", 78),
    ("EFTYPE", 79),
    ("EAUTH", 80),
    ("ENEEDAUTH", 81),
    ("EPWROFF", 82),
    ("EDEVERR", 83),
    ("EOVERFLOW", 84),
    ("EBADEXEC", 85),
    ("EBADARCH", 86),
    ("ESHLIBVERS", 87),
    ("EBADMACHO", 88),
    ("ECANCELED", 89),
    ("EIDRM", 90),
    ("ENOMSG", 91),
    ("EILSEQ", 92),
    ("ENOATTR", 93),
    ("EBADMSG", 94),
    ("EMULTIHOP", 95),
    ("ENODATA", 96),
    ("ENOLINK", 97),
    ("ENOSR", 98),
    ("ENOSTR", 99),
    ("EPROTO", 100),
    ("ETIME", 101),
    ("EOPNOTSUPP", 102),
    ("ENOPOLICY", 103),
    ("ENOTRECOVERABLE", 104),
    ("EOWNERDEAD", 105),
    ("EQFULL", 106),
    ("ENOTCAPABLE", 107),
];

/// Canonical Darwin `errno.errorcode` entries (`code -> name`).
const DARWIN_ERRORCODES: &[(i64, &str)] = &[
    (1, "EPERM"),
    (2, "ENOENT"),
    (3, "ESRCH"),
    (4, "EINTR"),
    (5, "EIO"),
    (6, "ENXIO"),
    (7, "E2BIG"),
    (8, "ENOEXEC"),
    (9, "EBADF"),
    (10, "ECHILD"),
    (11, "EDEADLK"),
    (12, "ENOMEM"),
    (13, "EACCES"),
    (14, "EFAULT"),
    (15, "ENOTBLK"),
    (16, "EBUSY"),
    (17, "EEXIST"),
    (18, "EXDEV"),
    (19, "ENODEV"),
    (20, "ENOTDIR"),
    (21, "EISDIR"),
    (22, "EINVAL"),
    (23, "ENFILE"),
    (24, "EMFILE"),
    (25, "ENOTTY"),
    (26, "ETXTBSY"),
    (27, "EFBIG"),
    (28, "ENOSPC"),
    (29, "ESPIPE"),
    (30, "EROFS"),
    (31, "EMLINK"),
    (32, "EPIPE"),
    (33, "EDOM"),
    (34, "ERANGE"),
    (35, "EAGAIN"),
    (36, "EINPROGRESS"),
    (37, "EALREADY"),
    (38, "ENOTSOCK"),
    (39, "EDESTADDRREQ"),
    (40, "EMSGSIZE"),
    (41, "EPROTOTYPE"),
    (42, "ENOPROTOOPT"),
    (43, "EPROTONOSUPPORT"),
    (44, "ESOCKTNOSUPPORT"),
    (45, "ENOTSUP"),
    (46, "EPFNOSUPPORT"),
    (47, "EAFNOSUPPORT"),
    (48, "EADDRINUSE"),
    (49, "EADDRNOTAVAIL"),
    (50, "ENETDOWN"),
    (51, "ENETUNREACH"),
    (52, "ENETRESET"),
    (53, "ECONNABORTED"),
    (54, "ECONNRESET"),
    (55, "ENOBUFS"),
    (56, "EISCONN"),
    (57, "ENOTCONN"),
    (58, "ESHUTDOWN"),
    (59, "ETOOMANYREFS"),
    (60, "ETIMEDOUT"),
    (61, "ECONNREFUSED"),
    (62, "ELOOP"),
    (63, "ENAMETOOLONG"),
    (64, "EHOSTDOWN"),
    (65, "EHOSTUNREACH"),
    (66, "ENOTEMPTY"),
    (67, "EPROCLIM"),
    (68, "EUSERS"),
    (69, "EDQUOT"),
    (70, "ESTALE"),
    (71, "EREMOTE"),
    (72, "EBADRPC"),
    (73, "ERPCMISMATCH"),
    (74, "EPROGUNAVAIL"),
    (75, "EPROGMISMATCH"),
    (76, "EPROCUNAVAIL"),
    (77, "ENOLCK"),
    (78, "ENOSYS"),
    (79, "EFTYPE"),
    (80, "EAUTH"),
    (81, "ENEEDAUTH"),
    (82, "EPWROFF"),
    (83, "EDEVERR"),
    (84, "EOVERFLOW"),
    (85, "EBADEXEC"),
    (86, "EBADARCH"),
    (87, "ESHLIBVERS"),
    (88, "EBADMACHO"),
    (89, "ECANCELED"),
    (90, "EIDRM"),
    (91, "ENOMSG"),
    (92, "EILSEQ"),
    (93, "ENOATTR"),
    (94, "EBADMSG"),
    (95, "EMULTIHOP"),
    (96, "ENODATA"),
    (97, "ENOLINK"),
    (98, "ENOSR"),
    (99, "ENOSTR"),
    (100, "EPROTO"),
    (101, "ETIME"),
    (102, "EOPNOTSUPP"),
    (103, "ENOPOLICY"),
    (104, "ENOTRECOVERABLE"),
    (105, "EOWNERDEAD"),
    (106, "EQFULL"),
    (107, "ENOTCAPABLE"),
];

/// Creates the `errno` module and allocates it on the heap.
///
/// The module exports integer constants for each Darwin errno symbol and
/// `errorcode`, a dict that maps canonical integer codes to symbolic names.
pub fn create_module(heap: &mut Heap<impl ResourceTracker>, interns: &Interns) -> Result<HeapId, ResourceError> {
    let mut module = Module::new(StaticStrings::Errno);

    for (name, value) in DARWIN_ERRNO_CONSTANTS {
        module.set_attr_str(name, Value::Int(*value), heap, interns)?;
    }

    let errorcode_id = create_errorcode_dict(heap, interns)?;
    module.set_attr_str("errorcode", Value::Ref(errorcode_id), heap, interns)?;

    heap.allocate(HeapData::Module(module))
}

/// Builds the `errno.errorcode` dictionary for Darwin.
fn create_errorcode_dict(heap: &mut Heap<impl ResourceTracker>, interns: &Interns) -> Result<HeapId, ResourceError> {
    let mut errorcode = Dict::with_capacity(DARWIN_ERRORCODES.len());

    for (code, name) in DARWIN_ERRORCODES {
        let name_id = heap.allocate(HeapData::Str(Str::from(*name)))?;
        if let Some(replaced) = errorcode
            .set(Value::Int(*code), Value::Ref(name_id), heap, interns)
            .expect("errno.errorcode keys and values are always hashable")
        {
            replaced.drop_with_heap(heap);
        }
    }

    heap.allocate(HeapData::Dict(errorcode))
}
