package com.alexanderlogistics;

import java.util.List;
import java.util.logging.Logger;

import javax.inject.Inject;

import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.SignalAdapter;
import org.imixs.workflow.exceptions.AdapterException;
import org.imixs.workflow.exceptions.PluginException;

/**
 * Der ZahlungseingangSaldoAdapter korrigiert den Saldo einer Ausgangsrechnung
 * anhand der Zahlungsdaten in einem Zahlungseingangsworkflow.
 * <p>
 * Die verknüpften Rechnungen in einem Zahlungseingang können am Item
 * $workitemref ermittelt werden.
 * <p>
 * Relevant für den Abgleich sin die felder:
 * <ul>
 * <li>invoice.currency
 * <li>payment.amount
 * <p>
 * <code>com.alexanderlogistics.ZahlungseingangSaldoAdapter</code>
 * 
 * @version 1.0
 * @author rsoika
 */
public class ZahlungseingangSaldoAdapter implements SignalAdapter {

    private static Logger logger = Logger.getLogger(ZahlungseingangSaldoAdapter.class.getName());

    @Inject
    ZahlungseingangService zahlungseingangService;

    /**
     * This method finds the outgoing invoicdes and updates the saldo
     * (payment.total).
     * 
     * @throws PluginException
     */
    @Override
    public ItemCollection execute(ItemCollection document, ItemCollection event)
            throws AdapterException, PluginException {

        updateSaldo(document);

        return document;
    }

    /**
     * Diese method hängt eine referenz der aktuellen Rechnung an den Zahlungsavis
     * 
     * @param document
     * @throws PluginException
     */
    private void updateSaldo(ItemCollection document) throws PluginException {

        // payment currency
        String paymentCurrency = document.getItemValueString("payment.currency");
        double masterPaymentAmount = document.getItemValueDouble("payment.amount");
        double masterPaymentDifference = document.getItemValueDouble("payment.difference");
        String paymentWarning = document.getItemValueString("payment.warning");

        List<ItemCollection> paymentDetails = zahlungseingangService.loadPaymentDetails(document);

        if (paymentDetails.size() == 0) {
            throw new PluginException(PluginException.class.getName(),
                    ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED,
                    "Bitte wählen Sie die zugeordneten Rechnungen aus.");
        }

        logger.info("......Updating " + paymentDetails.size() + " outgoing invoices....");

        // Erst müssen wir einmal prüfen ob alle einzel salden ungleich dem
        // MasterPamyneAmmoutn sind!
        double einzelPayments = 0;
        for (ItemCollection payment : paymentDetails) {
            einzelPayments = einzelPayments + payment.getItemValueFloat("payment.amount");
        }
        // Prüfe auf Plausisbilität
        if (paymentWarning.isEmpty()) {
            // falls mehr einzelsummen eingeben wurden als der oben angegeben Total Pamyment
            // dann soll eine Warnung ausgegen werden.
            if ( InvoiceUtil.round(masterPaymentAmount+masterPaymentDifference) != InvoiceUtil.round(einzelPayments)) {
                document.setItemValue("payment.warning", "invalid saldo");
                throw new PluginException(PluginException.class.getName(),
                        ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED,
                        "Der Zahlungseingang ist nicht identisch mit den ausgebuchten Rechnungssalden. Sollte die Eingabe korrekt sein, wiederholen Sie die Aktion.");
            }
        }

      
        for (ItemCollection payment : paymentDetails) {

            ItemCollection invoice = zahlungseingangService.findInvoice(payment.getUniqueID());

            if (invoice != null) {
                String invoiceNumber = invoice.getItemValueString("invoice.number");
                String invoiceCurrency = invoice.getItemValueString("invoice.currency");
                float invoiceSaldo = invoice.getItemValueFloat("invoice.saldo");
                float invoicePaymentAmount = payment.getItemValueFloat("payment.amount");
                float invoiceRate = invoice.getItemValueFloat("invoice.rate");

                
                if (!invoiceCurrency.equals(paymentCurrency)) {
                    if (invoiceRate == 0) {
                        throw new PluginException(PluginException.class.getName(),
                                ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED,
                                "Der Zahlungseingang kann nicht verbucht werden, da die Währung " + paymentCurrency
                                        + " nicht mit der Währung der Rechnung " + invoiceNumber + " übereinstimmt!");
                    }
                }
                
                /**
                 * Falls die Währung der Rechnung von der Währung der Zahlung abweicht, dann
                 * rechnen wir den PaymentAmmout mit der Rate aus.
                 */
                if ("EUR".equals(paymentCurrency) &&
                        !invoiceCurrency.equals(paymentCurrency) && invoiceRate != 0) {
                    invoicePaymentAmount = invoicePaymentAmount*invoiceRate;    
                    // runde auf 2 Stellen
                    invoicePaymentAmount =(float) InvoiceUtil.round(invoicePaymentAmount);
                }
                
            
                // vergeiche Restsaldo mit payment für diese Rechnung
                if (Math.abs(InvoiceUtil.round(invoicePaymentAmount)) > Math.abs(InvoiceUtil.round(invoiceSaldo))) {
                    throw new PluginException(PluginException.class.getName(),
                            ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED,
                            "Der Zahlungseingang kann nicht verbucht werden, da der Restsaldo der Rechnung "
                                    + invoiceNumber + " kleiner als der Zahlbetrag ist!");
                }

                // jetzt den Rechnungsbetrag verkleinern
                invoiceSaldo = invoiceSaldo - invoicePaymentAmount;
                
               
                invoiceSaldo=(float) InvoiceUtil.round(invoiceSaldo);
                invoice.setItemValue("invoice.saldo", invoiceSaldo);
                invoice.event(ZahlungseingangService.EVENT_UPDATE_INVOICE);
                zahlungseingangService.processInvoice(invoice);

            } else {
                throw new PluginException(PluginException.class.getName(),
                        ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, "Rechnung konnte nicht gefunden werden!");
            }

        }

    }

}