package de.oaa.xxx.games.bdsm;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import com.fasterxml.jackson.databind.ObjectMapper;

import de.oaa.xxx.games.common.aufgaben.Aufgabe;
import de.oaa.xxx.games.common.aufgaben.AufgabenList;
import de.oaa.xxx.games.common.aufgaben.Sperre;
import de.oaa.xxx.games.common.aufgaben.Strafe;
import de.oaa.xxx.games.bdsm.entity.BdsmGameEntity;
import de.oaa.xxx.games.bdsm.sperre.SperreCallback;
import de.oaa.xxx.games.bdsm.sperre.SperrenVerlaengernCallback;

public class BdsmGameDurchfuehren {

    private final AufgabenList aufgabenList;
    private final List<BdsmMitspieler> mitspieler = new ArrayList<>();
    private final List<AktiveSperre> aktiveSperren = new ArrayList<>();

    private final Integer wahrscheinlichkeitSperre;
    private final Integer wahrscheinlichkeitStrafe;

	private int aufgabenProLevel;
	private int level;
	private int aufgabenAufAktuellemLevel;

    public BdsmGameDurchfuehren(BdsmGameEntity entity) throws Exception {
		ObjectMapper objectMapper = new ObjectMapper();
        aufgabenList = objectMapper.readValue(entity.getAufgaben(), AufgabenList.class);
        entity.getMitspieler().forEach(mitspielerEntity -> mitspieler.add(mitspielerEntity.toMitspieler()));
        entity.getAktiveSperren().forEach(sperreEntity -> aktiveSperren.add(sperreEntity.toSperre(mitspieler)));

        wahrscheinlichkeitSperre = entity.getWahrscheinlichkeitSperre();
        wahrscheinlichkeitStrafe = entity.getWahrscheinlichkeitStrafe();

    	this.aufgabenProLevel = entity.getAufgabenProLevel() != null ? entity.getAufgabenProLevel() : 5;
    	this.level = entity.getLevel() != null ? entity.getLevel() : 1;
    	this.aufgabenAufAktuellemLevel = entity.getAufgabenAufAktuellemLevel() != null ? entity.getAufgabenAufAktuellemLevel() : 0;
    }

    public AufgabeAnzeige getNext() {
        checkLevel();
        if (level == 6) {
        	return null;
        }
        int nextInt = new Random().nextInt(1, 100);

        // Sonderfälle: bleiben wie bisher (inkl. eigener interner Fallbacks)
        if (nextInt == 1) {
            AufgabeAnzeige anzeige = findUltimativeStrafe();
            if (anzeige != null) return anzeige;
        } else if (nextInt == 2) {
            AufgabeAnzeige anzeige = findSperreVerlaengern();
            if (anzeige != null) return anzeige;
        } else {
            // Reihenfolge der Kategorien: gewürfelte zuerst, dann die anderen
            List<Supplier<AufgabeAnzeige>> reihenfolge;
            if (nextInt > wahrscheinlichkeitSperre + wahrscheinlichkeitStrafe + 2) {
                reihenfolge = List.of(this::findeAufgabe, this::findeStrafe, this::findeSperre);
            } else if (nextInt > wahrscheinlichkeitSperre + 2) {
                reihenfolge = List.of(this::findeStrafe, this::findeAufgabe, this::findeSperre);
            } else {
                reihenfolge = List.of(this::findeSperre, this::findeStrafe, this::findeAufgabe);
            }
            for (Supplier<AufgabeAnzeige> finder : reihenfolge) {
                AufgabeAnzeige anzeige = finder.get();
                if (anzeige != null) return anzeige;
            }
        }

        // Echtes Fallback: nur wenn wirklich keine Kategorie eine Aufgabe liefert
        BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
        BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
        String text = "Ups, da ist etwas schief gelaufen. Keine potenzielle Aufgabe gefunden. Entweder seid ihr inzwischen so gut weggesperrt, dass wirklich keine Aufgaben mehr zur Verfügung stehen, oder uns ist ein Fehler unterlaufen. {AKTIV} und {PASSIV} überbrücken die Zeit mit ein wenig Petting.";
        AufgabeAnzeige anzeige = new AufgabeAnzeige();
        anzeige.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : "");
        setMitspielerInfo(anzeige, aktiv);
        anzeige.setAufgabeText(getAnzeigeText(text, aktiv != null ? aktiv.getName() : "?", passiv != null ? passiv.getName() : "?"));
        anzeige.setTimer(120);
        return anzeige;
    }

    public void backToLvl5() {
    	this.level = 5;
    	this.aufgabenAufAktuellemLevel = 0;
    }

    public List<AufgabeAnzeige> getFinisher() {
        var list = new ArrayList<AufgabeAnzeige>();
        List.of(GeschlechtEnum.WEIBLICH, GeschlechtEnum.DIVERS, GeschlechtEnum.MAENNLICH).forEach(geschlecht -> {
            mitspieler.stream().filter(m -> geschlecht == m.getGeschlecht()).toList().forEach(cumming -> {
                var partner = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, cumming);
                var finishers = aufgabenList.getFinisher().stream()
                        .filter(finisher -> geschlecht == finisher.getGeschlecht() && finisher.isAufgabePassend(partner, cumming))
                        .toList();
                if (!finishers.isEmpty()) {
                	var aufgabe = finishers.get(new Random().nextInt(finishers.size()));
                    var anzeige = new AufgabeAnzeige();
                    anzeige.setNameAktiverMitspieler(cumming.getName());
                    setMitspielerInfo(anzeige, cumming);
                    anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(),
                            cumming.getName(), partner != null ? partner.getName() : ""));
                    list.add(anzeige);
                } else {
                	var anzeige = new AufgabeAnzeige();
                	anzeige.setNameAktiverMitspieler(cumming.getName());
                	anzeige.setAufgabeText(cumming.getName() + "geht heute leider leer aus...");
                	list.add(anzeige);
                }
            });
        });
        return list;
    }

    private void checkLevel() {
    	if (++aufgabenAufAktuellemLevel >= 1 + aufgabenProLevel) {
            aufgabenAufAktuellemLevel = 0;
            level++;
        }
    }

    private void setMitspielerInfo(AufgabeAnzeige anzeige, BdsmMitspieler aktiv) {
        if (aktiv != null) {
            anzeige.setMitspielerId(aktiv.getId());
            anzeige.setEigenesGeraet(aktiv.isEigenesGeraet());
        }
    }

    private AufgabeAnzeige findUltimativeStrafe() {
        BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
        if (aktiv != null) {
            BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
            if (passiv != null) {
                String text = "{AKTIV}, verschnüre {PASSIV} fachmännisch inkl. KG, Plugs, Knebel, Augenbinde und was dir sonst einfällt. Nutze die Ruhe für was auch immer du möchtest.";
                AufgabeAnzeige anzeige = new AufgabeAnzeige();
                anzeige.setNameAktiverMitspieler(aktiv.getName());
                setMitspielerInfo(anzeige, aktiv);
                anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName()));
                anzeige.setTimer(new Random().nextInt(1800, 7200));
                return anzeige;
            }
        }
        return findeStrafe();
    }

    private AufgabeAnzeige findSperreVerlaengern() {
        if (!aktiveSperren.isEmpty()) {
            AktiveSperre sperre = aktiveSperren.get(new Random().nextInt(aktiveSperren.size()));
            BdsmMitspieler passiv = sperre.getMitspieler();
            BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV, passiv);
            if (aktiv != null) {
                String text = "{AKTIV}, du entscheidest. Sollen alle bestehenden Zeitstrafen von {PASSIV} verlängert werden...?";
                AufgabeAnzeige anzeige = new AufgabeAnzeige();
                anzeige.setAufgabeText(getAnzeigeText(text, aktiv.getName(), passiv.getName()));
                anzeige.setNameAktiverMitspieler(aktiv.getName());
                setMitspielerInfo(anzeige, aktiv);
                SperrenVerlaengernCallback callback = new SperrenVerlaengernCallback();
                callback.setFaktor(new Random().nextInt(2, 4));
                callback.setSpielerId(passiv.getId());
                anzeige.setCallback(callback);
                return anzeige;
            }
        }
        return findeSperre();
    }

    private AufgabeAnzeige findeAufgabe() {
        BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
        if (aktiv != null) {
            BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_PASSIV, aktiv);
            if (passiv != null) {
                List<Aufgabe> list = aufgabenList.getAufgaben().stream()
                        .filter(aufgabe -> aufgabe.isAufgabePassend(level, aktiv, passiv))
                        .collect(Collectors.toList());
                if (!list.isEmpty()) {
                    Aufgabe aufgabe = list.get(new Random().nextInt(list.size()));
                    AufgabeAnzeige anzeige = new AufgabeAnzeige();
                    anzeige.setNameAktiverMitspieler(aktiv.getName());
                    setMitspielerInfo(anzeige, aktiv);
                    anzeige.setAufgabeText(getAnzeigeText(aufgabe.getText(), aktiv.getName(), passiv.getName()));
                    if (aufgabe.getSekundenVon() != null) {
                        if (aufgabe.getSekundenBis() != null) {
                            anzeige.setTimer(new Random().nextInt(aufgabe.getSekundenVon(), aufgabe.getSekundenBis()));
                        } else {
                            anzeige.setTimer(aufgabe.getSekundenVon());
                        }
                    }
                    return anzeige;
                }
            }
        }
        return null;
    }

    private AufgabeAnzeige findeStrafe() {
        BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
        if (aktiv != null) {
            BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
            if (passiv != null) {
                List<Strafe> list = aufgabenList.getStrafen().stream()
                        .filter(strafe -> strafe.isAufgabePassend(level, aktiv, passiv))
                        .collect(Collectors.toList());
                if (!list.isEmpty()) {
                    Strafe strafe = list.get(new Random().nextInt(list.size()));
                    AufgabeAnzeige anzeige = new AufgabeAnzeige();
                    anzeige.setNameAktiverMitspieler(aktiv.getName());
                    setMitspielerInfo(anzeige, aktiv);
                    anzeige.setAufgabeText(getAnzeigeText(strafe.getText(), aktiv.getName(), passiv.getName()));
                    if (strafe.getSekundenVon() != null) {
                        if (strafe.getSekundenBis() != null) {
                            anzeige.setTimer(new Random().nextInt(strafe.getSekundenVon(), strafe.getSekundenBis()));
                        } else {
                            anzeige.setTimer(strafe.getSekundenVon());
                        }
                    }
                    return anzeige;
                }
            }
        }
        return null;
    }

    private AufgabeAnzeige findeSperre() {
        BdsmMitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
        if (aktiv != null) {
            BdsmMitspieler passiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_PASSIV, aktiv);
            if (passiv != null) {
                List<Sperre> list = aufgabenList.getSperren().stream()
                        .filter(sperre -> sperre.isAufgabePassend(passiv))
                        .collect(Collectors.toList());
                if (!list.isEmpty()) {
                    Sperre sperre = list.get(new Random().nextInt(list.size()));
                    AufgabeAnzeige anzeige = new AufgabeAnzeige();
                    anzeige.setNameAktiverMitspieler(aktiv.getName());
                    setMitspielerInfo(anzeige, aktiv);
                    anzeige.setAufgabeText(getAnzeigeText(sperre.getText(), aktiv.getName(), passiv.getName()));
                    SperreCallback callback = new SperreCallback();
                    callback.setSperreId(sperre.getSperreId());
                    callback.setSpielerId(passiv.getId());
                    callback.setReleaseText(getAnzeigeText(sperre.getReleaseText(), aktiv.getName(), passiv.getName()));
                    anzeige.setCallback(callback);
                    return anzeige;
                }
            }
        }
        return null;
    }

    private String getAnzeigeText(String textMitPlatzhaltern, String nameAktiv, String namePassiv) {
        return textMitPlatzhaltern.replace("{AKTIV}", nameAktiv).replace("{PASSIV}", namePassiv);
    }

    private BdsmMitspieler findeMitspielerMitRolle(RolleEnum rolle) {
        List<BdsmMitspieler> list = mitspieler.stream()
                .filter(m -> m.getRollen().contains(rolle))
                .toList();
        return list.isEmpty() ? null : list.get(new Random().nextInt(list.size()));
    }

    private BdsmMitspieler findeMitspielerMitRolle(RolleEnum rolle, BdsmMitspieler gegenspieler) {
        if (gegenspieler == null) return findeMitspielerMitRolle(rolle);
        List<BdsmMitspieler> list = mitspieler.stream()
                .filter(m -> m != gegenspieler)
                .filter(m -> m.isPassenderSpielpartner(gegenspieler))
                .filter(m -> m.getRollen().contains(rolle))
                .toList();
        return list.isEmpty() ? null : list.get(new Random().nextInt(list.size()));
    }

    public int getAufgabenAufAktuellemLevel() {
		return aufgabenAufAktuellemLevel;
	}

    public int getLevel() {
		return level;
	}
}
