use core::{any::Any, marker::PhantomData};

use alloc::sync::Arc;
use yggdrasil_abi::{
    error::Error,
    io::{FileMode, OpenOptions},
};

use crate::{
    fs::sysfs::object::KObject,
    vfs::{CommonImpl, Filename, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl},
};

use super::Attribute;

pub trait BytesAttributeOps: Send + Sync + 'static {
    type Data: Send + 'static = ();
    const NAME: &'static str;
    const WRITEABLE: bool = false;

    fn read(state: &Self::Data, pos: usize, buffer: &mut [u8]) -> Result<usize, Error> {
        let _ = state;
        let _ = pos;
        let _ = buffer;
        Err(Error::NotImplemented)
    }

    fn write(state: &Self::Data, pos: usize, buffer: &[u8]) -> Result<usize, Error> {
        let _ = state;
        let _ = pos;
        let _ = buffer;
        Err(Error::ReadOnly)
    }

    fn size(state: &Self::Data) -> usize {
        let _ = state;
        0
    }
}

pub struct BytesAttribute<V: BytesAttributeOps>(PhantomData<V>);

struct BytesAttributeNode<V: BytesAttributeOps> {
    object: Arc<KObject<V::Data>>,
    _pd: PhantomData<V>,
}

impl<V: BytesAttributeOps> CommonImpl for BytesAttributeNode<V> {
    fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
        Ok(V::size(&self.object.data) as u64)
    }

    fn as_any(&self) -> &dyn Any {
        self as _
    }
}

impl<V: BytesAttributeOps> RegularImpl for BytesAttributeNode<V> {
    fn open(
        &self,
        _node: &NodeRef,
        opts: OpenOptions,
    ) -> Result<(u64, Option<InstanceData>), Error> {
        if opts.contains(OpenOptions::WRITE) && !V::WRITEABLE {
            return Err(Error::ReadOnly);
        }

        Ok((0, None))
    }

    fn close(&self, _node: &NodeRef, _instance: Option<&InstanceData>) -> Result<(), Error> {
        Ok(())
    }

    fn read(
        &self,
        _node: &NodeRef,
        _instance: Option<&InstanceData>,
        pos: u64,
        buf: &mut [u8],
    ) -> Result<usize, Error> {
        let pos = pos.try_into().map_err(|_| Error::InvalidArgument)?;
        V::read(&self.object.data, pos, buf)
    }

    fn write(
        &self,
        _node: &NodeRef,
        _instance: Option<&InstanceData>,
        pos: u64,
        buf: &[u8],
    ) -> Result<usize, Error> {
        let pos = pos.try_into().map_err(|_| Error::InvalidArgument)?;
        V::write(&self.object.data, pos, buf)
    }

    fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
        Ok(())
    }
}

impl<V: BytesAttributeOps> From<V> for BytesAttribute<V> {
    fn from(_value: V) -> Self {
        Self(PhantomData)
    }
}

impl<V: BytesAttributeOps> Attribute<V::Data> for BytesAttribute<V> {
    fn instantiate(&self, parent: &Arc<KObject<V::Data>>) -> Result<NodeRef, Error> {
        let mode = match V::WRITEABLE {
            false => FileMode::new(0o444),
            true => FileMode::new(0o644),
        };

        Ok(Node::regular(
            BytesAttributeNode {
                object: parent.clone(),
                _pd: PhantomData::<V>,
            },
            NodeFlags::IN_MEMORY_PROPS,
            Some(Metadata::now_root(mode, 0)),
            None,
        ))
    }

    // TODO implement this properly
    fn name(&self) -> &Filename {
        unsafe { Filename::from_str_unchecked(V::NAME) }
    }
}
