package org.cneko.justarod.mixin;

import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.component.type.FoodComponent;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.Registries;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.World;
import org.cneko.justarod.effect.JREffects;
import org.cneko.justarod.entity.*;
import org.cneko.justarod.packet.JRSyncPayload;
import org.cneko.justarod.property.JRProperty;
import org.cneko.justarod.property.JRRegistry;
import org.cneko.justarod.quirks.JRQuirks;
import org.cneko.toneko.common.mod.entities.INeko;
import org.cneko.toneko.common.mod.quirks.Quirk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.ArrayList;
import java.util.List;

import static org.cneko.justarod.JRAttributes.Companion;

@SuppressWarnings({"AddedMixinMembersNamePattern", "DataFlowIssue"})
@Mixin(PlayerEntity.class)
public abstract class PlayerMixin implements Powerable, Pregnant, BDSMable {

    @Unique
    private short slowTick = 10;


    @Unique
    private double power = 0;

    @Unique
    private int ballMouth = 0;
    @Unique
    private int electricShock = 0;
    @Unique
    private int bundled = 0;
    @Unique
    private int eyePatch = 0;
    @Unique
    private int earplug = 0;
    @Unique
    private int handcuffed = 0;
    @Unique
    private int shackled = 0;
    @Unique
    private int noMatingPlz = 0;

    @Unique
    private boolean male = false;
    @Unique
    private boolean female = true;
    @Unique
    private int pregnant = 0;
    @Unique
    private EntityType<?> childrenType = null;
    @Unique
    private int menstruation = 0;
    @Unique
    private int menstruationComfort = 0;
    @Unique
    private boolean sterilization = false;
    @Unique
    private boolean ectopicPregnancy = false;
    @Unique
    private int aids = 0;
    @Unique
    private boolean immune2Aids = false;
    @Unique
    private boolean hydatidiformMole = false;
    @Unique
    private int babyCount = 0;
    @Unique
    private int hpv = 0;
    @Unique
    private boolean immune2HPV = false;
    @Unique
    private boolean hasUterus = false;
    @Unique
    private boolean isPCOS = false;
    @Unique
    private int brithControlling = 0;
    @Unique
    private int ovarianCancer = 0;
    @Unique
    private int breastCancer = 0;
    @Unique
    private int syphilis = 0;
    @Unique
    private int excretion = 0;
    @Unique
    private int urination = 0;
    @Unique
    private int uterineCold = 0;
    @Unique
    private int urethritis = 0;
    @Unique
    private int prostatitis = 0;
    @Unique
    private int hemorrhoids = 0;
    @Unique
    private int paronychia = 0;
    @Unique
    private boolean nailRemoved = false;
    @Unique
    private int nailRegrowTime = 0;
    @Unique
    private boolean amputated = false;
    @Unique
    private float parthenogenesisVariance = 0;

    @Unique
    private boolean orchiectomy = false;

    @Unique
    private float milk = 0;
    @Unique
    private int mastitis = 0;
    @Unique
    private int lactationStimulation = 0;



    @Override
    public double getPower() {
        return power;
    }

    @Override
    public void setPower(double power) {
        this.power = power;
    }

    @Override
    public boolean canPowerUp() {
        PlayerEntity player = (PlayerEntity) (Object) this;
        return player.getHungerManager().getFoodLevel() >= 3;
    }

    @Override
    public int getBallMouth() {
        return ballMouth;
    }

    @Override
    public void setBallMouth(int ballMouth) {
        this.ballMouth = ballMouth;
    }

    @Override
    public int getElectricShock() {
        return electricShock;
    }

    @Override
    public void setElectricShock(int electricShock) {
        this.electricShock = electricShock;
    }

    @Override
    public int getBundled() {
        return bundled;
    }

    @Override
    public void setBundled(int bundled) {
        this.bundled = bundled;
    }

    @Override
    public int getEyePatch() {
        return eyePatch;
    }

    @Override
    public void setEyePatch(int eyePatch) {
        this.eyePatch = eyePatch;
    }

    @Override
    public void setEarplug(int earplug) {
        this.earplug = earplug;
    }

    @Override
    public int getEarplug() {
        return earplug;
    }

    @Override
    public void setHandcuffed(int handcuffed) {
        this.handcuffed = handcuffed;
    }

    @Override
    public int getHandcuffed() {
        return handcuffed;
    }

    @Override
    public void setShackled(int shackled) {
        this.shackled = shackled;
    }
    @Override
    public int getShackled() {
        return shackled;
    }

    @Override
    public void setNoMatingPlz(int noMatingPlz) {
        this.noMatingPlz = noMatingPlz;
    }

    @Override
    public int getNoMatingPlz() {
        return noMatingPlz;
    }

    @Override
    public boolean isFemale() {
        return female;
    }

    @Override
    public void setFemale(boolean female) {
        this.female = female;
    }

    @Override
    public boolean isMale() {
        return male;
    }

    @Override
    public void setMale(boolean male) {
        this.male = male;
    }

    @Override
    public int getPregnant() {
        return pregnant;
    }

    @Override
    public void setPregnant(int pregnant) {
        this.pregnant = pregnant;
    }

    @Override
    public EntityType<?> getChildrenType() {
        return childrenType;
    }

    @Override
    public void setChildrenType(EntityType<?> childrenType) {
        this.childrenType = childrenType;
    }

    @Override
    public void setMenstruationComfort(int time) {
        this.menstruationComfort = time;
    }

    @Override
    public int getMenstruationComfort() {
        return menstruationComfort;
    }

    @Override
    public boolean isSterilization() {
        return sterilization;
    }

    @Override
    public void setSterilization(boolean sterilization) {
        this.sterilization = sterilization;
    }

    @Override
    public boolean isEctopicPregnancy() {
        return ectopicPregnancy;
    }

    @Override
    public void setEctopicPregnancy(boolean ectopicPregnancy) {
        this.ectopicPregnancy = ectopicPregnancy;
    }

    @Override
    public int getAids() {
        return aids;
    }

    @Override
    public void setAids(int aids) {
        this.aids = aids;
    }

    @Override
    public boolean isImmune2Aids() {
        return immune2Aids;
    }

    @Override
    public void setImmune2Aids(boolean immune2Aids) {
        this.immune2Aids = immune2Aids;
    }

    @Override
    public boolean isHydatidiformMole() {
        return hydatidiformMole;
    }

    @Override
    public void setHydatidiformMole(boolean hydatidiformMole) {
        this.hydatidiformMole = hydatidiformMole;
    }

    @Override
    public int getBabyCount() {
        return babyCount;
    }

    @Override
    public void setBabyCount(int babyCount) {
        this.babyCount = babyCount;
    }

    @Override
    public void setHPV(int time) {
        this.hpv = time;
    }

    @Override
    public int getHPV() {
        return hpv;
    }

    @Override
    public boolean isImmune2HPV() {
        return immune2HPV;
    }

    @Override
    public void setImmune2HPV(boolean immune2HPV) {
        this.immune2HPV = immune2HPV;
    }

    public boolean hasUterus() {
        return hasUterus;
    }

    @Override
    public void setHasUterus(boolean hasUterus) {
        this.hasUterus = hasUterus;
    }

    @Override
    public boolean isPCOS() {
        return isPCOS;
    }

    @Override
    public void setPCOS(boolean PCOS) {
        isPCOS = PCOS;
    }

    @Override
    public int getBrithControlling() {
        return brithControlling;
    }

    @Override
    public void setBrithControlling(int brithControlling) {
        this.brithControlling = brithControlling;
    }

    @Override
    public void setOvarianCancer(int ovarianCancer) {
        this.ovarianCancer = ovarianCancer;
    }

    @Override
    public int getOvarianCancer() {
        return ovarianCancer;
    }

    @Override
    public int getBreastCancer() {
        return breastCancer;
    }

    @Override
    public void setBreastCancer(int breastCancer) {
        this.breastCancer = breastCancer;
    }

    @Override
    public void setSyphilis(int syphilis) {
        this.syphilis = syphilis;
    }

    @Override
    public int getSyphilis() {
        return syphilis;
    }

    @Override
    public void setExcretion(int excretion) {
        this.excretion = excretion;
    }

    @Override
    public int getExcretion() {
        return excretion;
    }

    @Override
    public void setUrination(int urination) {
        this.urination = urination;
    }

    @Override
    public int getUrination() {
        return urination;
    }

    @Override
    public void setUterineCold(int uterineCold) {
        this.uterineCold = uterineCold;
    }

    @Override
    public int getUterineCold() {
        return uterineCold;
    }

    @Override
    public int getUrethritis() {
        return urethritis;
    }

    @Override
    public void setUrethritis(int urethritis) {
        this.urethritis = urethritis;
    }

    @Override
    public boolean isAmputated() {
        return amputated;
    }

    @Override
    public void setAmputated(boolean amputated) {
        this.amputated = amputated;
    }

    @Override
    public boolean isOrchiectomy() {
        return orchiectomy;
    }

    @Override
    public void setOrchiectomy(boolean orchiectomy) {
        this.orchiectomy = orchiectomy;
    }

    @Override
    public int getProstatitis() {
        return prostatitis;
    }

    @Override
    public void setProstatitis(int prostatitis) {
        this.prostatitis = prostatitis;
    }

    @Override
    public float getParthenogenesisVariance() {
        return parthenogenesisVariance;
    }

    @Override
    public void setParthenogenesisVariance(float parthenogenesisVariance) {
        this.parthenogenesisVariance = parthenogenesisVariance;
    }

    @Override
    public int getHemorrhoids() {
        return hemorrhoids;
    }

    @Override
    public void setHemorrhoids(int hemorrhoids) {
        this.hemorrhoids = hemorrhoids;
    }

    @Override
    public int getParonychia() {
        return paronychia;
    }

    @Override
    public void setParonychia(int paronychia) {
        this.paronychia = paronychia;
    }

    @Override
    public boolean isNailRemoved() {
        return nailRemoved;
    }

    @Override
    public void setNailRemoved(boolean nailRemoved) {
        this.nailRemoved = nailRemoved;
    }

    @Override
    public int getNailRegrowTime() {
        return nailRegrowTime;
    }

    @Override
    public void setNailRegrowTime(int nailRegrowTime) {
        this.nailRegrowTime = nailRegrowTime;
    }

    @Unique
    private boolean hymen = true;

    @Override
    public boolean hasHymen() {
        return hymen;
    }
    @Override
    public void setHasHymen(boolean hasHymen) {
        this.hymen = hasHymen;
    }

    @Unique
    private boolean imperforateHymen = false;

    @Override
    public void setImperforateHymen(boolean imperforateHymen) {
        this.imperforateHymen = imperforateHymen;
    }

    @Override
    public boolean isImperforateHymen() {
        return imperforateHymen;
    }

    @Unique
    private boolean protogynyEnabled = false;
    @Unique
    private boolean isUndergoingProtogyny = false;
    @Unique
    private int protogynyProgress = 0;
    @Override
    public boolean isProtogynyEnabled() {
        return protogynyEnabled;
    }
    @Override
    public void setProtogynyEnabled(boolean protogynyEnabled) {
        this.protogynyEnabled = protogynyEnabled;
    }
    @Override
    public boolean isUndergoingProtogyny() {
        return isUndergoingProtogyny;
    }
    @Override
    public void setUndergoingProtogyny(boolean undergoingProtogyny) {
        isUndergoingProtogyny = undergoingProtogyny;
    }
    @Override
    public int getProtogynyProgress() {
        return protogynyProgress;
    }
    @Override
    public void setProtogynyProgress(int protogynyProgress) {
        this.protogynyProgress = protogynyProgress;
    }

    @Unique
    private int ovarianClock = 0;
    @Unique
    private float uterineThickness = 0;
    @Unique
    private float endoE2 = 0;
    @Unique
    private float endoP = 0;
    @Unique
    private float endoT = 0;
    @Unique
    private float exoE2 = 0;
    @Unique
    private float exoP = 0;
    @Unique
    private float exoT = 0;

    @Override
    public int getOvarianClock() {
        return ovarianClock;
    }
    @Override
    public void setOvarianClock(int ovarianClock) {
        this.ovarianClock = ovarianClock;
    }

    @Override
    public float getUterineThickness() {
        return uterineThickness;
    }

    @Override
    public void setUterineThickness(float uterineThickness) {
        this.uterineThickness = uterineThickness;
    }

    @Override
    public float getEndoE2() {
        return endoE2;
    }

    @Override
    public void setEndoE2(float endoE2) {
        this.endoE2 = endoE2;
    }

    @Override
    public float getEndoP() {
        return endoP;
    }

    @Override
    public void setEndoP(float endoP) {
        this.endoP = endoP;
    }

    @Override
    public float getEndoT() {
        return endoT;
    }

    @Override
    public void setEndoT(float endoT) {
        this.endoT = endoT;
    }

    @Override
    public float getExoP() {
        return exoP;
    }

    @Override
    public void setExoP(float exoP) {
        this.exoP = exoP;
    }

    @Override
    public float getExoE2() {
        return exoE2;
    }

    @Override
    public void setExoE2(float exoE2) {
        this.exoE2 = exoE2;
    }

    @Override
    public float getExoT() {
        return exoT;
    }

    @Override
    public void setExoT(float exoT) {
        this.exoT = exoT;
    }

    @Unique
    private float exoBlocker = 0;
    @Unique
    private int hrtMtfProgress = 0;
    @Unique
    private int hrtFtmProgress = 0;
    @Unique
    private int vaginalAtrophy = 0;

    @Override
    public float getExoBlocker() {
        return exoBlocker;
    }

    @Override
    public void setExoBlocker(float exoBlocker) {
        this.exoBlocker = exoBlocker;
    }

    @Override
    public int getHrtFtmProgress() {
        return hrtFtmProgress;
    }

    @Override
    public void setHrtFtmProgress(int hrtFtmProgress) {
        this.hrtFtmProgress = hrtFtmProgress;
    }

    @Override
    public int getHrtMtfProgress() {
        return hrtMtfProgress;
    }

    @Override
    public void setHrtMtfProgress(int hrtMtfProgress) {
        this.hrtMtfProgress = hrtMtfProgress;
    }

    @Override
    public int getVaginalAtrophy() {
        return vaginalAtrophy;
    }

    @Override
    public void setVaginalAtrophy(int vaginalAtrophy) {
        this.vaginalAtrophy = vaginalAtrophy;
    }

    @Unique
    private int cataract = 0;

    @Override
    public int getCataract() {
        return cataract;
    }

    @Override
    public void setCataract(int cataract) {
        this.cataract = cataract;
    }

    @Unique
    private int corpusLuteumRupture = 0;
    @Unique
    private boolean severeCorpusLuteumRupture = false;

    @Override
    public int getCorpusLuteumRupture() {
        return corpusLuteumRupture;
    }

    @Override
    public void setCorpusLuteumRupture(int corpusLuteumRupture) {
        this.corpusLuteumRupture = corpusLuteumRupture;
    }

    @Override
    public boolean isSevereCorpusLuteumRupture() {
        return severeCorpusLuteumRupture;
    }

    @Override
    public void setSevereCorpusLuteumRupture(boolean severeCorpusLuteumRupture) {
        this.severeCorpusLuteumRupture = severeCorpusLuteumRupture;
    }

    @Override
    public float getMilk() {
        return milk;
    }

    @Override
    public void setMilk(float milk) {
        this.milk = milk;
    }

    @Override
    public int getMastitis() {
        return mastitis;
    }

    @Override
    public void setMastitis(int mastitis) {
        this.mastitis = mastitis;
    }

    @Override
    public void setLactationStimulation(int lactationStimulation) {
        this.lactationStimulation = lactationStimulation;
    }

    @Override
    public int getLactationStimulation() {
        return lactationStimulation;
    }

    @Unique
    private int urinaryIncontinence = 0;

    @Override
    public int getUrinaryIncontinence() {
        return urinaryIncontinence;
    }

    @Override
    public void setUrinaryIncontinence(int urinaryIncontinence) {
        this.urinaryIncontinence = urinaryIncontinence;
    }

    @Override
    public boolean isYuri() {
        return ((INeko)this).hasQuirk(JRQuirks.Companion.getYURI_QUIRK());
    }

    @Override
    public Entity createBaby() {
        PlayerEntity player = (PlayerEntity) (Object) this;
        var baby = (Entity) getChildrenType().create(player.getWorld());
        if (baby instanceof MobEntity mob) {
            mob.setBaby(true);
            mob.age = -48000;
            if (baby instanceof INeko neko) {
                neko.addOwner(player.getUuid(), new INeko.Owner(new ArrayList<>(), 0));
            }
        }
        baby.setPos(player.getX(), player.getY(), player.getZ());
        return baby;
    }

    @Inject(method = "readCustomDataFromNbt", at = @At("HEAD"))
    public void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) {
        power = this.readPowerFromNbt(nbt);
        this.readPregnantFromNbt(nbt);
        this.readBDSMFromNbt(nbt);
    }

    @Inject(method = "writeCustomDataToNbt", at = @At("HEAD"))
    public void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) {
        this.writePowerToNbt(nbt);
        this.writePregnantToNbt(nbt);
        this.writeBDSMToNbt(nbt);
    }

    @Unique
    private static void syncToClient(ServerPlayerEntity player) {
        List<Object> currentValues = new ArrayList<>();

        // 自动采集所有数据
        for (JRProperty<?> prop : JRRegistry.INSTANCE.getPROPERTIES()) {
            // 在 Kotlin 里声明的 (Pregnant) -> T ，在 Java 里会被编译为 invoke()
            Object value = ((JRProperty<Object>) prop).getGetter().invoke(player);
            currentValues.add(value);
        }

        // 发送给客户端
        ServerPlayNetworking.send(player, new JRSyncPayload(currentValues));
    }

    @Inject(method = "tick",at = @At("HEAD"))
    public void tick(CallbackInfo ci) {
        PlayerEntity player = (PlayerEntity) (Object) this;
        if (slowTick++ >= 10){
            slowTick = 0;
            if (player instanceof ServerPlayerEntity sp) {
                // 同步power
                syncToClient(sp);
                // slowTick区域
                Pregnant.hormoneAndCycleSlowTick(sp);
                Pregnant.yuriSlowTick(player);
            }
        }
        if (player.getWorld() instanceof ServerWorld) {
            Powerable.tickPower(player);
            Pregnant.pregnantTick(player);
            Pregnant.aidsTick(player);
            Pregnant.HPVTick(player);
            Pregnant.ovarianCancerTick(player);
            Pregnant.breastCancerTick(player);
            Pregnant.syphilisTick(player);
            Pregnant.excretionTick(player);
            Pregnant.urinationTick(player);
            Pregnant.uterineColdTick(player);
            Pregnant.amputatedTick(player);
            Pregnant.urethritisTick(player);
            Pregnant.prostatitisTick(player);
            Pregnant.hemorrhoidsTick(player);
            Pregnant.paronychiaTick(player);
            Pregnant.hymenTick(player);
            Pregnant.imperforateHymenTick(player);
            Pregnant.protogynyTick(player);
            Pregnant.cataractTick(player);
            Pregnant.corpusLuteumTriggerTick(player);
            Pregnant.corpusLuteumRuptureTick(player);
            Pregnant.lactationTick(player);
            Pregnant.urinaryIncontinenceTick(player);
            BDSMable.ballMouthTick(player);
            BDSMable.electricShockTick(player);
            BDSMable.bundledTick(player);
            BDSMable.eyePatchTick(player);
            BDSMable.earplugTick(player);
            BDSMable.handcuffedTick(player);
            BDSMable.shackledTick(player);
            BDSMable.noMatingPlzTick(player);
        }
    }

    @Inject(method = "eatFood",at = @At("HEAD"))
    public void eatFood(World world, ItemStack stack, FoodComponent foodComponent, CallbackInfoReturnable<ItemStack> cir) {
        if (stack.isOf(Items.MILK_BUCKET)){
            // 如果有HPV且在3天内
            if (this.getHPV() > 0 && this.getHPV() < 20*60*20*3) {
                this.setHPV(0);
                // 移除HPV效果
                ((PlayerEntity)(Object)this).removeStatusEffect(Registries.STATUS_EFFECT.getEntry(JREffects.Companion.getHPV_EFFECT()));
            }
        }
        if (stack.isOf(Items.ENCHANTED_GOLDEN_APPLE)){
            // 如果有HPV且在6天内
            if (this.getHPV() > 0 && this.getHPV() < 20*60*20*6) {
                this.setHPV(0);
                ((PlayerEntity)(Object)this).removeStatusEffect(Registries.STATUS_EFFECT.getEntry(JREffects.Companion.getHPV_EFFECT()));
            }
        }
    }

    @Inject(method = "createPlayerAttributes", at = @At("RETURN"))
    private static void createPlayerAttributes(CallbackInfoReturnable<DefaultAttributeContainer.Builder> cir) {
        cir.getReturnValue()
                .add(Companion.getPLAYER_LUBRICATING())
                .add(Companion.getGENERIC_MAX_POWER());
    }

}
