package de.subway_surfers.vpr_app;

import Logik.Mahlzeit;
import Logik.Zutat;
import RestAPISchnittstelle.RestApiClient;
import com.google.gson.*;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;

import java.time.LocalDate;
import java.util.ArrayList;

public class GerichterstellungMitarbeiterView {

	private @FXML HBox anzeigeInhaltsstoffe;
	private @FXML TextArea eingabeBeschreibung;
	private @FXML TextField eingabeName;
	private @FXML Button buttonInhaltsstoffe;
	private @FXML TextField eingabeInhaltsstoffe;
	private @FXML TextField eingabePreis;
	private @FXML DatePicker datePicker;
	private @FXML ListView<String> listViewVorschlag;
	private @FXML Label status;

	private ArrayList<Zutat> zutaten;
	private ArrayList<Mahlzeit> mahlzeiten;

	/**
	 * Füllt die Listview mit Vorschlägen und enthält den Eventlistener für das Textfeld
	 * Ist außerdem für die Skalierung der Buttons vom Inhaltsstoff zuständig
	 * Geschrieben: Sven Alteköster, Stefan Groß
	 *
	 */
	public void initialize() {
		zutaten = new ArrayList<>();
		mahlzeiten = new ArrayList<>();

		//Liste mit Daten füllen
		mahlzeiten = getMahlzeiten();
		for (Mahlzeit m : mahlzeiten) {
			listViewVorschlag.getItems().add(m.getName());
		}

		//Eventlistener für die Eingabe des Textfelds
		eingabeName.textProperty().addListener((obs, oldValue, newValue) -> {
			aktualisiereListView(newValue);
		});

		//Wenn ein Eintrag in der Listeview 2x angeklickt wird, werden die entsprechenden Felder mit
		//den Daten des Gerichts eingefügt
		listViewVorschlag.setOnMouseClicked(event -> {
			if (event.getClickCount() == 2) {
				String ausgewaehlterName = listViewVorschlag.getSelectionModel().getSelectedItem();
				if (ausgewaehlterName != null) {
					for (Mahlzeit m : mahlzeiten) {
						if (m.getName().equals(ausgewaehlterName)) {
							eingabeName.setText(m.getName());
							eingabePreis.setText(String.valueOf(m.getPreis()));
							eingabeBeschreibung.setText(m.getBeschreibung());
							break;
						}
					}
				}
			}
		});


		//wird das Fenster vergrößert, wird das Eingabefeld für Inhaltstoffe und den Hinzufügenbutton
		// auf die volle breite vergrößert.
		((Pane) eingabeInhaltsstoffe.getParent()).widthProperty().addListener((obs, oldValue, newValue) -> {
			//eingabeName, da dieses Feld immer die gesamte breite der Stage haben.
			eingabeInhaltsstoffe.setPrefWidth(eingabeName.getWidth() - buttonInhaltsstoffe.getPrefWidth());
		});
	}

	/**
	 * Holt Mahlzeiten von der API für die ListView.
	 * Geschrieben: Stefan Groß
	 *
	 * @return Liste von Mahlzeiten
	 */
	public ArrayList<Mahlzeit> getMahlzeiten() {
		ArrayList<Mahlzeit> mahlzeitenListe = new ArrayList<>();
		RestApiClient restApiClient = new RestApiClient();
		JsonElement je = JsonParser.parseString(restApiClient.get("Gericht"));
		JsonArray js = je.getAsJsonArray();

		for (JsonElement element : js) {
			JsonObject mahlzeitJson = element.getAsJsonObject();
			String name = mahlzeitJson.get("name").getAsString();
			float preis = mahlzeitJson.get("preis").getAsFloat();
			String beschreibung = mahlzeitJson.get("beschreibung").getAsString();
			Mahlzeit mahlzeit = new Mahlzeit(name, preis, beschreibung);
			mahlzeitenListe.add(mahlzeit);
		}
		return mahlzeitenListe;
	}


	/**
	 * Aktualisiert die ListView je nach Eingabe in das eingabeName-Feld
	 * Geschrieben: Stefan Groß
	 * Getestet:
	 *
	 * @param eingabeGerichtFilter "Suchtext" für die Gerichte
	 */
	public void aktualisiereListView(String eingabeGerichtFilter) {
		listViewVorschlag.getItems().clear();

		if (eingabeGerichtFilter == null || eingabeGerichtFilter.isEmpty()) {
			for (Mahlzeit m : mahlzeiten) {
				listViewVorschlag.getItems().add(m.getName());
			}
		}
		else {
			for (Mahlzeit m : mahlzeiten) {
				if (m.getName().toLowerCase().contains(eingabeGerichtFilter.toLowerCase())) {
					listViewVorschlag.getItems().add(m.getName());
				}
			}
		}
	}


	public void onButtonInhaltsstoffeClick() {
		String text = eingabeInhaltsstoffe.getText();
		if (!text.isEmpty()) {
			eingabeInhaltsstoffe.setText("");

			Button neuerInhalt = new Button();
			neuerInhalt.setText(text);
			anzeigeInhaltsstoffe.getChildren().add(neuerInhalt);

			Zutat neue = new Zutat(text);
			zutaten.add(neue);
			neuerInhalt.setOnAction(e -> {
				((HBox) neuerInhalt.getParent()).getChildren().remove(neuerInhalt);
				zutaten.remove(neue);
			});
		}
	}


	/**
	 * Bei klick auf den Speichernbutton wird die Eingabe überprüft
	 * es werden gegebenenfalls Fehlermeldungen angezeigt und es werden Mahlzeiten bei gültigen Eingaben gespeichert
	 * Geschrieben: Stefan Groß
	 */
	public void onSpeichernButtonClick() {
		String eingabe = eingabeName.getText();
		String beschreibung = eingabeBeschreibung.getText();
		StringBuilder fehlerMeldungen = new StringBuilder();
		boolean istGueltig = true;
		float preis = 0;

		resetFehlerrahmen();

		if (eingabe.isEmpty()) {
			eingabeName.setStyle("-fx-text-box-border: #B22222; -fx-focus-color: #B22222;");
			fehlerMeldungen.append("Name eingeben. ");
			istGueltig = false;
		}

		if (beschreibung.isEmpty()) {
			eingabeBeschreibung.setStyle("-fx-text-box-border: #B22222; -fx-focus-color: #B22222;");
			fehlerMeldungen.append("Beschreibung eingeben. ");
			istGueltig = false;
		}

		try {
			preis = Float.parseFloat(eingabePreis.getText());
		}
		catch (NumberFormatException e) {
			eingabePreis.setStyle("-fx-text-box-border: #B22222; -fx-focus-color: #B22222;");
			fehlerMeldungen.append("Preis ist ungültig. ");
			istGueltig = false;
		}

		LocalDate datum = datePicker.getValue();
		if (datum == null) {
			datePicker.setStyle("-fx-border-color: #B22222;");
			fehlerMeldungen.append("Gültiges Datum eingeben. ");
			istGueltig = false;
		}

		if (zutaten.isEmpty()) {
			eingabeInhaltsstoffe.setStyle("-fx-border-color: #B22222;");
			fehlerMeldungen.append("Inhaltsstoffe fehlen. ");
			istGueltig = false;
		}

		if (!istGueltig) {
			status.setText(fehlerMeldungen.toString());
			return;
		}


		Mahlzeit mahlzeit = new Mahlzeit(eingabe, preis, beschreibung);
		mahlzeiten.add(mahlzeit);
		status.setText("Mahlzeit hinzugefügt");
		resetEingabeFelder();

		int datumId = datumBearbeiten(datum);
		int gerichtId = gerichteAbfragen(mahlzeit);

		inhaltsstoff(gerichtId);
		gibtsAm(gerichtId, datumId);
	}

	/**
	 * Setzt die "Fehlerrahmen" der aller Elemente zurück
	 * Geschrieben: Stefan Groß
	 *
	 */
	private void resetFehlerrahmen() {
		eingabeName.setStyle(null);
		eingabeBeschreibung.setStyle(null);
		eingabePreis.setStyle(null);
		eingabeInhaltsstoffe.setStyle(null);
		datePicker.setStyle(null);
	}

	/**
	 * Setzt alle Eingabefelder auf einen leerwert zurück
	 * Geschrieben: Stefan Groß
	 *
	 */

	private void resetEingabeFelder() {
		eingabeName.setText("");
		eingabeBeschreibung.setText("");
		eingabePreis.setText("");
		eingabeInhaltsstoffe.setText("");
		datePicker.setValue(null);
	}

	/**
	 * Holt oder erstellt ein Datum mithilfe der API und gibt die ID zurück.
	 * Geschrieben: Stefan Groß
	 *
	 * @param datum Das eingegebene Datum
	 *
	 * @return ID des Datums
	 */
	private int datumBearbeiten(LocalDate datum) {
		RestApiClient restApiClient = new RestApiClient();
		String datumAsString = datum.toString();

		JsonElement jsonElement = JsonParser.parseString(restApiClient.get("Tagesplan"));
		JsonArray jsonArray = jsonElement.getAsJsonArray();

		for (JsonElement element : jsonArray) {
			JsonObject obj = element.getAsJsonObject();
			if (obj.get("datum").getAsString().equals(datumAsString)) {
				return Integer.parseInt(obj.get("id").getAsString());
			}
		}

		restApiClient.post("Tagesplan", "{\"datum\":\"" + datumAsString + "\"}");

		jsonElement = JsonParser.parseString(restApiClient.get("Tagesplan"));
		jsonArray = jsonElement.getAsJsonArray();

		for (JsonElement element : jsonArray) {
			JsonObject obj = element.getAsJsonObject();
			if (obj.get("datum").getAsString().equals(datumAsString)) {
				return Integer.parseInt(obj.get("id").getAsString());
			}
		}

		return -1;
	}

	/**
	 * Verwendet die API, um eine Beziehung in der Datenbank zwischen Gericht und Datum herzustellen.
	 * Geschrieben: Stefan Groß
	 *
	 * @param gid ID des Gerichts
	 * @param tid ID des Datums
	 */
	public void gibtsAm(int gid, int tid) {
		RestApiClient restApiClient = new RestApiClient();

		JsonObject gibtsAmJson = new JsonObject();

		gibtsAmJson.addProperty("tid", tid);
		gibtsAmJson.addProperty("gid", gid);
		restApiClient.post("gibtsAm", gibtsAmJson.toString());
	}

	/**
	 * Ruft Inhaltsstoffe ab und fügt sie zu einem Gericht hinzu.
	 * Geschrieben: Stefan Groß
	 *
	 * @param gerichtId ID des Gerichts
	 */

	public void inhaltsstoff(int gerichtId) {
		RestApiClient restApiClient = new RestApiClient();

		for (Zutat zutat : zutaten) {
			JsonElement jE = JsonParser.parseString(restApiClient.get("Inhaltsstoff"));
			JsonArray inhaltsstoffArray = jE.getAsJsonArray();

			int id = -1;

			// Überprüfen, ob der Inhaltsstoff bereits existiert.
			// Holt Id falls er existiert
			for (JsonElement element : inhaltsstoffArray) {
				JsonObject inhaltsstoff = element.getAsJsonObject();
				if (inhaltsstoff.get("name").getAsString().equalsIgnoreCase(zutat.getName())) {
					id = inhaltsstoff.get("id").getAsInt();
					break;
				}
			}

			// Fügt Inhaltsstoff hinzu, wenn er nicht existiert
			if (id == -1) {
				JsonObject neuerInhaltsstoff = new JsonObject();
				neuerInhaltsstoff.addProperty("name", zutat.getName());
				restApiClient.post("Inhaltsstoff", neuerInhaltsstoff.toString());

				// Fragt Id vom neuen Inhaltsstoff ab
				jE = JsonParser.parseString(restApiClient.get("Inhaltsstoff"));
				inhaltsstoffArray = jE.getAsJsonArray();
				JsonObject letzterInhaltsstoff = inhaltsstoffArray.get(inhaltsstoffArray.size() - 1).getAsJsonObject();
				id = letzterInhaltsstoff.get("id").getAsInt();
			}

			// Stellt verknüpfung zur Enthälttabelle her
			JsonObject enthaelt = new JsonObject();
			enthaelt.addProperty("iid", id);
			enthaelt.addProperty("gid", gerichtId);
			restApiClient.post("Enthaelt", enthaelt.toString());
		}
	}

	/**
	 * Prüft, ob ein Gericht bereits existiert, wenn es nicht existiert wird es hinzugefügt.
	 * Falls es existiert wird Preis und Beschreibung aktualisiert.
	 * Geschrieben: Stefan Groß
	 *
	 * @param mahlzeit Die zu überprüfende oder hinzuzufügende Mahlzeit
	 *
	 * @return ID des Gerichts
	 */

	private int gerichteAbfragen(Mahlzeit mahlzeit) {
		RestApiClient restApiClient = new RestApiClient();
		JsonElement je = JsonParser.parseString(restApiClient.get("Gericht"));
		JsonArray js = je.getAsJsonArray();

		for (JsonElement element : js) {
			JsonObject mahlzeitJson = element.getAsJsonObject();
			String nameMahlzeit = mahlzeitJson.get("name").getAsString();

			if (nameMahlzeit.equals(mahlzeit.getName())) {
				int id = Integer.parseInt(mahlzeitJson.get("id").getAsString());

				// Update des existierenden Gerichts
				JsonObject updateGericht = new JsonObject();
				updateGericht.addProperty("id", id);
				updateGericht.addProperty("name", mahlzeit.getName());
				updateGericht.addProperty("preis", mahlzeit.getPreis());
				updateGericht.addProperty("beschreibung", mahlzeit.getBeschreibung());

				restApiClient.put("Gericht", id, updateGericht.toString());
				return id;
			}
		}

		// Neues Gericht zur Datenbank hinzufügen, falls es nicht existiert
		JsonObject neuesGericht = new JsonObject();
		neuesGericht.addProperty("name", mahlzeit.getName());
		neuesGericht.addProperty("preis", mahlzeit.getPreis());
		neuesGericht.addProperty("beschreibung", mahlzeit.getBeschreibung());

		restApiClient.post("Gericht", neuesGericht.toString());

		// ID des Gerichts abrufen
		je = JsonParser.parseString(restApiClient.get("Gericht"));
		js = je.getAsJsonArray();
		for (JsonElement element : js) {
			JsonObject mahlzeitJson = element.getAsJsonObject();
			if (mahlzeitJson.get("name").getAsString().equals(mahlzeit.getName())) {
				return Integer.parseInt(mahlzeitJson.get("id").getAsString());
			}
		}
		return -1;
	}

}