use alloc::sync::Arc;

use libk::{error::Error, task::thread::Thread, vfs::File};
use yggdrasil_abi::{
    abi_serde::wire,
    net::{types::LocalSocketAddress, AncillaryMessage, MessageHeader, MessageHeaderMut},
};

pub mod packet;
pub use packet::LocalPacketSocket;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum OwnedAddress {
    Path(Arc<str>),
    Anonymous(u64),
}

pub fn load_address(bytes: &[u8]) -> Result<LocalSocketAddress<'_>, Error> {
    Ok(wire::from_slice(bytes)?)
}

pub fn store_address(
    message: &mut MessageHeaderMut,
    address: &LocalSocketAddress,
) -> Result<(), Error> {
    if let Some(source) = message.source.as_mut() {
        wire::to_slice(address, source)?;
    }
    Ok(())
}

pub fn read_ancillary(message: &MessageHeader) -> Result<Option<Arc<File>>, Error> {
    if message.ancillary.is_empty() {
        return Ok(None);
    }
    let ancillary: AncillaryMessage = wire::from_slice(message.ancillary)?;
    #[allow(irrefutable_let_patterns)]
    let AncillaryMessage::File(fd) = ancillary
    else {
        log::warn!("local: invalid ancillary message: {ancillary:?}");
        return Err(Error::InvalidArgument);
    };
    let process = Thread::current().process();
    let file = process.io.lock().files.file(fd).cloned()?;
    Ok(Some(file))
}
pub fn write_ancillary(
    message: &mut MessageHeaderMut,
    file: Option<Arc<File>>,
) -> Result<(), Error> {
    let have_file = file.is_some();
    if let (Some(ancillary), Some(file)) = (message.ancillary.as_mut(), file) {
        let process = Thread::current().process();
        let mut io = process.io.lock();
        let fd = io.files.place_file(file, true)?;
        let msg = AncillaryMessage::File(fd);
        match wire::to_slice(&msg, ancillary).map_err(Error::from) {
            Ok(len) => {
                message.ancillary_len = len;
                log::debug!("local: shared ancillary fd {fd:?}");
                Ok(())
            }
            Err(error) => {
                log::warn!("local: failed to store ancillary file: {error:?}");
                io.files.close_file(fd).ok();
                Err(error)
            }
        }
    } else {
        if have_file && message.ancillary.is_none() {
            log::warn!("local: ancillary data discarded: receiver did not specify a buffer");
        }
        message.ancillary_len = 0;
        Ok(())
    }
}

impl OwnedAddress {
    fn to_borrowed(&self) -> LocalSocketAddress<'_> {
        match self {
            Self::Path(path) => LocalSocketAddress::Path(path.as_ref()),
            Self::Anonymous(anon) => LocalSocketAddress::Anonymous(*anon),
        }
    }
}

impl From<LocalSocketAddress<'_>> for OwnedAddress {
    fn from(value: LocalSocketAddress) -> Self {
        match value {
            LocalSocketAddress::Path(path) => Self::Path(path.into()),
            LocalSocketAddress::Anonymous(anon) => Self::Anonymous(anon),
        }
    }
}
