use std::{
    io,
    os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd},
    path::{Path, PathBuf},
};

use runtime::{
    abi::net::{types::LocalSocketAddress, AncillaryMessage, MessageHeader, MessageHeaderMut},
    abi_serde::wire,
    rt as yggdrasil_rt,
};
use yggdrasil_rt::net::SocketType;

use crate::sys::{self, LocalPacketSocket};

pub struct LocalPacketSocketImpl {
    fd: OwnedFd,
}

#[derive(Debug)]
pub enum BorrowedAddressImpl<'a> {
    Named(&'a Path),
    #[allow(unused)]
    Anonymous(u64),
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum OwnedAddressImpl {
    Named(PathBuf),
    Anonymous(u64),
}

impl<'a> BorrowedAddressImpl<'a> {
    pub fn to_sys(&self) -> LocalSocketAddress<'a> {
        match *self {
            Self::Named(path) => LocalSocketAddress::Path(path.to_str().unwrap()),
            Self::Anonymous(id) => LocalSocketAddress::Anonymous(id),
        }
    }
}

impl OwnedAddressImpl {
    pub fn from_sys(address: &LocalSocketAddress) -> Self {
        match *address {
            LocalSocketAddress::Path(path) => Self::Named(path.into()),
            LocalSocketAddress::Anonymous(id) => Self::Anonymous(id),
        }
    }
}

impl<'a> sys::BorrowedAddress<'a> for BorrowedAddressImpl<'a> {
    type Owned = OwnedAddressImpl;
}

impl sys::OwnedAddress for OwnedAddressImpl {
    type Borrowed<'a> = BorrowedAddressImpl<'a>;

    fn as_borrowed(&self) -> Self::Borrowed<'_> {
        match self {
            Self::Named(path) => BorrowedAddressImpl::Named(path.as_path()),
            &Self::Anonymous(id) => BorrowedAddressImpl::Anonymous(id),
        }
    }

    fn from_borrowed(borrowed: &Self::Borrowed<'_>) -> Self {
        match *borrowed {
            BorrowedAddressImpl::Named(path) => Self::Named(path.into()),
            BorrowedAddressImpl::Anonymous(id) => Self::Anonymous(id),
        }
    }
}

fn read_ancillary(buffer: &[u8]) -> io::Result<Option<OwnedFd>> {
    if buffer.is_empty() {
        return Ok(None);
    }
    let message: AncillaryMessage = wire::from_slice(buffer).map_err(yggdrasil_rt::Error::from)?;
    match message {
        AncillaryMessage::File(fd) => Ok(Some(unsafe { OwnedFd::from_raw_fd(fd) })),
    }
}

impl LocalPacketSocket for LocalPacketSocketImpl {
    type Address<'a> = BorrowedAddressImpl<'a>;
    type OwnedAddress = OwnedAddressImpl;

    fn new() -> io::Result<Self> {
        let raw = unsafe { yggdrasil_rt::sys::create_socket(SocketType::LocalPacket) }?;
        let fd = unsafe { OwnedFd::from_raw_fd(raw) };
        Ok(Self { fd })
    }

    fn connect<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        let path = path.as_ref().to_str().unwrap();
        let path = LocalSocketAddress::Path(path);
        let mut address = [0; 512];
        let len = wire::to_slice(&path, &mut address).map_err(yggdrasil_rt::Error::from)?;
        let this = Self::new()?;
        unsafe { yggdrasil_rt::sys::connect(this.as_raw_fd(), &address[..len]) }?;
        Ok(this)
    }

    fn bind<P: AsRef<Path>>(path: P) -> io::Result<Self> {
        let path = path.as_ref().to_str().unwrap();
        let path = LocalSocketAddress::Path(path);
        let mut address = [0; 512];
        let len = wire::to_slice(&path, &mut address).map_err(yggdrasil_rt::Error::from)?;
        let this = Self::new()?;
        unsafe { yggdrasil_rt::sys::bind(this.as_raw_fd(), &address[..len]) }?;
        Ok(this)
    }

    fn set_nonblocking(&self, nb: bool) -> io::Result<()> {
        runtime::rt::io::set_nonblocking(self.as_raw_fd(), nb)?;
        Ok(())
    }

    fn receive(&self, data: &mut [u8]) -> io::Result<usize> {
        let len = yggdrasil_rt::net::socket::receive(self.as_raw_fd(), data)?;
        Ok(len)
    }

    fn receive_from(&self, data: &mut [u8]) -> io::Result<(usize, Self::OwnedAddress)> {
        let mut address = [0; 512];
        let mut message = MessageHeaderMut {
            source: Some(&mut address),
            payload: data,
            ancillary: None,
            ancillary_len: 0,
        };
        let len = unsafe { yggdrasil_rt::sys::receive_message(self.as_raw_fd(), &mut message) }?;
        let address: LocalSocketAddress =
            wire::from_slice(&address).map_err(yggdrasil_rt::Error::from)?;
        Ok((len, OwnedAddressImpl::from_sys(&address)))
    }

    fn receive_with_ancillary(&self, data: &mut [u8]) -> io::Result<(usize, Option<OwnedFd>)> {
        let mut ancillary = [0; 32];
        let mut message = MessageHeaderMut {
            source: None,
            payload: data,
            ancillary: Some(&mut ancillary),
            ancillary_len: 0,
        };
        let len = unsafe { yggdrasil_rt::sys::receive_message(self.as_raw_fd(), &mut message) }?;
        let anc_len = message.ancillary_len;
        let fd = read_ancillary(&ancillary[..anc_len])?;
        Ok((len, fd))
    }

    fn receive_with_ancillary_from(
        &self,
        data: &mut [u8],
    ) -> io::Result<(usize, Option<OwnedFd>, Self::OwnedAddress)> {
        let mut ancillary = [0; 32];
        let mut address = [0; 512];
        let mut message = MessageHeaderMut {
            source: Some(&mut address),
            payload: data,
            ancillary: Some(&mut ancillary),
            ancillary_len: 0,
        };
        let len = unsafe { yggdrasil_rt::sys::receive_message(self.as_raw_fd(), &mut message) }?;
        let anc_len = message.ancillary_len;
        let address: LocalSocketAddress =
            wire::from_slice(&address).map_err(yggdrasil_rt::Error::from)?;
        let address = OwnedAddressImpl::from_sys(&address);
        let fd = read_ancillary(&ancillary[..anc_len])?;
        Ok((len, fd, address))
    }

    fn send(&self, data: &[u8]) -> io::Result<usize> {
        let len = yggdrasil_rt::net::socket::send(self.as_raw_fd(), data)?;
        Ok(len)
    }

    fn send_to(&self, data: &[u8], address: &Self::Address<'_>) -> io::Result<usize> {
        let address = address.to_sys();
        let mut destination = [0; 512];
        let address_len =
            wire::to_slice(&address, &mut destination).map_err(yggdrasil_rt::Error::from)?;
        let message = MessageHeader {
            payload: data,
            destination: Some(&destination[..address_len]),
            ancillary: &[],
        };
        let len = unsafe { yggdrasil_rt::sys::send_message(self.as_raw_fd(), &message) }?;
        Ok(len)
    }

    fn send_with_ancillary(&self, data: &[u8], ancillary: RawFd) -> io::Result<usize> {
        let mut anc_buffer = [0; 32];
        let anc_len = wire::to_slice(&AncillaryMessage::File(ancillary), &mut anc_buffer)
            .map_err(yggdrasil_rt::Error::from)?;
        let message = MessageHeader {
            destination: None,
            payload: data,
            ancillary: &anc_buffer[..anc_len],
        };
        let len = unsafe { yggdrasil_rt::sys::send_message(self.as_raw_fd(), &message) }?;
        Ok(len)
    }

    fn send_with_ancillary_to(
        &self,
        data: &[u8],
        ancillary: RawFd,
        address: &Self::Address<'_>,
    ) -> io::Result<usize> {
        let address = address.to_sys();
        let mut destination = [0; 512];
        let mut anc_buffer = [0; 32];
        let anc_len = wire::to_slice(&AncillaryMessage::File(ancillary), &mut anc_buffer)
            .map_err(yggdrasil_rt::Error::from)?;
        let address_len =
            wire::to_slice(&address, &mut destination).map_err(yggdrasil_rt::Error::from)?;
        let message = MessageHeader {
            destination: Some(&destination[..address_len]),
            payload: data,
            ancillary: &anc_buffer[..anc_len],
        };
        let len = unsafe { yggdrasil_rt::sys::send_message(self.as_raw_fd(), &message) }?;
        Ok(len)
    }
}

impl AsRawFd for LocalPacketSocketImpl {
    fn as_raw_fd(&self) -> RawFd {
        self.fd.as_raw_fd()
    }
}

impl<'a> From<&'a Path> for BorrowedAddressImpl<'a> {
    fn from(value: &'a Path) -> Self {
        Self::Named(value)
    }
}
