use crate::{
    ServerPacket,
    ser::{NetworkReadExt, ReadingError},
};
use pumpkin_data::packet::serverbound::PLAY_PLAYER_ACTION;
use pumpkin_macros::java_packet;
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::version::JavaMinecraftVersion;

use crate::VarInt;

#[java_packet(PLAY_PLAYER_ACTION)]
pub struct SPlayerAction {
    pub status: VarInt,
    pub position: BlockPos,
    pub face: u8,
    pub sequence: VarInt,
}

impl<'a> ServerPacket<'a> for SPlayerAction {
    fn read(
        bytebuf: &mut &'a [u8],
        _protocol_version: &JavaMinecraftVersion,
    ) -> Result<Self, ReadingError> {
        Ok(Self {
            status: bytebuf.get_var_int()?,
            position: BlockPos::from_i64(bytebuf.get_i64_be()?),
            face: bytebuf.get_u8()?,
            sequence: bytebuf.get_var_int()?,
        })
    }
}

#[expect(clippy::doc_markdown)]
pub enum Status {
    /// Sent when the player starts digging a block. If the block was insta-mined or the player is in creative mode, the client will not send `Status` = `FinishedDigging``, and will assume the server completed the destruction. To detect this, it is necessary to calculate the block destruction speed server-side.
    StartedDigging = 0,
    /// Sent when the player lets go of the Mine Block key (default: left click). Face is always set to -Y.
    CancelledDigging,
    /// Sent when the client thinks it is finished.
    FinishedDigging,
    /// Triggered by using the Drop Item key (default: Q) with the modifier to drop the entire selected stack (default: Control or Command, depending on OS). Location is always set to 0/0/0, Face is always set to -Y. Sequence is always set to 0.
    DropItemStack,
    /// Triggered by using the Drop Item key (default: Q). Location is always set to 0/0/0, Face is always set to -Y. Sequence is always set to 0.
    DropItem,
    /// I didn't make that up
    /// Indicates that the currently held item should have its state updated such as eating food, pulling back bows, using buckets, etc. Location is always set to 0/0/0, Face is always set to -Y. Sequence is always set to 0.
    ReleaseItemInUse,
    /// Used to swap or assign an item to the second hand. Location is always set to 0/0/0, Face is always set to -Y. Sequence is always set to 0.
    SwapItem,
    /// Sent when a player is holding a spear and performs a jab attack.
    SpearJab,
}

pub struct InvalidStatus;

impl TryFrom<i32> for Status {
    type Error = InvalidStatus;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::StartedDigging),
            1 => Ok(Self::CancelledDigging),
            2 => Ok(Self::FinishedDigging),
            3 => Ok(Self::DropItemStack),
            4 => Ok(Self::DropItem),
            5 => Ok(Self::ReleaseItemInUse),
            6 => Ok(Self::SwapItem),
            7 => Ok(Self::SpearJab),
            _ => Err(InvalidStatus),
        }
    }
}
