package de.stoermer.xxx.domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import de.stoermer.xxx.domain.aufgaben.AlleLocksVerlaengern;
import de.stoermer.xxx.domain.aufgaben.Aufgabe;
import de.stoermer.xxx.domain.aufgaben.BeglueckungSchablone;
import de.stoermer.xxx.domain.aufgaben.BestrafungSchablone;
import de.stoermer.xxx.domain.aufgaben.LockSchablone;
import de.stoermer.xxx.domain.aufgaben.RumMachen;
import de.stoermer.xxx.domain.aufgaben.UltimativeStrafe;

public class Session {

	private List<Spieler> spieler;
	private int wahrscheinlichkeitSperre; // 0 - 100;
	private int wahrscheinlichkeitStrafe; // 0 - 100;
	
	private List<LockSchablone> sperren;
	private List<BeglueckungSchablone> beglueckungen;
	private List<BestrafungSchablone> strafen;
	
	private int aufgabeAufAktuellenLevel = 0;
	private int aufgabenProLevel = 10;
	private int level = 1;
	
	public Session(List<Spieler> spieler, int wahrscheinlichkeitSperre, int wahrscheinlichkeitStrafe,
			List<Toy> toys, int aufgabenProLevel, SchablonenProvider schablonenProvider) throws InkonsistenterSpielerZustand, InkonsistenterAufgabenZustand {
		this.spieler = spieler;
		this.wahrscheinlichkeitSperre = wahrscheinlichkeitSperre;
		this.wahrscheinlichkeitStrafe = wahrscheinlichkeitStrafe;
		this.sperren = schablonenProvider.getSperren().stream().filter(sperre -> sperre.moeglich(toys)).toList();
		this.beglueckungen = schablonenProvider.getBeglueckungen().stream().filter(sperre -> sperre.moeglich(toys)).toList();
		this.strafen = schablonenProvider.getStrafen().stream().filter(sperre -> sperre.moeglich(toys)).toList();
		this.aufgabenProLevel = aufgabenProLevel;
		
		checkSpieler();
		cehckAufgabe();
	}
	
	private void cehckAufgabe() throws InkonsistenterAufgabenZustand {
		if (sperren.isEmpty() && strafen.isEmpty() && beglueckungen.isEmpty()) {
			throw new InkonsistenterAufgabenZustand("Ein Spiel ohne Aufgaben macht wenig Sinn...");
		}
	}

	private void checkSpieler() throws InkonsistenterSpielerZustand {
		for (Spieler spieler : spielerMitRolle(Rolle.BESTRAFTE_PERSON)) {
			if (spielerMitRolleAußer(Rolle.BESTRAFENDE_PERSON, spieler) == null) {
				throw new InkonsistenterSpielerZustand(spieler.getName() + " verfügt über keine Person, die sie/ihn bestrafen kann");
			}
		}
		for (Spieler spieler : spielerMitRolle(Rolle.BESTRAFENDE_PERSON)) {
			if (spielerMitRolleAußer(Rolle.BESTRAFTE_PERSON, spieler) == null) {
				throw new InkonsistenterSpielerZustand(spieler.getName() + " verfügt über keine Person, die sie/er bestrafen kann");
			}
		}
		for (Spieler spieler : spielerMitRolle(Rolle.BEGLUECKTE_PERSON)) {
			if (spielerMitRolleAußer(Rolle.BEGLUECKENDE_PERSON, spieler) == null) {
				throw new InkonsistenterSpielerZustand(spieler.getName() + " verfügt über keine Person, die sie/ihn beglücken kann");
			}
		}
		for (Spieler spieler : spielerMitRolle(Rolle.BEGLUECKENDE_PERSON)) {
			if (spielerMitRolleAußer(Rolle.BEGLUECKTE_PERSON, spieler) == null) {
				throw new InkonsistenterSpielerZustand(spieler.getName() + " verfügt über keine Person, die sie/ihn beglücken kann");
			}
		}
	}

	public List<Spieler> spielerMitRolle(Rolle rolle) {
		return spieler.stream().filter(s -> s.getRollen().contains(rolle)).toList();
	}
	
	public Spieler spielerMitRolleAußer(Rolle rolle, Spieler ausschluss) {
		List<Spieler> list = spieler.stream().filter(spieler -> spieler != ausschluss && spieler.getRollen().contains(rolle)).toList();
		if (list != null && list.size() > 0) {
			return list.get(new Random().nextInt(list.size()));
		}
		return null;
	}
	
	public Aufgabe naechsteAufgabe() {
		System.out.println("Aktuelles Level: " + level);
		System.out.println("Vor neuer Aufgabe: ");
		logStatus();
		Random random = new Random();
		int nextInt = random.nextInt(100);
		System.out.println("Random Wert: " + nextInt);
		if (nextInt == 1) {
			List<Spieler> bestraftePersonen = spielerMitRolle(Rolle.BESTRAFTE_PERSON);
			if (bestraftePersonen != null && !bestraftePersonen.isEmpty()) {
				Spieler bestraftePerson = bestraftePersonen.get(random.nextInt(bestraftePersonen.size()));
				Spieler bestrafendePerson = spielerMitRolleAußer(Rolle.BESTRAFENDE_PERSON, bestraftePerson);
				return new UltimativeStrafe(bestraftePerson, bestrafendePerson);
			}
			
		} 
		if (nextInt == 2) {
			List<Spieler> bestraftePersonen = spielerMitRolle(Rolle.BESTRAFTE_PERSON);
			if (bestraftePersonen != null && !bestraftePersonen.isEmpty()) {				
				Spieler bestraftePerson = bestraftePersonen.get(random.nextInt(bestraftePersonen.size()));
				Spieler bestrafendePerson = spielerMitRolleAußer(Rolle.BESTRAFENDE_PERSON, bestraftePerson);
				return new AlleLocksVerlaengern(bestraftePerson, bestrafendePerson);
			}	
		}
		if (nextInt < 2 + wahrscheinlichkeitSperre) {
			System.out.println("Wahrscheinlichkeit Sperre");
			List<Spieler> spielerMitRolle = new ArrayList<>(spielerMitRolle(Rolle.BESTRAFTE_PERSON));
			Collections.shuffle(spielerMitRolle);
			for (Spieler weggesperrtePerson : spielerMitRolle) {
				List<Werkzeug> wegsperrbareWerkzeuge = new ArrayList<>(weggesperrtePerson.wegsperrbareWerkzeuge());
				Collections.shuffle(wegsperrbareWerkzeuge);
				for (Werkzeug werkzeug : wegsperrbareWerkzeuge) {
					List<LockSchablone> potentielleSperren = sperren.stream().filter(sperre -> sperre.getWerkzeug() == werkzeug).toList();
					if (potentielleSperren != null && !potentielleSperren.isEmpty()) {
						LockSchablone lockSchablone = potentielleSperren.get(random.nextInt(potentielleSperren.size()));						
						Spieler keyHolder = spielerMitRolleAußer(Rolle.BESTRAFENDE_PERSON, weggesperrtePerson);
						return lockSchablone.neuesLock(weggesperrtePerson, keyHolder);
					}
				}
			}
		} 
		if (nextInt < 2 + wahrscheinlichkeitSperre + wahrscheinlichkeitStrafe) {
			System.out.println("Wahrscheinlichkeit Strafe");
			List<Spieler> spielerMitRolle = spielerMitRolle(Rolle.BESTRAFTE_PERSON);
			if (spielerMitRolle != null && !spielerMitRolle.isEmpty()) {
				Spieler bestraftePerson = spielerMitRolle.get(random.nextInt(spielerMitRolle.size()));
				Spieler bestrafendePerson = spielerMitRolleAußer(Rolle.BESTRAFENDE_PERSON, bestraftePerson);
				if (bestrafendePerson != null && bestraftePerson != null) {
					List<BestrafungSchablone> potentielleStrafen = strafen.stream().filter(strafe -> strafe.isAusfuehrbar(bestraftePerson, bestrafendePerson, level)).toList();
					if (potentielleStrafen != null && !potentielleStrafen.isEmpty()) {
						BestrafungSchablone schablone = potentielleStrafen.get(random.nextInt(potentielleStrafen.size()));
						checkLevel();
						return schablone.neueBestrafung(bestraftePerson, bestrafendePerson);
					}
				}
			}
		}
		System.out.println("Wähle Standard-Aufgabe");
		List<Spieler> spielerMitRolle = spielerMitRolle(Rolle.BEGLUECKTE_PERSON);
		if (spielerMitRolle != null && !spielerMitRolle.isEmpty()) {
			Spieler begluecktePerson = spielerMitRolle.get(random.nextInt(spielerMitRolle.size()));
			Spieler beglueckendePerson = spielerMitRolleAußer(Rolle.BEGLUECKENDE_PERSON, begluecktePerson);
			if (beglueckendePerson != null) {
				List<BeglueckungSchablone> potentielleBeglueckungen = beglueckungen.stream().filter(begl -> begl.isAusfuehrbar(begluecktePerson, beglueckendePerson, level)).toList();
				if (potentielleBeglueckungen != null && !potentielleBeglueckungen.isEmpty()) {
					BeglueckungSchablone schablone = potentielleBeglueckungen.get(random.nextInt(potentielleBeglueckungen.size()));
					checkLevel();
					return schablone.beglueckungAusSchablone(begluecktePerson, beglueckendePerson);
				}
			}
		}
		checkLevel();
		return new RumMachen(spieler.get(0), spieler.get(1));
	}
	
	private void checkLevel() {
		if (++aufgabeAufAktuellenLevel >= aufgabenProLevel && level < 5) {
			aufgabeAufAktuellenLevel = 0;
			level++;
		}
	}
	
	private void logStatus() {
		for (Spieler aSpieler : spieler ) {
			aSpieler.logStauts();
		}
	}
	
	public List<String> pruefeLocks() {
		List<String> result = new ArrayList<>();
		spieler.forEach(spieler -> result.addAll(spieler.pruefeLocks()));
		return result;
	}
	
	public List<Spieler> getSpieler() {
		return spieler;
	}

	public int getWahrscheinlichkeitSperre() {
		return wahrscheinlichkeitSperre;
	}

	public int getWahrscheinlichkeitStrafe() {
		return wahrscheinlichkeitStrafe;
	}

	public List<LockSchablone> getSperren() {
		return sperren;
	}

	public List<BeglueckungSchablone> getBeglueckungen() {
		return beglueckungen;
	}

	public List<BestrafungSchablone> getStrafen() {
		return strafen;
	}

	public int getAufgabeAufAktuellenLevel() {
		return aufgabeAufAktuellenLevel;
	}

	public int getAufgabenProLevel() {
		return aufgabenProLevel;
	}

	public int getLevel() {
		return level;
	}
}

