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

use alloc::{string::String, sync::Arc, vec::Vec};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
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 StringAttributeOps: Sync + Send + 'static {
    type Data: Send + 'static = ();

    const WRITEABLE: bool = false;
    const NAME: &'static str;
    const LIMIT: usize = 4096;
    const NEWLINE: bool = true;

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

pub struct StringAttribute<V: StringAttributeOps>(PhantomData<V>);

struct StringAttributeNode<V: StringAttributeOps> {
    object: Arc<KObject<V::Data>>,
    _pd: PhantomData<V>,
}

struct StringAttributeState {
    value: IrqSafeRwLock<Vec<u8>>,
    modified: AtomicBool,
}

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

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

impl<V: StringAttributeOps> RegularImpl for StringAttributeNode<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 mut value = V::read(self.object.data())?.into_bytes();
        if V::NEWLINE {
            value.push(b'\n');
        }

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

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

    fn close(&self, _node: &NodeRef, instance: Option<&InstanceData>) -> Result<(), Error> {
        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::<StringAttributeState>()
            .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> {
        if !V::WRITEABLE {
            return Err(Error::InvalidFile);
        }
        let instance = instance.ok_or(Error::InvalidFile)?;
        let instance = instance
            .downcast_ref::<StringAttributeState>()
            .ok_or(Error::InvalidFile)?;

        let mut value = instance.value.write();

        let pos: usize = pos.try_into().map_err(|_| Error::InvalidFile)?;
        if pos > value.len() {
            return Err(Error::InvalidArgument);
        }
        if pos + buf.len() > V::LIMIT {
            return Err(Error::InvalidArgument);
        }

        let amount_copy = (value.len() - pos).min(buf.len());

        value[pos..pos + amount_copy].copy_from_slice(&buf[..amount_copy]);
        if amount_copy < buf.len() {
            value.extend_from_slice(&buf[amount_copy..]);
        }
        instance.modified.store(true, Ordering::Release);

        Ok(buf.len())
    }

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

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

impl<V: StringAttributeOps> Attribute<V::Data> for StringAttribute<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(
            StringAttributeNode {
                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) }
    }
}
