package com.alexanderlogistics.mahnlauf;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;

import javax.inject.Inject;

import org.apache.poi.ss.usermodel.CellCopyPolicy;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.imixs.archive.core.SnapshotService;
import org.imixs.workflow.FileData;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.engine.DocumentService;
import org.imixs.workflow.engine.WorkflowService;
import org.imixs.workflow.exceptions.AdapterException;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.exceptions.QueryException;

import org.imixs.workflow.poi.POIFindReplaceAdapter;

/**
 * Der ZahlungsavisAdapter importiert eine Excel Datei aus einem Textblock
 * 
 * <pre>
 * {@code
        <inkasso name="textblock">textblock-ref</inkasso>
        <inkasso name="template">filename</inkasso>
        <inkasso name="targetname">filename</inkasso>
   }
 * </pre>
 * <p>
 * Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden
 * (siehe POIFineReplaceAdapter).
 * 
 * <p>
 * Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer
 * 16 eingefügt werden.
 * <p> 
 * Dabei werden Gutschriften in spalte E und Rechnungen in Spalte F eingetrgen.
 * <p>
 * Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
 * 
 * @version 1.0
 * @author rsoika
 */
public class InkassoExportAdapter extends POIFindReplaceAdapter {

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

    public static final String ERROR_CONFIG = "CONFIG_ERROR";
    final String TYPE_TEXTBLOCK = "textblock";

    @Inject
    WorkflowService workflowService;

    @Inject
    DocumentService documentService;

    @Inject
    SnapshotService snapshotService;

    /**
     * This method finds or create the Zahlungsavis and adds a reference
     * ($workitemref) to the current invoice.
     * 
     * @throws PluginException
     */
    @SuppressWarnings("unchecked")
    @Override
    public ItemCollection execute(ItemCollection document, ItemCollection event)
            throws AdapterException, PluginException {

        // read the zahlungsavis options
        ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "inkasso", document, false);
        if (evalItemCollection == null) {
            throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
                    "missing inkasso configuration in model event - please check model configuration");
        }
        String textblock = evalItemCollection.getItemValueString("textblock");
        String template = evalItemCollection.getItemValueString("template");
        String targetName = evalItemCollection.getItemValueString("target-name");
        // adapt text....
        targetName = workflowService.adaptText(targetName, document);

        appendExcelTemplate(document, textblock, template, targetName);

        // Process POI instructions
        // read the config
        ItemCollection poiConfig = workflowService.evalWorkflowResult(event, "poi-update", document, false);
        if (poiConfig == null || !poiConfig.hasItem("findreplace")) {
            throw new PluginException(POIFindReplaceAdapter.class.getSimpleName(), CONFIG_ERROR,
                    "missing poi configuration");
        }
        List<String> replaceDevList = poiConfig.getItemValue("findreplace");
        String eval = poiConfig.getItemValueString("eval");
        try {
            this.updateFileData(document.getFileData(targetName), document, replaceDevList, eval);
        } catch (PluginException | IOException e) {
            throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
                    "failed to update zahlungsavis - wrong POI configuraiton: " + e.getMessage());
        }

        // now we add separate lines for each invoice....
        insertInvoiceRows(document, targetName);

        return document;
    }

    /**
     * This helper method inserts a row for each invoice of the current Zahlunsavis
     * at row 16 into the excel template file
     * <p>
     * The method copies the Row A16 as a reference row
     * <p>
     * The named cell 'TOTAL' should contain the summary formula. It will be
     * evaluated at the end.
     * 
     * @throws PluginException
     */
    @SuppressWarnings("unchecked")
    private void insertInvoiceRows(ItemCollection document, String fileName) throws PluginException {
        List<String> invoiceIDs = document.getItemValue("$workitemref");
        FileData fileData = document.getFileData(fileName);

        // load XSSFWorkbook
        try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) {
            XSSFWorkbook doc = new XSSFWorkbook(imputStream);
            // NOTE: we only take the first sheet !
            XSSFSheet sheet = doc.getSheetAt(0);
            CellReference cr = new CellReference("A16");
            XSSFRow referenceRow = sheet.getRow(cr.getRow());
            int referenceRowPos = 16;
            int rowPos = 16;
            //int lastRow = sheet.getLastRowNum();
            int lastRow = 999;
            logger.finest("Last rownum="+lastRow);
            sheet.shiftRows(rowPos, lastRow, invoiceIDs.size(), true, true);

            for (String invoiceID : invoiceIDs) {
                logger.finest("......copy row...");
                ItemCollection invoice = documentService.load(invoiceID);
                // now create a new line..
                XSSFRow row = sheet.createRow(rowPos);
                row.copyRowFrom(referenceRow, new CellCopyPolicy());
                // insert values
                row.getCell(0).setCellValue(invoice.getItemValueString("invoice.number"));
                row.getCell(1).setCellValue(invoice.getItemValueDate("invoice.date"));
                row.getCell(2).setCellValue(invoice.getItemValueDate("invoice.duedate"));
                // total
                row.getCell(3).setCellValue(invoice.getItemValueDouble("invoice.total"));
                // saldo
                row.getCell(4).setCellValue(invoice.getItemValueDouble("invoice.saldo"));

                rowPos++;
            }
            // delete reference row 16
            sheet.shiftRows(referenceRowPos, lastRow + invoiceIDs.size(), -1, true, true);

            // finally update the total formula...
            evalXSSFSheet(doc, sheet, "TOTAL");

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            // write back the file

            doc.write(byteArrayOutputStream);
            doc.close();
            byte[] newContent = byteArrayOutputStream.toByteArray();
            FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null);
            // update the fileData
            document.addFileData(fileDataNew);
            logger.finest("......new document added");

        } catch (IOException e) {
            throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
                    "failed to update inkasso: " + e.getMessage());
        }
    }

    /**
     * This method loads a text-block for a specified ref and appends the named
     * fileData object of this document.
     * 
     * @param document
     * @throws PluginException
     */
    private void appendExcelTemplate(ItemCollection document, String textblockRef, String template, String targetName)
            throws PluginException {

        if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
            throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
                    "invalid inkasso configuration in model event - textblock/template reference not defined!");
        }

        // load the text block
        FileData fileData = loadTextBlockFileData(textblockRef, template);

        // do we found the document?
        if (fileData == null) {
            throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
                    "invalid inkasso configuration in model event - template=" + template + " not found!");
        }
        fileData.setName(targetName);
        // append document
        logger.info("...append new inkasso Template: " + targetName);

        document.addFileData(fileData);

    }

    /**
     * This method returns a text-block ItemCollection for a specified name.
     * 
     * @param name in attribute txtname
     * 
     *
     */
    public FileData loadTextBlockFileData(String name, String fileName) {
        ItemCollection textBlockItemCollection = null;

        // load text-block by name....
        String sQuery = "(type:\"" + TYPE_TEXTBLOCK + "\" AND txtname:\"" + name + "\")";
        Collection<ItemCollection> col;
        try {
            // find the textblock...
            col = documentService.find(sQuery, 1, 0);
            if (col.size() > 0) {
                textBlockItemCollection = col.iterator().next();
                // fetch the fileData...
                return snapshotService.getWorkItemFile(textBlockItemCollection.getUniqueID(), fileName);

            } else {
                logger.warning("Missing text-block : '" + name + "'");
            }
        } catch (QueryException e) {
            logger.warning("getTextBlock - invalid query: " + e.getMessage());
        }

        return null;
    }

}