/*
 * © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
 */

package com.tiedup.remake.rig.network;

import java.util.function.Supplier;

import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.network.NetworkDirection;
import net.minecraftforge.network.NetworkEvent;

import com.tiedup.remake.rig.TiedUpRigConstants;
import com.tiedup.remake.rig.anim.client.Layer;

/**
 * Packet S→C : déclenche une animation one-shot sur une entité distante.
 *
 * <p>Utilisé pour les animations cinematic serveur-authoritatives (NPC capture
 * grab, hit stun, death). Le client résout l'entité par ID, récupère son
 * {@code LivingEntityPatch} via {@code TiedUpCapabilities}, et appelle
 * {@code animator.playAnimation(anim, transitionTime)} avec la priorité donnée.
 *
 * <p>Le server-side dispatch (méthode {@code LivingEntityPatch.playAnimationSync})
 * sera implémenté en P3-12. Ici on pose juste l'infra packet + handler stub
 * qui log et termine.
 *
 * @param entityId        ID runtime de l'entité ciblée (Entity.getId())
 * @param animId          ResourceLocation de l'animation à jouer
 * @param transitionTime  durée de blend en secondes (typiquement 0.15F = 3 ticks)
 * @param priorityOrdinal ordinal de Layer.Priority (LOWEST=0...HIGHEST=4),
 *                        transmis sous forme byte pour compacité
 */
public record PacketPlayRigAnim(
    int entityId,
    ResourceLocation animId,
    float transitionTime,
    byte priorityOrdinal
) {
    public static final int PRIORITY_COUNT = Layer.Priority.values().length;

    /** Factory avec Layer.Priority (plus expressif qu'un byte direct). */
    public static PacketPlayRigAnim of(int entityId, ResourceLocation animId, float transitionTime, Layer.Priority priority) {
        return new PacketPlayRigAnim(entityId, animId, transitionTime, (byte) priority.ordinal());
    }

    public Layer.Priority priority() {
        // Guard out-of-range si un peer malveillant envoie un byte invalide.
        // Note : priorityOrdinal & 0xFF donne unsigned [0,255] donc idx < 0 est
        // impossible ici — seul le bound haut est à tester (SMELL-002).
        int idx = priorityOrdinal & 0xFF;
        if (idx >= PRIORITY_COUNT) {
            TiedUpRigConstants.LOGGER.warn(
                "[PacketPlayRigAnim] priority ordinal out of range ({}), falling back to LOWEST. Possible protocol mismatch.",
                idx
            );
            return Layer.Priority.LOWEST; // fallback safe
        }
        return Layer.Priority.values()[idx];
    }

    public void encode(FriendlyByteBuf buf) {
        buf.writeVarInt(this.entityId);
        buf.writeResourceLocation(this.animId);
        buf.writeFloat(this.transitionTime);
        buf.writeByte(this.priorityOrdinal);
    }

    public static PacketPlayRigAnim decode(FriendlyByteBuf buf) {
        int entityId = buf.readVarInt();
        ResourceLocation animId = buf.readResourceLocation();
        float transitionTime = buf.readFloat();
        byte priorityOrdinal = buf.readByte();
        return new PacketPlayRigAnim(entityId, animId, transitionTime, priorityOrdinal);
    }

    /**
     * Handler client — stub P3-11. Le corps complet (resolve entity, lookup
     * patch, playAnimation) viendra en P3-12.
     *
     * <p>La logique client-only est isolée dans {@link ClientHandler} pour que
     * le classloader serveur ne résolve jamais {@code Minecraft.*} (sera
     * référencé en P3-12). Voir review transversale Phase 3 (reco #3).
     */
    public static void handleOnClient(PacketPlayRigAnim pkt, Supplier<NetworkEvent.Context> ctx) {
        NetworkEvent.Context context = ctx.get();

        // Direction guard : ce packet est S→C uniquement. ModNetwork.reg() helper
        // n'enforce pas NetworkDirection, donc un client malveillant pourrait
        // spoofer ce packet vers le serveur. Guard défensif (voir P3-11 review
        // RISK-001 — fix systémique du helper reg() tracé en backlog séparé).
        if (context.getDirection() != NetworkDirection.PLAY_TO_CLIENT) {
            TiedUpRigConstants.LOGGER.warn(
                "[PacketPlayRigAnim] rejected non-S→C packet direction: {} (possible spoofing attempt)",
                context.getDirection()
            );
            context.setPacketHandled(true);
            return;
        }

        // Délégation client-only. L'inner class n'est class-loadée que si cette
        // branche s'exécute, donc safe côté serveur même si Minecraft.* y est
        // référencé en P3-12.
        context.enqueueWork(() -> ClientHandler.apply(pkt));
        context.setPacketHandled(true);
    }

    /**
     * Handler client-only. Encapsule toute dépendance {@code Minecraft.*} pour
     * éviter un NoClassDefFoundError serveur-side si la JVM resolve les refs
     * dist-only du handler principal.
     */
    private static final class ClientHandler {
        private ClientHandler() {}

        @OnlyIn(Dist.CLIENT)
        static void apply(PacketPlayRigAnim pkt) {
            TiedUpRigConstants.LOGGER.debug(
                "[PacketPlayRigAnim] received (stub P3-11): entityId={}, animId={}, transition={}s, priority={}",
                pkt.entityId, pkt.animId, pkt.transitionTime, pkt.priority()
            );
            // TODO P3-12 : Minecraft.getInstance().level.getEntity(pkt.entityId())
            //              + LivingEntityPatch lookup + animator.playAnimation
        }
    }
}
