use libk::error::Error;

use crate::device::ScsiDeviceType;

pub trait ScsiCommand {
    type Response;
    const REQUEST_LEN: usize;
    const RESPONSE_LEN: usize;

    fn into_bytes(self) -> [u8; Self::REQUEST_LEN];
    fn parse_response(bytes: &[u8]) -> Result<Self::Response, Error>;
}

// Add more info when needed
pub struct ScsiInquiry;
#[derive(Debug)]
pub struct ScsiInquiryResponse {
    pub device_type: ScsiDeviceType,
}

impl ScsiCommand for ScsiInquiry {
    type Response = ScsiInquiryResponse;
    const REQUEST_LEN: usize = 6;
    const RESPONSE_LEN: usize = 36;

    fn into_bytes(self) -> [u8; Self::REQUEST_LEN] {
        [0x12, 0x00, 0x00, 0x00, 0x00, 0x00]
    }

    fn parse_response(bytes: &[u8]) -> Result<Self::Response, Error> {
        if bytes.len() != 36 {
            return Err(Error::InvalidArgument);
        }
        let device_type = ScsiDeviceType::try_from(bytes[0] & 0x1F).unwrap_or_default();
        Ok(ScsiInquiryResponse { device_type })
    }
}

pub struct ScsiTestUnitReady;
#[derive(Debug)]
pub struct ScsiTestUnitReadyResponse;

impl ScsiCommand for ScsiTestUnitReady {
    type Response = ScsiTestUnitReadyResponse;
    const RESPONSE_LEN: usize = 0;
    const REQUEST_LEN: usize = 6;

    fn into_bytes(self) -> [u8; Self::REQUEST_LEN] {
        [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
    }

    fn parse_response(_bytes: &[u8]) -> Result<Self::Response, Error> {
        Ok(ScsiTestUnitReadyResponse)
    }
}

pub struct ScsiRequestSense;
#[derive(Debug)]
pub struct ScsiRequestSenseResponse;

impl ScsiCommand for ScsiRequestSense {
    type Response = ScsiRequestSenseResponse;
    const RESPONSE_LEN: usize = 0;
    const REQUEST_LEN: usize = 6;

    fn into_bytes(self) -> [u8; Self::REQUEST_LEN] {
        [0x03, 0x00, 0x00, 0x00, 0x00, 0x00]
    }

    fn parse_response(_bytes: &[u8]) -> Result<Self::Response, Error> {
        Ok(ScsiRequestSenseResponse)
    }
}

pub struct ScsiReadCapacity;
#[derive(Debug)]
pub struct ScsiReadCapacityResponse {
    pub block_size: u32,
    pub block_count: u32,
}

impl ScsiCommand for ScsiReadCapacity {
    type Response = ScsiReadCapacityResponse;
    const REQUEST_LEN: usize = 10;
    const RESPONSE_LEN: usize = 8;

    fn into_bytes(self) -> [u8; Self::REQUEST_LEN] {
        [0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
    }

    fn parse_response(bytes: &[u8]) -> Result<Self::Response, Error> {
        if bytes.len() != 8 {
            return Err(Error::InvalidArgument);
        }
        let block_count = u32::from_be_bytes(bytes[0..4].try_into().unwrap());
        let block_size = u32::from_be_bytes(bytes[4..8].try_into().unwrap());
        Ok(ScsiReadCapacityResponse {
            block_size,
            block_count,
        })
    }
}
