use crate::{errors::ProtocolError, protocol::{Header, MessageType, ReplyData, RequestData}};
use crate::encoding::{ToBytes, FromBytes};

pub trait Transport {
    fn read(&mut self) -> std::io::Result<&[u8]>;
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize>;
    
    fn read_message(&mut self) -> Result<MessageType, Box<dyn std::error::Error>>
    {
        let buffer = self.read()?;
        let bytes = buffer.len();
        let mut read: i32 = 0;
        let header = Header::from_bytes(&buffer[read as usize..bytes], &mut read)?;

        match header.message_type {
            2 => {
                let reply = ReplyData::from_bytes(&buffer[read as usize..bytes as usize], &mut read)?;
                Ok(MessageType::Reply(header, reply))
            }
            3 => Ok(MessageType::ValidateConnection(header)),
            _ => Err(Box::new(ProtocolError::new(&format!("TCP: Unsuppored reply message type: {}", header.message_type))))
        }
    }

    fn validate_connection(&mut self) -> Result<(), Box<dyn std::error::Error>>
    {
        let header = Header::new(0, 14);
        let mut bytes = header.to_bytes()?;
        let written = self.write(&mut bytes)?;
        if written != header.message_size as usize {
            return Err(Box::new(ProtocolError::new("TCP: Could not validate connection")))
        }

        Ok(())
    }

    fn close_connection(&mut self) -> Result<(), Box<dyn std::error::Error>>
    {
        let header = Header::new(4, 14);
        let mut bytes = header.to_bytes()?;
        let written = self.write(&mut bytes)?;
        if written != header.message_size as usize {
            return Err(Box::new(ProtocolError::new("TCP: Could not validate connection")))
        }

        Ok(())
    }

    fn make_request(&mut self, request: &RequestData) -> Result<(), Box<dyn std::error::Error>>
    {
        let req_bytes = request.to_bytes()?;
        let header = Header::new(0, 14 + req_bytes.len() as i32);
        let mut bytes = header.to_bytes()?;
        bytes.extend(req_bytes);

        let written = self.write(&mut bytes)?;
        if written != header.message_size as usize {
            return Err(Box::new(ProtocolError::new(&format!("TCP: Error writing request {}", request.request_id))))
        }
        Ok(())
    }
}
