#![allow(unused)]

use std::net::Ipv4Addr;

use prost::Message;
use proto::{PacketHead, YSMessage};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum PacketParseError {
    #[error("buf_len={0} is too short")]
    BufferIsTooShort(usize),
    #[error("head magic error")]
    HeadMagicMismatch,
    #[error("body_len={0} is too long")]
    TooLongBodyLen(usize),
    #[error("buf_len={0} is too short, packet_len={1}")]
    BufferExceeded(usize, usize),
    #[error("PacketHead::decode failed")]
    HeadParseFailed,
    #[error("tail magic error 0x{0:X}")]
    TailMagicMismatch(u16),
}

#[derive(Clone)]
pub struct Packet {
    pub cmd_id: u16,
    pub head: PacketHead,
    pub recv_vec: Vec<u8>,
}

impl Packet {
    const MIN_PACKET_LEN: usize = 12;
    const HEAD_MAGIC: u16 = 0x4567;
    const TAIL_MAGIC: u16 = 0x89AB;

    pub fn new(proto: impl YSMessage, head: PacketHead) -> Self {
        Self {
            cmd_id: proto.get_cmd_id(),
            head,
            recv_vec: proto.encode_to_vec(),
        }
    }

    pub fn parse_from_array(buf: &[u8]) -> Result<Self, PacketParseError> {
        let buf_len = buf.len();

        if buf_len < Self::MIN_PACKET_LEN {
            return Err(PacketParseError::BufferIsTooShort(buf_len));
        }

        let head_magic = u16::from_be_bytes(buf[0..2].try_into().unwrap());
        if head_magic != Self::HEAD_MAGIC {
            return Err(PacketParseError::HeadMagicMismatch);
        }

        let cmd_id = u16::from_be_bytes(buf[2..4].try_into().unwrap());
        let head_len = u16::from_be_bytes(buf[4..6].try_into().unwrap()) as usize;
        let body_len = u32::from_be_bytes(buf[6..10].try_into().unwrap()) as usize;

        if body_len > 0x19999999 {
            return Err(PacketParseError::TooLongBodyLen(body_len));
        }

        let packet_len = Self::MIN_PACKET_LEN + head_len + body_len;
        if buf_len < packet_len {
            return Err(PacketParseError::BufferExceeded(buf_len, packet_len));
        }

        let Ok(head) = PacketHead::decode(&buf[10..10 + head_len]) else {
            return Err(PacketParseError::HeadParseFailed);
        };

        let tail_magic = u16::from_be_bytes(
            buf[10 + head_len + body_len..12 + head_len + body_len]
                .try_into()
                .unwrap(),
        );

        if tail_magic != Self::TAIL_MAGIC {
            return Err(PacketParseError::TailMagicMismatch(tail_magic));
        }

        Ok(Self {
            cmd_id,
            head,
            recv_vec: buf[10 + head_len..10 + head_len + body_len].to_vec(),
        })
    }

    pub fn to_vec(&self) -> Vec<u8> {
        let mut output = Vec::with_capacity(
            Self::MIN_PACKET_LEN + self.head.encoded_len() + self.recv_vec.len(),
        );

        let head = self.head.encode_to_vec();
        output.extend(Self::HEAD_MAGIC.to_be_bytes());
        output.extend(self.cmd_id.to_be_bytes());
        output.extend((head.len() as u16).to_be_bytes());
        output.extend((self.recv_vec.len() as u32).to_be_bytes());
        output.extend(head);
        output.extend(&self.recv_vec);
        output.extend(Self::TAIL_MAGIC.to_be_bytes());

        output
    }

    pub fn get_proto<T>(&self) -> Result<T, prost::DecodeError>
    where
        T: prost::Message + Default,
    {
        T::decode(&*self.recv_vec)
    }

    pub fn get_user_ip(&self) -> String {
        Ipv4Addr::from(self.head.user_ip).to_string()
    }
}
