package com.alexanderlogistics;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.engine.DocumentService;
import org.imixs.workflow.exceptions.QueryException;

import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.json.Json;
import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;

/**
 * Der AGLAnalyticController berechnet verschiedene Analyse Daten
 * 
 * @author rsoika
 *
 */
@Named
@RequestScoped
public class AGLAnalyticController implements Serializable {

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

	@Inject
	protected DocumentService documentService;

	@Inject
	protected DebitorStatistikController debitorStatistikController;

	List<ItemCollection> steuerbescheide = null;

	public void onEvent(@Observes AnalyticEvent event) {
		if ("analytic.steuer.eust".equals(event.getKey())) {
			event.setValue(computeSteuerbescheideEUST());
			event.setLabel("EUR");
			event.setDescription("Gesamt Einfuhrumsatzsteuer (EUSt)");
			event.getWorkitem().setItemValue("eust.total", event.getValue());

		}
		if ("analytic.steuer.zoll".equals(event.getKey())) {
			event.setValue(computeSteuerbescheideZoll());
			event.setLabel("EUR");
			event.setDescription("Gesamt Zölle (ZOLLEU)");
		}
		if ("analytic.steuer.due".equals(event.getKey())) {
			event.setValue(computeSteuerbescheideUeberfaellig());
			event.setLabel("EUR");
			event.setDescription("Überfällige Steuerbescheide die noch nicht abgerechnet wurden.");
		}

		if ("analytic.steuer.trend".equals(event.getKey())) {
			event.setValue(accumulateSteuerbescheideByWeek());
			event.setLabel("Zoll & EUST");
			event.setDescription("Fälligkeiten nach KW");
		}
	}

	/**
	 * Berechnet die Summe aller offenen EUST Beträge
	 * // tax.code=[0], amount=[3885.16], activity.type=[EUST],
	 * // filenumber=[LA-PAP-2408-024]}
	 * 
	 * @return
	 */
	private double computeSteuerbescheideEUST() {
		logger.info("compute EUST...");
		List<ItemCollection> list = getSteuerbescheide();
		double result = 0;

		for (ItemCollection steuerItemCol : list) {
			result = result + getZollBySteuerBeleg(steuerItemCol, "EUST");
			// List<ItemCollection> positionsTabelle =
			// InvoiceUtil.explodeChildList(steuerItemCol);
			// for (ItemCollection pos : positionsTabelle) {
			// if ("EUST".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
			// result = result + pos.getItemValueDouble("amount");
			// }
			// }

		}
		return InvoiceUtil.round(result);
	}

	/**
	 * Berechnet die Summe aller offenen Zoll Beträge
	 * 
	 * @return
	 */
	private double computeSteuerbescheideZoll() {
		logger.info("compute Zoll...");
		List<ItemCollection> list = getSteuerbescheide();
		double result = 0;

		for (ItemCollection steuerItemCol : list) {
			result = result + getZollBySteuerBeleg(steuerItemCol, "ZOLL");
			// List<ItemCollection> positionsTabelle =
			// InvoiceUtil.explodeChildList(steuerItemCol);
			// for (ItemCollection pos : positionsTabelle) {
			// if ("ZOLL".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
			// result = result + pos.getItemValueDouble("amount");
			// }
			// }

		}
		return InvoiceUtil.round(result);
	}

	/**
	 * Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
	 * 
	 * @return
	 */
	private double computeSteuerbescheideUeberfaellig() {
		logger.info("compute EUST...");
		List<ItemCollection> list = getSteuerbescheide();
		double result = 0;
		for (ItemCollection steuerItemCol : list) {
			if (steuerItemCol.getTaskID() == 1200) {
				result = result + steuerItemCol.getItemValueDouble("invoice.saldo");
			}
		}
		return InvoiceUtil.round(result);

	}

	/**
	 * Berechnet die summ zoll/eust für einen Beleg
	 * 
	 * @param steuerItemCol
	 * @return
	 */
	private double getZollBySteuerBeleg(ItemCollection steuerItemCol, String type) {
		double result = 0;
		List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(steuerItemCol);
		for (ItemCollection pos : positionsTabelle) {
			if (type.equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
				result = result + pos.getItemValueDouble("amount");
			}
		}
		return result;
	}

	private List<ItemCollection> getSteuerbescheide() {
		if (steuerbescheide != null) {
			return steuerbescheide;
		}
		// compute steuerbescheide....
		logger.info("Load Steuerbescheide for analytics....");
		String query = "(type:workitem ) AND ($workflowgroup:\"Steuerbescheid\")";
		try {
			steuerbescheide = documentService.find(query, 999, 0, "$created", false);
			return steuerbescheide;
		} catch (QueryException e) {
			logger.severe("Failed to get invoices: " + e.getMessage());
		}

		return null;
	}

	/**
	 * Berechnet Chart daten aller Steuerbescheide bei KW
	 * 
	 * <pre>
	 * 
	   chartCfg = {
		  type: 'bar',
		  data: {
			datasets: [{
			  data: [20, 10],
			}],
			labels: ['1', '2']
		  }
	    }
	 * 
	 * </pre>
	 * 
	 * @return
	 */
	private String accumulateSteuerbescheideByWeek() {
		logger.info("accumulate steuer by week...");

		List<ItemCollection> list = getSteuerbescheide();
		Map<Integer, SteuerData> saldoByWeek = new HashMap<>();
		Calendar calendar = Calendar.getInstance();
		int earliestWeek = Integer.MAX_VALUE;
		int latestWeek = Integer.MIN_VALUE;

		// Step 1: Group saldo by week
		for (ItemCollection steuerItemCol : list) {
			double saldo = steuerItemCol.getItemValueDouble("invoice.saldo");
			Date date = steuerItemCol.getItemValueDate("invoice.date");

			// Get the week number and year from the date
			calendar.setTime(date);
			int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
			int year = calendar.get(Calendar.YEAR);
			int yearWeek = year * 100 + weekNumber; // Unique key for each year + week combination

			// Accumulate the saldo per week
			SteuerData steuerDataByWeek = saldoByWeek.getOrDefault(yearWeek, new SteuerData());
			double eust = getZollBySteuerBeleg(steuerItemCol, "EUST");
			double zoll = getZollBySteuerBeleg(steuerItemCol, "ZOLL");
			steuerDataByWeek.add(eust, zoll, saldo);

			saldoByWeek.put(yearWeek, steuerDataByWeek);

			// Track the earliest and latest weeks
			earliestWeek = Math.min(earliestWeek, yearWeek);
			latestWeek = Math.max(latestWeek, yearWeek);
		}

		// Step 2: Prepare the JSON structure and ensure we have all weeks from earliest
		// to latest
		JsonArrayBuilder dataBuilderSaldo = Json.createArrayBuilder();
		JsonArrayBuilder dataBuilderEust = Json.createArrayBuilder();
		JsonArrayBuilder dataBuilderZoll = Json.createArrayBuilder();
		JsonArrayBuilder labelsBuilder = Json.createArrayBuilder();

		Calendar startCal = Calendar.getInstance();
		startCal.set(Calendar.YEAR, earliestWeek / 100);
		startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);

		Calendar endCal = Calendar.getInstance();
		endCal.set(Calendar.YEAR, latestWeek / 100);
		endCal.set(Calendar.WEEK_OF_YEAR, latestWeek % 100);

		while (!startCal.after(endCal)) {
			int weekNumber = startCal.get(Calendar.WEEK_OF_YEAR);
			int year = startCal.get(Calendar.YEAR);
			int yearWeek = year * 100 + weekNumber;

			// Add the saldo for the week or 0.0 if no data
			SteuerData steuerData = saldoByWeek.getOrDefault(yearWeek, new SteuerData());
			dataBuilderSaldo.add(steuerData.saldo);
			dataBuilderEust.add(steuerData.eust);
			dataBuilderZoll.add(steuerData.zoll);
			labelsBuilder.add(weekNumber + "/" + year); // Label as 'week/year'

			// Move to the next week
			startCal.add(Calendar.WEEK_OF_YEAR, 1);
		}

		// Step 3: Prepare the JSON structure
		JsonObjectBuilder jsonBuilder = Json.createObjectBuilder()
				.add("type", "bar")
				.add("data", Json.createObjectBuilder()
						.add("datasets", Json.createArrayBuilder()

								.add(Json.createObjectBuilder() // First dataset for 'tax'
										.add("label", "Zoll")
										.add("backgroundColor", "#96f")
										.add("borderWidth", 1)
										.add("data", dataBuilderZoll))
								.add(Json.createObjectBuilder() // Second dataset for 'eust'
										.add("label", "EUSt")
										.add("backgroundColor", "#36a2eb")
										.add("borderWidth", 1)
										.add("data", dataBuilderEust))
								.add(Json.createObjectBuilder() // Third dataset for 'Saldo'
										.add("label", "Saldo")
										.add("backgroundColor", "#ff6384")
										.add("borderWidth", 1)
										.add("data", dataBuilderSaldo)))

						.add("labels", labelsBuilder));

		// Step 4: Return the JSON structure as a string
		JsonObject chartCfg = jsonBuilder.build();
		return chartCfg.toString();
	}

	/**
	 * Helper method to hold zoll and eust totls in an object. Needed to build
	 * totals per week.
	 */
	class SteuerData {
		double total = 0.0;
		double zoll = 0.0;
		double eust = 0.0;
		double saldo = 0.0;

		public SteuerData() {
			// default constructor
		}

		/**
		 * Adds new zoll and eust values and updates the total
		 * 
		 * @param eustNew eust value to be added
		 * @param zollNew zoll value to be added
		 */
		public void add(double eustNew, double zollNew, double saldoNew) {
			eust = eust + eustNew;
			zoll = zoll + zollNew;
			saldo = saldo + saldoNew;
			// update total
			total = eust + zoll;
		}

	}
}
