package de.oaa.xxx.session;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.oaa.xxx.session.aufgaben.Aufgabe;
import de.oaa.xxx.session.aufgaben.AufgabenList;
import de.oaa.xxx.session.aufgaben.Sperre;
import de.oaa.xxx.session.aufgaben.Strafe;
import de.oaa.xxx.session.entity.SessionEntity;
import de.oaa.xxx.session.sperre.SperreCallback;
import de.oaa.xxx.session.sperre.SperrenVerlaengernCallback;

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

public class SessionDurchfuehren {

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

    private final Integer wahrscheinlichkeitSperre;
    private final Integer wahrscheinlichkeitStrafe;

    private final Integer aufgabenProLevel;
    private Integer level;
    private Integer aufgabenAufAktuellemLevel;

    public SessionDurchfuehren(SessionEntity 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();

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

    public AufgabeAnzeige getNext() {
        checkLevel();
        AufgabeAnzeige anzeige = null;
        int nextInt = new Random().nextInt(1, 100);
        if (nextInt == 1) {
            anzeige = findUltimativeStrafe();
        } else if (nextInt == 2) {
            anzeige = findSperreVerlaengern();
        } else if (nextInt > wahrscheinlichkeitSperre + wahrscheinlichkeitStrafe + 2) {
            anzeige = findeAufgabe();
        } else if (nextInt > wahrscheinlichkeitSperre + 2) {
            anzeige = findeStrafe();
        } else {
            anzeige = findeSperre();
        }
        if (anzeige == null) {
            Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
            Mitspieler 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.";
            anzeige = new AufgabeAnzeige();
            anzeige.setNameAktiverMitspieler(aktiv != null ? aktiv.getName() : "");
            anzeige.setAufgabeText(getAnzeigeText(text, aktiv != null ? aktiv.getName() : "?", passiv != null ? passiv.getName() : "?"));
            anzeige.setTimer(120);
        }
        return anzeige;
    }

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

    private AufgabeAnzeige findUltimativeStrafe() {
        Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
        if (aktiv != null) {
            Mitspieler 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());
                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()));
            Mitspieler passiv = sperre.getMitspieler();
            Mitspieler 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());
                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() {
        Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.AUFGABE_AKTIV);
        if (aktiv != null) {
            Mitspieler 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());
                    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 findeStrafe();
    }

    private AufgabeAnzeige findeStrafe() {
        Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
        if (aktiv != null) {
            Mitspieler 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());
                    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 findeSperre();
    }

    private AufgabeAnzeige findeSperre() {
        Mitspieler aktiv = findeMitspielerMitRolle(RolleEnum.BESTRAFUNG_AKTIV);
        if (aktiv != null) {
            Mitspieler 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());
                    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 Mitspieler findeMitspielerMitRolle(RolleEnum rolle) {
        List<Mitspieler> list = mitspieler.stream()
                .filter(m -> m.getRollen().contains(rolle))
                .toList();
        return list.isEmpty() ? null : list.get(new Random().nextInt(list.size()));
    }

    private Mitspieler findeMitspielerMitRolle(RolleEnum rolle, Mitspieler gegenspieler) {
        if (gegenspieler == null) return findeMitspielerMitRolle(rolle);
        List<Mitspieler> 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()));
    }
}
