use core::{
    ffi::{c_int, c_void},
    net::SocketAddr,
    ptr::{null, null_mut},
};

use yggdrasil_abi::{
    abi_serde::wire,
    net::{MessageHeader, MessageHeaderMut},
};
use yggdrasil_rt::{io::RawFd, sys as syscall};

use crate::{
    error::{CIsizeResult, ResultExt, TryFromExt},
    headers::errno::Errno,
    util::PointerExt,
};

use super::{msghdr, sockaddr, socklen_t, SocketAddrExt};

#[no_mangle]
unsafe extern "C" fn recv(
    fd: c_int,
    buffer: *mut c_void,
    len: usize,
    flags: c_int,
) -> CIsizeResult {
    recvfrom(fd, buffer, len, flags, null_mut(), null_mut())
}

#[no_mangle]
unsafe extern "C" fn recvfrom(
    fd: c_int,
    buffer: *mut c_void,
    len: usize,
    flags: c_int,
    remote: *mut sockaddr,
    remote_len: *mut socklen_t,
) -> CIsizeResult {
    let _ = flags;
    let fd = RawFd::e_try_from(fd)?;
    let payload = buffer.cast::<u8>().ensure_slice_mut(len);
    let mut source = if remote.is_null() || remote_len.is_null() {
        None
    } else {
        Some([0u8; 32])
    };
    let mut message = MessageHeaderMut {
        payload,
        source: source.as_mut().map(|x| &mut x[..]),
        ancillary: None,
        ancillary_len: 0,
    };
    let len = syscall::receive_message(fd, &mut message).e_map_err(Errno::from)?;
    if let Some(source) = source.as_ref() {
        let peer: SocketAddr = wire::from_slice(source)
            .map_err(yggdrasil_rt::Error::from)
            .e_map_err(Errno::from)?;
        *remote_len = peer.to_c(remote, *remote_len)?;
    }
    CIsizeResult::success(len)
}

#[no_mangle]
unsafe extern "C" fn recvmsg(fd: c_int, message: *mut msghdr, flags: c_int) -> CIsizeResult {
    let _ = fd;
    let _ = message;
    let _ = flags;
    todo!()
}

#[no_mangle]
unsafe extern "C" fn send(
    fd: c_int,
    data: *const c_void,
    len: usize,
    flags: c_int,
) -> CIsizeResult {
    sendto(fd, data, len, flags, null(), 0)
}

#[no_mangle]
unsafe extern "C" fn sendmsg(fd: c_int, message: *const msghdr, flags: c_int) -> CIsizeResult {
    let _ = fd;
    let _ = message;
    let _ = flags;
    todo!()
}

#[no_mangle]
unsafe extern "C" fn sendto(
    fd: c_int,
    data: *const c_void,
    len: usize,
    flags: c_int,
    remote: *const sockaddr,
    remote_len: socklen_t,
) -> CIsizeResult {
    let _ = flags;
    let fd = RawFd::e_try_from(fd)?;
    let payload = data.cast::<u8>().ensure_slice(len);
    let destination = if remote.is_null() || remote_len == 0 {
        let mut sockaddr_buf = [0; 32];
        let sockaddr = remote.to_rust(len)?;
        wire::to_slice(&sockaddr, &mut sockaddr_buf)
            .map_err(yggdrasil_rt::Error::from)
            .e_map_err(Errno::from)?;
        Some(sockaddr_buf)
    } else {
        None
    };
    let message = MessageHeader {
        destination: destination.as_ref().map(|x| &x[..]),
        payload,
        ancillary: &[],
    };
    let len = syscall::send_message(fd, &message).e_map_err(Errno::from)?;
    CIsizeResult::success(len)
}
