use core::{any::Any, marker::PhantomData, sync::atomic::AtomicBool};

use alloc::{sync::Arc, vec::Vec};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use yggdrasil_abi::{
    error::Error,
    io::{FileMode, OpenOptions},
};

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

pub trait IntegerAttributeValue: Sized + Send + Sync + 'static {
    fn from_raw(data: &[u8]) -> Result<Self, Error>;
    fn into_raw(&self, format: IntegerAttributeFormat) -> Result<Vec<u8>, Error>;
}

pub enum IntegerAttributeFormat {
    Decimal,
    Hex,
    Octal,
}

pub trait IntegerAttributeOps<N: IntegerAttributeValue>: Sync + Send + 'static {
    type Data: Send + 'static = ();

    const WRITEABLE: bool = false;
    const NAME: &'static str;
    const FORMAT: IntegerAttributeFormat = IntegerAttributeFormat::Decimal;

    fn read(state: &Self::Data) -> Result<N, Error> {
        let _ = state;
        Err(Error::NotImplemented)
    }
    fn write(state: &Self::Data, value: N) -> Result<(), Error> {
        let _ = state;
        let _ = value;
        Err(Error::ReadOnly)
    }
}

pub struct IntegerAttribute<N: IntegerAttributeValue, V: IntegerAttributeOps<N>>(
    PhantomData<(V, N)>,
);

struct IntegerAttributeNode<N: IntegerAttributeValue, V: IntegerAttributeOps<N>> {
    object: Arc<KObject<V::Data>>,
    _pd: PhantomData<(V, N)>,
}

struct IntegerAttributeState {
    value: IrqSafeRwLock<Vec<u8>>,
    #[allow(unused)]
    modified: AtomicBool,
}

macro_rules! impl_integer_value {
    ($($ty:ty),+ $(,)?) => {
        $(
            impl IntegerAttributeValue for $ty {
                fn from_raw(_data: &[u8]) -> Result<Self, Error> {
                    todo!("IntegerAttributeValue::from_raw()")
                }
                fn into_raw(&self, format: IntegerAttributeFormat) -> Result<Vec<u8>, Error> {
                    let string = match format {
                        IntegerAttributeFormat::Decimal => alloc::format!("{self}\n"),
                        IntegerAttributeFormat::Hex => alloc::format!("{self:#x}\n"),
                        IntegerAttributeFormat::Octal => alloc::format!("{self:#o}\n")
                    };
                    Ok(string.into_bytes())
                }
            }
        )+
    };
}

impl_integer_value!(u8, u16, u32, u64, i8, i16, i32, i64);

impl<N: IntegerAttributeValue, V: IntegerAttributeOps<N>> CommonImpl
    for IntegerAttributeNode<N, V>
{
    fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
        Ok(0)
    }

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

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

        let value = V::read(self.object.data())?.into_raw(V::FORMAT)?;

        let instance = IntegerAttributeState {
            value: IrqSafeRwLock::new(value),
            modified: AtomicBool::new(false),
        };

        Ok((0, Some(Arc::new(instance))))
    }

    fn close(&self, _node: &NodeRef, _instance: Option<&InstanceData>) -> Result<(), Error> {
        Ok(())
        // if V::WRITEABLE {
        //     let instance = instance.ok_or(Error::InvalidFile)?;
        //     let instance = instance
        //         .downcast_ref::<StringAttributeState>()
        //         .ok_or(Error::InvalidFile)?;

        //     if instance.modified.load(Ordering::Acquire) {
        //         let value = instance.value.read();
        //         let value_str =
        //             core::str::from_utf8(&value[..]).map_err(|_| Error::InvalidArgument)?;

        //         // Trim whitespace and newlines
        //         V::write(&self.object.data, value_str.trim())?;
        //     }
        // }

        // Ok(())
    }

    fn read(
        &self,
        _node: &NodeRef,
        instance: Option<&InstanceData>,
        pos: u64,
        buf: &mut [u8],
    ) -> Result<usize, Error> {
        let instance = instance.ok_or(Error::InvalidFile)?;
        let instance = instance
            .downcast_ref::<IntegerAttributeState>()
            .ok_or(Error::InvalidFile)?;

        let value = instance.value.read();
        let len = value.len();
        if pos >= len as u64 {
            return Ok(0);
        }
        let pos = pos as usize;
        let amount = (len - pos).min(buf.len());
        buf[..amount].copy_from_slice(&value[pos..pos + amount]);

        Ok(amount)
    }

    fn write(
        &self,
        _node: &NodeRef,
        _instance: Option<&InstanceData>,
        _pos: u64,
        _buf: &[u8],
    ) -> Result<usize, Error> {
        todo!("Integer attribute write")
    }

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

impl<N: IntegerAttributeValue, V: IntegerAttributeOps<N>> From<V> for IntegerAttribute<N, V> {
    fn from(_value: V) -> Self {
        Self(PhantomData)
    }
}

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

        Ok(Node::regular(
            IntegerAttributeNode {
                object: parent.clone(),
                _pd: PhantomData::<(V, N)>,
            },
            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) }
    }
}
