package com.tiedup.remake.compat.mca.personality;

import com.tiedup.remake.compat.mca.MCACompat;
import com.tiedup.remake.compat.mca.capability.MCAKidnappedCapability;
import com.tiedup.remake.core.TiedUpMod;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.world.entity.LivingEntity;
import org.jetbrains.annotations.Nullable;

/**
 * Manages MCA personality and TiedUp trait access for villagers.
 *
 * <p>Uses reflection to access MCA's VillagerBrain.getPersonality() method,
 * since MCA is an optional dependency.
 *
 * <p>Also manages TiedUp traits stored in the villager's capability.
 */
public class MCAPersonalityManager {

    private static final MCAPersonalityManager INSTANCE =
        new MCAPersonalityManager();

    /** Cache personality per entity (weak refs to allow GC) */
    private final Map<LivingEntity, MCAPersonality> personalityCache =
        new WeakHashMap<>();

    /** Cached reflection objects */
    private Method getVillagerBrainMethod;
    private Method getPersonalityMethod;
    private boolean reflectionInitialized = false;

    public static MCAPersonalityManager getInstance() {
        return INSTANCE;
    }

    private MCAPersonalityManager() {}

    // MCA PERSONALITY ACCESS (Reflection)

    /**
     * Get the MCA personality for a villager.
     * Uses reflection to access MCA's VillagerBrain.getPersonality().
     *
     * @param entity The MCA villager entity
     * @return The personality, or UNKNOWN if not available
     */
    public MCAPersonality getPersonality(LivingEntity entity) {
        if (entity == null || !MCACompat.isMCAVillager(entity)) {
            return MCAPersonality.UNKNOWN;
        }

        // Check cache first
        MCAPersonality cached = personalityCache.get(entity);
        if (cached != null) {
            return cached;
        }

        // Try to get via reflection
        String personalityId = getPersonalityIdViaReflection(entity);
        MCAPersonality personality = MCAPersonality.fromMcaId(personalityId);

        // Cache result (even if UNKNOWN, to avoid repeated reflection failures)
        personalityCache.put(entity, personality);

        return personality;
    }

    /**
     * Get raw personality ID string from MCA via reflection.
     *
     * <p>MCA structure:
     * <ul>
     *   <li>entity.getVillagerBrain() -&gt; VillagerBrain</li>
     *   <li>villagerBrain.getPersonality() -&gt; Personality enum</li>
     *   <li>personality.name() -&gt; String</li>
     * </ul>
     */
    @Nullable
    private String getPersonalityIdViaReflection(LivingEntity entity) {
        try {
            initializeReflection(entity);

            if (getVillagerBrainMethod == null) {
                return null;
            }

            // Get VillagerBrain
            Object brain = getVillagerBrainMethod.invoke(entity);
            if (brain == null) {
                return null;
            }

            // Get personality from brain
            if (getPersonalityMethod == null) {
                // Find getPersonality method on brain
                for (Method m : brain.getClass().getMethods()) {
                    if (
                        m.getName().equals("getPersonality") &&
                        m.getParameterCount() == 0
                    ) {
                        getPersonalityMethod = m;
                        break;
                    }
                }
            }

            if (getPersonalityMethod == null) {
                return null;
            }

            Object personality = getPersonalityMethod.invoke(brain);
            if (personality != null && personality.getClass().isEnum()) {
                return personality.toString().toLowerCase();
            }
        } catch (Exception e) {
            TiedUpMod.LOGGER.debug(
                "[MCA Personality] Failed to get personality for {}: {}",
                entity.getName().getString(),
                e.getMessage()
            );
        }

        return null;
    }

    private void initializeReflection(LivingEntity entity) {
        if (reflectionInitialized) return;
        reflectionInitialized = true;

        try {
            // MCA villagers implement VillagerLike which has getVillagerBrain()
            getVillagerBrainMethod = entity
                .getClass()
                .getMethod("getVillagerBrain");
            TiedUpMod.LOGGER.debug(
                "[MCA Personality] Found getVillagerBrain method"
            );
        } catch (NoSuchMethodException e) {
            TiedUpMod.LOGGER.debug(
                "[MCA Personality] getVillagerBrain not found"
            );
        }
    }

    // TIEDUP TRAIT ACCESS (Capability)

    /**
     * Get the TiedUp trait for a villager.
     * Stored in the villager's MCAKidnappedCapability.
     *
     * @param entity The MCA villager entity
     * @return The trait, or NONE if not set
     */
    public TiedUpTrait getTrait(LivingEntity entity) {
        if (entity == null || !MCACompat.isMCAVillager(entity)) {
            return TiedUpTrait.NONE;
        }

        return entity
            .getCapability(MCACompat.MCA_KIDNAPPED)
            .map(MCAKidnappedCapability::getTrait)
            .orElse(TiedUpTrait.NONE);
    }

    /**
     * Set the TiedUp trait for a villager.
     *
     * @param entity The MCA villager entity
     * @param trait The trait to set
     */
    public void setTrait(LivingEntity entity, TiedUpTrait trait) {
        if (entity == null || !MCACompat.isMCAVillager(entity)) {
            return;
        }

        entity
            .getCapability(MCACompat.MCA_KIDNAPPED)
            .ifPresent(cap -> {
                cap.setTrait(trait);
                TiedUpMod.LOGGER.debug(
                    "[MCA Personality] Set trait {} for {}",
                    trait.getId(),
                    entity.getName().getString()
                );
            });
    }

    // COMBINED CALCULATIONS

    /**
     * Get combined struggle multiplier (personality * trait).
     */
    public float getCombinedStruggle(LivingEntity entity) {
        MCAPersonality personality = getPersonality(entity);
        TiedUpTrait trait = getTrait(entity);
        return TiedUpTrait.getCombinedStruggle(personality, trait);
    }

    /**
     * Get combined compliance multiplier (personality * trait).
     */
    public float getCombinedCompliance(LivingEntity entity) {
        MCAPersonality personality = getPersonality(entity);
        TiedUpTrait trait = getTrait(entity);
        return TiedUpTrait.getCombinedCompliance(personality, trait);
    }

    /**
     * Get combined mood change when tied (personality base + trait modifier).
     */
    public int getCombinedMoodTied(LivingEntity entity) {
        MCAPersonality personality = getPersonality(entity);
        TiedUpTrait trait = getTrait(entity);
        return TiedUpTrait.getCombinedMoodTied(personality, trait);
    }

    /**
     * Check if this villager generally enjoys being restrained
     * (based on combined personality and trait).
     */
    public boolean enjoysBondage(LivingEntity entity) {
        return getCombinedMoodTied(entity) > 0;
    }

    // CACHE MANAGEMENT

    /**
     * Clear personality cache for an entity.
     * Call this when entity is removed or personality might have changed.
     */
    public void clearCache(LivingEntity entity) {
        personalityCache.remove(entity);
    }

    /**
     * Clear entire personality cache.
     */
    public void clearAllCache() {
        personalityCache.clear();
    }
}
