//! Wire format encoding and decoding for the quicproquo v2 RPC protocol.
//!
//! ## Request frame
//! ```text
//! [method_id: u16 BE][request_id: u32 BE][payload_len: u32 BE][protobuf bytes]
//! ```
//!
//! ## Response frame
//! ```text
//! [status: u8][request_id: u32 BE][payload_len: u32 BE][protobuf bytes]
//! ```
//!
//! ## Push frame (server → client, uni-stream)
//! ```text
//! [event_type: u16 BE][payload_len: u32 BE][protobuf bytes]
//! ```

use bytes::{Buf, BufMut, Bytes, BytesMut};

use crate::error::{RpcError, RpcStatus};

/// Maximum payload size: 4 MiB.
pub const MAX_PAYLOAD_SIZE: usize = 4 * 1024 * 1024;

/// Request header size: 2 (method) + 4 (req_id) + 4 (len) = 10 bytes.
pub const REQUEST_HEADER_SIZE: usize = 10;

/// Response header size: 1 (status) + 4 (req_id) + 4 (len) = 9 bytes.
pub const RESPONSE_HEADER_SIZE: usize = 9;

/// Push header size: 2 (event_type) + 4 (len) = 6 bytes.
pub const PUSH_HEADER_SIZE: usize = 6;

// ── Request ──────────────────────────────────────────────────────────────────

/// A decoded RPC request frame.
#[derive(Debug, Clone)]
pub struct RequestFrame {
    pub method_id: u16,
    pub request_id: u32,
    pub payload: Bytes,
}

impl RequestFrame {
    /// Encode this request into a byte buffer.
    pub fn encode(&self) -> Bytes {
        let mut buf = BytesMut::with_capacity(REQUEST_HEADER_SIZE + self.payload.len());
        buf.put_u16(self.method_id);
        buf.put_u32(self.request_id);
        buf.put_u32(self.payload.len() as u32);
        buf.put(self.payload.clone());
        buf.freeze()
    }

    /// Decode a request frame from a byte buffer.
    /// Returns `None` if the buffer does not contain a complete frame.
    pub fn decode(buf: &mut BytesMut) -> Result<Option<Self>, RpcError> {
        if buf.len() < REQUEST_HEADER_SIZE {
            return Ok(None);
        }

        // Peek at payload_len without consuming.
        let payload_len =
            u32::from_be_bytes([buf[6], buf[7], buf[8], buf[9]]) as usize;

        if payload_len > MAX_PAYLOAD_SIZE {
            return Err(RpcError::PayloadTooLarge {
                size: payload_len,
                max: MAX_PAYLOAD_SIZE,
            });
        }

        let total = REQUEST_HEADER_SIZE + payload_len;
        if buf.len() < total {
            return Ok(None);
        }

        let method_id = buf.get_u16();
        let request_id = buf.get_u32();
        let _len = buf.get_u32();
        let payload = buf.split_to(payload_len).freeze();

        Ok(Some(Self {
            method_id,
            request_id,
            payload,
        }))
    }
}

// ── Response ─────────────────────────────────────────────────────────────────

/// A decoded RPC response frame.
#[derive(Debug, Clone)]
pub struct ResponseFrame {
    pub status: u8,
    pub request_id: u32,
    pub payload: Bytes,
}

impl ResponseFrame {
    /// Encode this response into a byte buffer.
    pub fn encode(&self) -> Bytes {
        let mut buf = BytesMut::with_capacity(RESPONSE_HEADER_SIZE + self.payload.len());
        buf.put_u8(self.status);
        buf.put_u32(self.request_id);
        buf.put_u32(self.payload.len() as u32);
        buf.put(self.payload.clone());
        buf.freeze()
    }

    /// Decode a response frame from a byte buffer.
    pub fn decode(buf: &mut BytesMut) -> Result<Option<Self>, RpcError> {
        if buf.len() < RESPONSE_HEADER_SIZE {
            return Ok(None);
        }

        let payload_len =
            u32::from_be_bytes([buf[5], buf[6], buf[7], buf[8]]) as usize;

        if payload_len > MAX_PAYLOAD_SIZE {
            return Err(RpcError::PayloadTooLarge {
                size: payload_len,
                max: MAX_PAYLOAD_SIZE,
            });
        }

        let total = RESPONSE_HEADER_SIZE + payload_len;
        if buf.len() < total {
            return Ok(None);
        }

        let status = buf.get_u8();
        let request_id = buf.get_u32();
        let _len = buf.get_u32();
        let payload = buf.split_to(payload_len).freeze();

        Ok(Some(Self {
            status,
            request_id,
            payload,
        }))
    }

    /// Convert the status byte to an `RpcStatus`.
    pub fn rpc_status(&self) -> Option<RpcStatus> {
        RpcStatus::from_u8(self.status)
    }
}

// ── Push ─────────────────────────────────────────────────────────────────────

/// A decoded server-push event frame (sent on QUIC uni-streams).
#[derive(Debug, Clone)]
pub struct PushFrame {
    pub event_type: u16,
    pub payload: Bytes,
}

impl PushFrame {
    /// Encode this push frame into a byte buffer.
    pub fn encode(&self) -> Bytes {
        let mut buf = BytesMut::with_capacity(PUSH_HEADER_SIZE + self.payload.len());
        buf.put_u16(self.event_type);
        buf.put_u32(self.payload.len() as u32);
        buf.put(self.payload.clone());
        buf.freeze()
    }

    /// Decode a push frame from a byte buffer.
    pub fn decode(buf: &mut BytesMut) -> Result<Option<Self>, RpcError> {
        if buf.len() < PUSH_HEADER_SIZE {
            return Ok(None);
        }

        let payload_len =
            u32::from_be_bytes([buf[2], buf[3], buf[4], buf[5]]) as usize;

        if payload_len > MAX_PAYLOAD_SIZE {
            return Err(RpcError::PayloadTooLarge {
                size: payload_len,
                max: MAX_PAYLOAD_SIZE,
            });
        }

        let total = PUSH_HEADER_SIZE + payload_len;
        if buf.len() < total {
            return Ok(None);
        }

        let event_type = buf.get_u16();
        let _len = buf.get_u32();
        let payload = buf.split_to(payload_len).freeze();

        Ok(Some(Self {
            event_type,
            payload,
        }))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn request_roundtrip() {
        let frame = RequestFrame {
            method_id: 42,
            request_id: 1001,
            payload: Bytes::from_static(b"hello"),
        };
        let encoded = frame.encode();
        let mut buf = BytesMut::from(encoded.as_ref());
        let decoded = RequestFrame::decode(&mut buf).expect("decode").expect("complete");
        assert_eq!(decoded.method_id, 42);
        assert_eq!(decoded.request_id, 1001);
        assert_eq!(decoded.payload, Bytes::from_static(b"hello"));
        assert!(buf.is_empty());
    }

    #[test]
    fn response_roundtrip() {
        let frame = ResponseFrame {
            status: RpcStatus::Ok as u8,
            request_id: 2002,
            payload: Bytes::from_static(b"world"),
        };
        let encoded = frame.encode();
        let mut buf = BytesMut::from(encoded.as_ref());
        let decoded = ResponseFrame::decode(&mut buf).expect("decode").expect("complete");
        assert_eq!(decoded.status, 0);
        assert_eq!(decoded.request_id, 2002);
        assert_eq!(decoded.payload, Bytes::from_static(b"world"));
    }

    #[test]
    fn push_roundtrip() {
        let frame = PushFrame {
            event_type: 7,
            payload: Bytes::from_static(b"event-data"),
        };
        let encoded = frame.encode();
        let mut buf = BytesMut::from(encoded.as_ref());
        let decoded = PushFrame::decode(&mut buf).expect("decode").expect("complete");
        assert_eq!(decoded.event_type, 7);
        assert_eq!(decoded.payload, Bytes::from_static(b"event-data"));
    }

    #[test]
    fn incomplete_request_returns_none() {
        let mut buf = BytesMut::from(&[0u8; 5][..]);
        assert!(RequestFrame::decode(&mut buf).expect("no error").is_none());
    }

    #[test]
    fn payload_too_large_rejected() {
        // Craft a request header with payload_len = MAX + 1.
        let mut buf = BytesMut::new();
        buf.put_u16(1);
        buf.put_u32(1);
        buf.put_u32((MAX_PAYLOAD_SIZE + 1) as u32);
        let result = RequestFrame::decode(&mut buf);
        assert!(matches!(result, Err(RpcError::PayloadTooLarge { .. })));
    }

    #[test]
    fn empty_payload_request() {
        let frame = RequestFrame {
            method_id: 0,
            request_id: 0,
            payload: Bytes::new(),
        };
        let encoded = frame.encode();
        assert_eq!(encoded.len(), REQUEST_HEADER_SIZE);
        let mut buf = BytesMut::from(encoded.as_ref());
        let decoded = RequestFrame::decode(&mut buf).expect("decode").expect("complete");
        assert!(decoded.payload.is_empty());
    }
}
