use core::task::{Context, Poll};

use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::{device::Device, spi::SpiController};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use yggdrasil_abi::{
    error::Error,
    io::device::spi::{self, SpiConfig, SpiRequestVariant},
    option::RequestValue,
};

use crate::{
    device::char::CharDevice,
    task::{mem::ForeignPointer, thread::Thread},
    vfs::FileReadiness,
};

pub struct SpiControllerDevice {
    device: Arc<dyn SpiController>,
    config: IrqSafeRwLock<SpiConfig>,
}

impl From<Arc<dyn SpiController>> for SpiControllerDevice {
    fn from(value: Arc<dyn SpiController>) -> Self {
        Self {
            device: value,
            config: IrqSafeRwLock::new(Default::default()),
        }
    }
}

impl Device for SpiControllerDevice {
    fn display_name(&self) -> &str {
        self.device.display_name()
    }
}

// TODO timeouts and better access
#[async_trait]
impl CharDevice for SpiControllerDevice {
    async fn read(&self, _buffer: &mut [u8]) -> Result<usize, Error> {
        Err(Error::InvalidOperation)
    }
    async fn write(&self, _buffer: &[u8]) -> Result<usize, Error> {
        Err(Error::InvalidOperation)
    }
    fn read_nonblocking(&self, _buffer: &mut [u8]) -> Result<usize, Error> {
        Err(Error::InvalidOperation)
    }
    fn write_nonblocking(&self, _buffer: &[u8]) -> Result<usize, Error> {
        Err(Error::InvalidOperation)
    }
    fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
        if let Ok(request) = SpiRequestVariant::try_from(option) {
            match request {
                SpiRequestVariant::Transfer => {
                    let xfer = spi::Transfer::load_request(&buffer[..len])?;
                    let thread = Thread::current();
                    let space = thread.address_space();
                    let len = xfer.len;
                    let tx_buffer = unsafe { xfer.tx_buffer.validate_user_slice(len, space) }?;
                    let rx_buffer = unsafe { xfer.rx_buffer.validate_user_slice_mut(len, space) }?;
                    let config = self.config.read();

                    let len = block!(
                        self.device
                            .spi_transfer(tx_buffer, rx_buffer, &config)
                            .await
                    )??;

                    let len = spi::Transfer::store_response(&len, buffer)?;

                    Ok(len)
                }
                SpiRequestVariant::SetConfig => {
                    // TODO validate config against min/max controller capabilities
                    let config = spi::SetConfig::load_request(&buffer[..len])?;
                    *self.config.write() = config;
                    Ok(0)
                }
            }
        } else {
            todo!()
        }
    }
}

impl FileReadiness for SpiControllerDevice {
    fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
        let _ = cx;
        todo!()
    }
}
