package com.alexanderlogistics;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import javax.enterprise.context.ConversationScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;

import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.engine.DocumentService;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.exceptions.QueryException;
import org.imixs.workflow.faces.util.ErrorHandler;

/**
 * The CargosoftController CDI Bean provides Front-End methods for the cargosoft
 * kreditore objects.
 * <p>
 * The method search provides a suggest list searching a phrase within the
 * cargosoft creditor list (type=cargosoftkreditor).
 * 
 * <p>
 * Besonderheit
 * <p>
 * Wir transportieren in der Resutl Liste eine versteckte JSON Struktur mit
 * allen IBAN Informationen Das ist im Grunde etwas irre aber durch die
 * JSF-Ajax-JavaScript Hölle kommern wir derzeit aus der Nummer nicht mehr
 * anders raus.
 * 
 * @author rsoika
 *
 */
@Named
@ConversationScoped
public class CargosoftController implements Serializable {

	private static final long serialVersionUID = 1L;
	private static Logger logger = Logger.getLogger(CargosoftController.class.getName());

	@Inject
	protected DocumentService documentService;

	private List<KreditorSearchEntry> searchResult = null;

	private ItemCollection kreditor = null; // used for zahlungsziel (kreidtor.xhtml)

	/**
	 * This method searches a text phrase within the list of cargosoft kreditor
	 * objects (type=cargosoftkreditor).
	 * <p>
	 * JSF Integration:
	 * 
	 * {@code 
	 * 
	 * <h:commandScript name="imixsOfficeWorkflow.mlSearch" action=
	 * "#{cargosoftController.search()}" rendered="#{cargosoftController!=null}"
	 * render= "cargosoft-results" /> }
	 * 
	 * <p>
	 * JavaScript Example:
	 * 
	 * <pre>
	 * {@code
	 *  imixsOfficeWorkflow.cargosoftSearch({ item: '_invoicenumber' })
	 *  }
	 * </pre>
	 * 
	 * @param searchmode - if 'multi', display multiple IBANs in seperate lines
	 */
	public void searchCreditor(String searchmode) {
		boolean bMulitMode = "multi".equalsIgnoreCase(searchmode);
		searchResult = new ArrayList<KreditorSearchEntry>();
		// get the param from faces context....
		FacesContext fc = FacesContext.getCurrentInstance();
		String phrase = fc.getExternalContext().getRequestParameterMap().get("phrase");
		if (phrase == null) {
			return;
		}

		logger.fine("search prase '" + phrase + "'");

		// String input =workflowController.getWorkitem().getItemValueString(itemName);
		if (phrase == null || phrase.length() < 2) {
			return;
		}

		// die cargosoft Kreditornummer beginnt seltsamerweise mit einem K
		// und die Debitoren mit einem D.....
		String query = "(type:" + KreditorDebitorService.TYPE_CARGOSOFTKREDITOR + ") AND ( (name:K" + phrase
				+ "*) OR (name:D" + phrase + "*)  OR (" + phrase.toLowerCase() + "*) )";

		try {
			List<ItemCollection> result = documentService.find(query, 20, 0, "$modified", true);
			logger.info("found " + result.size() + " creditors...");
			if (result != null) {
				for (ItemCollection cdtr : result) {

					// Aenderugn 22. Jun wir geben das K/D direkt witer
					String cdtrNo = cdtr.getItemValueString("_VENDOR_NUM");
					String display = cdtrNo + " - " + cdtr.getItemValueString("_VENDOR_Name");

					// Im MulitMode zeigen wir pro IBAN (1-4) eine eigene Zeile an. Dieser
					// Modus wird für die Rechnungserfassung benötigt.
					// im SingleMode wird hingegen nur ein Datensatz in der Trefferliste angezeigt.
					// (Ein Kreditoren Eintrag kann bis zu 4 IBANs / BICs haben
					if (bMulitMode) {
						// in der JSON Struktur werden alle IBANS übertragen
						display = display + " |" + cdtr.getItemValueString("cdtr.creditperiod");
						display = display + "|" + cdtr.getItemValueString("cdtr.iban");
						display = display + "|" + cdtr.getItemValueString("cdtr.bic");
						searchResult.add(new KreditorSearchEntry(cdtrNo, display, buildJsonData(cdtr,
								cdtr.getItemValueString("cdtr.iban"), cdtr.getItemValueString("cdtr.bic"))));
						// haben wir noch weitere IBAN/BIC kombis?
						for (int j = 1; j <= 4; j++) {
							if (!cdtr.getItemValueString("cdtr.iban" + j).isEmpty()) {
								// Dublette mit alternativer IBAN erzeugen
								display = cdtrNo + " - " + cdtr.getItemValueString("_VENDOR_Name");
								display = display + " |" + cdtr.getItemValueString("cdtr.creditperiod");
								display = display + "|" + cdtr.getItemValueString("cdtr.iban" + j);
								display = display + "|" + cdtr.getItemValueString("cdtr.bic" + j);
								searchResult.add(new KreditorSearchEntry(cdtrNo, display,
										buildJsonData(cdtr, cdtr.getItemValueString("cdtr.iban" + j),
												cdtr.getItemValueString("cdtr.bic" + j))));
							}
						}
					} else {
						// Modus für Kreditoren Verwaltung
						searchResult.add(new KreditorSearchEntry(cdtrNo, display, buildJsonData(cdtr)));
					}

				}
			}
		} catch (QueryException e) {
			e.printStackTrace();
		}

	}

	/**
	 * This method searches a text phrase within the list of cargosoft Debitor
	 * objects (type=cargosoftkreditor).
	 * <p>
	 * JSF Integration:
	 * 
	 * {@code 
	 * 
	 * <h:commandScript name="imixsOfficeWorkflow.mlSearch" action=
	 * "#{cargosoftController.search()}" rendered="#{cargosoftController!=null}"
	 * render= "cargosoft-results" /> }
	 * 
	 * <p>
	 * JavaScript Example:
	 * 
	 * <pre>
	 * {@code
	 *  imixsOfficeWorkflow.cargosoftSearch({ item: '_invoicenumber' })
	 *  }
	 * </pre>
	 * 
	 * @param searchmode - if 'multi', display multiple IBANs in seperate lines
	 */
	public void searchDebitor() {

		searchResult = new ArrayList<KreditorSearchEntry>();
		// get the param from faces context....
		FacesContext fc = FacesContext.getCurrentInstance();
		String phrase = fc.getExternalContext().getRequestParameterMap().get("phrase");
		if (phrase == null) {
			return;
		}

		logger.fine("search prase '" + phrase + "'");

		// String input =workflowController.getWorkitem().getItemValueString(itemName);
		if (phrase == null || phrase.length() < 2) {
			return;
		}

		// die cargosoft Kreditornummer beginnt seltsamerweise mit einem K
		// und die Debitoren mit einem D.....
		String query = "(type:" + KreditorDebitorService.TYPE_CARGOSOFTKREDITOR + ") AND (name:D*) AND ((name:D"
				+ phrase + "*)  OR (" + phrase.toLowerCase() + "*) )";

		try {
			List<ItemCollection> result = documentService.find(query, 20, 0, "$modified", true);
			logger.fine("found " + result.size() + " debitors...");
			if (result != null) {
				for (ItemCollection dbtr : result) {
					// Aenderugn 22. Jun wir geben das K/D direkt witer
					String dbtrNo = dbtr.getItemValueString("_VENDOR_NUM");
					String display = dbtrNo + " - " + dbtr.getItemValueString("_VENDOR_Name");
					display = display.replace("\"", "");
					display = display.replace("'", "");
					searchResult.add(new KreditorSearchEntry(dbtrNo, display, buildJsonData(dbtr)));
				}
			}
		} catch (QueryException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Hilfsmethode die eine JSON Struktur mit einer IBAN/BIC kombinatuion erzeugt.
	 * Diese JSON Struktur wird von der Rechnungserfassungs Maske benötigt
	 * 
	 * @return
	 */
	private String buildJsonData(ItemCollection cdtr, String iban, String bic) {
		JsonObjectBuilder objectBuilder = Json.createObjectBuilder(). //
				add("no", jsonVal(cdtr.getItemValueString("_VENDOR_NUM"))). //
				add("name", cdtr.getItemValueString("_VENDOR_Name")). //
				add("creditperiod", jsonVal(cdtr.getItemValueString("cdtr.creditperiod"))). //
				add("iban", jsonVal(iban)). //
				add("bic", jsonVal(bic));
		JsonObject jsonObject = objectBuilder.build();

		String jsonString = "{}";
		try (Writer writer = new StringWriter()) {
			Json.createWriter(writer).write(jsonObject);
			jsonString = writer.toString();
		} catch (IOException e) {
			logger.warning("Unable to build json structure");
		}
		return jsonString;
	}

	/**
	 * Hilfsmethode die eine JSON Struktur mit allen IBN/BIC kombinationen erzeugt.
	 * Diese JSON Strukur wird dann von der Kreitoren.xhtml Maske wieder ausgelesen.
	 * 
	 * @return
	 */
	private String buildJsonData(ItemCollection cdtr) {
	
		JsonObjectBuilder objectBuilder = Json.createObjectBuilder(). //
				add("no", jsonVal(cdtr.getItemValueString("_VENDOR_NUM"))). //
				add("name", jsonVal(cdtr.getItemValueString("_VENDOR_Name"))). //
				add("creditperiod", jsonVal(cdtr.getItemValueString("cdtr.creditperiod"))). //
				//add("mail", jsonVal(cdtr.getItemValue("cdtr.mail"))). //

				add("mail", jsonVal((String) cdtr.getItemValue("cdtr.mail").stream()
                .collect(Collectors.joining("\\n")) )). //
			
				add("iban", jsonVal(cdtr.getItemValueString("cdtr.iban"))). //
				add("bic", jsonVal(cdtr.getItemValueString("cdtr.bic"))). //
				// add ibans für kreditoren verwaltung
				add("iban2", jsonVal(cdtr.getItemValueString("cdtr.iban2"))). //
				add("iban3", jsonVal(cdtr.getItemValueString("cdtr.iban3"))). //
				add("iban4", jsonVal(cdtr.getItemValueString("cdtr.iban4"))). //
				add("bic2", jsonVal(cdtr.getItemValueString("cdtr.bic2"))). //
				add("bic3", jsonVal(cdtr.getItemValueString("cdtr.bic3"))). //
				add("bic4", jsonVal(cdtr.getItemValueString("cdtr.bic4"))); //

		JsonObject jsonObject = objectBuilder.build();

		String jsonString = "{}";
		try (Writer writer = new StringWriter()) {
			Json.createWriter(writer).write(jsonObject);
			jsonString = writer.toString();
		} catch (IOException e) {
			logger.warning("Unable to build json structure");
		}
		return jsonString;
	}

	/**
	 * Helper method to remove " and ' characters - causing problems
	 * 
	 * @param val
	 * @return
	 */
	public static String jsonVal(String val) {
		val = val.replace("\"", "");
		val = val.replace("'", "");
		return val;
	}

	/**
	 * Die Resultliste wird als eine Liste einen Arrays zurückgegeben. Der Erste
	 * Eintrag
	 * 
	 * @return
	 */
	public List<KreditorSearchEntry> getSearchResult() {
		return searchResult;
	}

	/**
	 * This method reset the search and input state.
	 */
	public void reset() {
		searchResult = new ArrayList<KreditorSearchEntry>();
		logger.fine("reset");
	}

	public ItemCollection getKreditor() {
		if (kreditor == null) {
			kreditor = new ItemCollection();
		}
		return kreditor;
	}

	public void setKreditor(ItemCollection kreditor) {
		this.kreditor = kreditor;
	}

	/**
	 * Diese Method überträgt die geänderten Daten in das Kreidtoren/Debitoren
	 * Objekt (cdtr.creditperiod,cdtr.iban,cdtr.bic)
	 * 
	 * Zusätzlich wird geprüft ob die eingegebene Mail Adresse eine gültige SMTP Adresse ist
	 * @throws PluginException 
	 */
	public void updateZahlungsziel() throws PluginException  {

		String cdtrNumber = kreditor.getItemValueString("cdtr.number");
		logger.fine("Zahlungsziel = " + cdtrNumber);

		// search workitem
		String query = "(type:" + KreditorDebitorService.TYPE_CARGOSOFTKREDITOR + ") AND (name:" + cdtrNumber + ")";

		List<ItemCollection> result;
		try {
			result = documentService.find(query, 1, 0);
			if (result.size() == 1) {
				ItemCollection cargoCreditor = result.get(0);
				try {
					cargoCreditor.setItemValue("cdtr.creditperiod",
							Integer.parseInt(kreditor.getItemValueString("cdtr.creditperiod")));
				} catch (NumberFormatException nfe) {
					// no op
				}
				

				List<String> mailList= kreditor.getItemValue("cdtr.mail");
				mailList= mailList.stream()
					.filter(str -> str != null && !str.isEmpty())
					.map(String::trim)
					.collect(Collectors.toList());

				for (String mail: mailList) {
					if (!InvoiceUtil.validateEmail(mail)) {
						throw new PluginException(CargosoftController.class.getName(),
								"INVALID_EMAIL", "Die Daten konnten nicht aktualisiert werden! Bitte geben Sie eine gültige E-Mail Adresse ein!");		    
					}	
				}
				
				cargoCreditor.setItemValue("cdtr.mail",mailList);
				cargoCreditor.setItemValue("cdtr.iban", kreditor.getItemValueString("cdtr.iban"));
				cargoCreditor.setItemValue("cdtr.bic", kreditor.getItemValueString("cdtr.bic"));
				cargoCreditor.setItemValue("cdtr.iban2", kreditor.getItemValueString("cdtr.iban2"));
				cargoCreditor.setItemValue("cdtr.bic2", kreditor.getItemValueString("cdtr.bic2"));
				cargoCreditor.setItemValue("cdtr.iban3", kreditor.getItemValueString("cdtr.iban3"));
				cargoCreditor.setItemValue("cdtr.bic3", kreditor.getItemValueString("cdtr.bic3"));
				cargoCreditor.setItemValue("cdtr.iban4", kreditor.getItemValueString("cdtr.iban4"));
				cargoCreditor.setItemValue("cdtr.bic4", kreditor.getItemValueString("cdtr.bic4"));
				documentService.save(cargoCreditor);
			}
		} catch (QueryException e) {
			logger.severe("Unable to find cargosoft kreditor " + e.getMessage());
			
		 } catch (PluginException pe) {
	           
	            // add a new FacesMessage into the FacesContext
	            ErrorHandler.handlePluginException(pe);
	            throw pe;
		}

	}

}
