package com.alexanderlogistics.dwc;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

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.ai.workflow.OpenAIAPIAdapter;
import org.imixs.archive.core.SnapshotService;
import org.imixs.workflow.FileData;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.SignalAdapter;
import org.imixs.workflow.engine.DocumentService;
import org.imixs.workflow.engine.ReportService;
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.sepa.services.SepaWorkflowService;

import com.alexanderlogistics.KreditorDebitorService;
import com.alexanderlogistics.OPListExportAdapterByWeek;

import jakarta.ejb.EJB;
import jakarta.inject.Inject;

/**
 * TheSEPAExportAdapterNBD is an extension of the SEPAExportAdapter.
 * 
 * This adapter export the payment information in a Excel Sheet acording to the
 * 'businessONLINE NBD' interface description.
 * 
 * *
 * 
 * <pre>
 * {@code
        <imixs-sepa name="CONFIG">
            <textblock>....</textblock>
            <template>....</template>
        </imixs-sepa>
 * }
 * </pre>
 * 
 * @version 1.0
 * @author rsoika
 */
public class SEPAExportAdapterNBD implements SignalAdapter {

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

    public static final String ERROR_CONFIG = "CONFIG_ERROR";
    public static final String ERROR_MISSING_INVOICE = "ERROR_MISSING_INVOICE";

    final String TYPE_TEXTBLOCK = "textblock";

    @EJB
    ReportService reportService;

    @Inject
    SepaWorkflowService sepaWorkflowService;

    @Inject
    DocumentService documentService;

    @Inject
    WorkflowService workflowService;

    @Inject
    SnapshotService snapshotService;

    @Inject
    KreditorDebitorService kreditorDebitorService;

    /**
     * This method collects a data set with all invoices and computes a NBA Excel
     * File. The templated is loaded from a text block
     * 
     * @throws PluginException
     */
    @SuppressWarnings("unchecked")
    @Override
    public ItemCollection execute(ItemCollection sepaExport, ItemCollection event)
            throws AdapterException, PluginException {

        String key = sepaExport.getItemValueString("name");

        // Textblock laden
        List<ItemCollection> sepaConfigList = workflowService.evalWorkflowResultXML(event, "imixs-sepa", "CONFIG",
                sepaExport,
                false);
        if (sepaConfigList == null || sepaConfigList.size() != 1) {
            // no configuration found!
            throw new PluginException(OpenAIAPIAdapter.class.getSimpleName(), ERROR_CONFIG,
                    "Missing or invalid imixs-sepa definition in Event " + sepaExport.getTaskID() + "."
                            + sepaExport.getEventID());
        }

        ItemCollection sepaConf = sepaConfigList.get(0);
        String template = sepaConf.getItemValueString("template");
        FileData fileData = loadTextBlockFileData(sepaConf.getItemValueString("textblock"), template);

        // do we found the document?
        if (fileData == null) {
            throw new PluginException(OPListExportAdapterByWeek.class.getSimpleName(),
                    ERROR_CONFIG,
                    "invalid sepa configuration in model event - template not found!");
        }
        fileData.setName(template);
        // create the attachment based on the report definition
        // attach a file to the current workitem
        // create a harmonized debitor name for the filename.....
        String sDepName = sepaExport.getItemValueString(SepaWorkflowService.ITEM_DBTR_NAME);
        sDepName = sDepName.replace("&", "_");
        sDepName = sDepName.replace(">", "_");
        sDepName = sDepName.replace("<", "_");
        sDepName = sDepName.replace(" ", "_");
        // build a timestamp for the filename
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HHmm");
        String sepaFileName = "sepa_" + sDepName + "_" + df.format(new Date()) + ".xlsx";

        fileData.setName(sepaFileName);

        // append document
        logger.info("...append invoices to Template: " + template);

        // get the data source based on the $workitemref ....
        List<String> refList = sepaExport.getItemValue("$workitemref");
        List<ItemCollection> data = new ArrayList<ItemCollection>();
        logger.info("...SEPA export started - " + refList.size() + " invoices found...");

        for (String ref : refList) {
            // load invoice
            ItemCollection invoice = sepaWorkflowService.loadInvoice(ref);
            if (invoice == null) {
                logger.warning("Invoice '" + ref + "' not found! SEPA Export can not be executed");
                continue;
            }
            // avoid unsupported characters in sepa fields
            invoice = sepaWorkflowService.harmonizeSEPAItem(invoice, SepaWorkflowService.ITEM_CDTR_NAME);
            invoice = sepaWorkflowService.harmonizeSEPAItem(invoice, SepaWorkflowService.ITEM_DBTR_NAME);
            data.add(invoice);
        }

        // finally we add the teh invoices
        try {
            insertInvoiceRows(sepaExport, fileData, data);
        } catch (PluginException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (QueryException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return sepaExport;
    }

    /**
     * This helper method inserts a row for each invoice of the OPListe 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
     * @throws QueryException
     */
    private void insertInvoiceRows(ItemCollection workitem, FileData fileData, List<ItemCollection> invoices)
            throws PluginException, QueryException {

        // 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("A2");
            XSSFRow referenceRow = sheet.getRow(cr.getRow());

            int rowPos = 1;

            // int lastRow = sheet.getLastRowNum();
            int lastRow = 999;
            logger.finest("Last rownum=" + lastRow);
            sheet.shiftRows(rowPos, lastRow, invoices.size(), true, true);

            for (ItemCollection invoice : invoices) {
                logger.finest("......copy row...");
                // now create a new line..
                XSSFRow row = sheet.createRow(rowPos);
                row.copyRowFrom(referenceRow, new CellCopyPolicy());
                // insert values
                row.getCell(0).setCellValue(workitem.getItemValueString("dbtr.iban"));
                row.getCell(1).setCellValue(invoice.getItemValueString("sequencenumber"));

                row.getCell(2).setCellValue(invoice.getItemValueString("invoice.ttc"));
                row.getCell(3).setCellValue(invoice.getItemValueString("cdtr.name"));
                row.getCell(4).setCellValue(buildAddressString(invoice));
                row.getCell(5).setCellValue(getVendorField(invoice, "_vendor_country"));
                row.getCell(6).setCellValue("A");
                row.getCell(7).setCellValue(invoice.getItemValueString("cdtr.iban"));
                row.getCell(8).setCellValue(invoice.getItemValueString("invoice.spc"));
                row.getCell(9).setCellValue(invoice.getItemValueString("cdtr.bic"));
                row.getCell(10).setCellValue(invoice.getItemValueString("invoice.lcc"));
                row.getCell(11).setCellValue(getVendorField(invoice, "_vendor_country"));
                row.getCell(12).setCellValue(invoice.getItemValueString("invoice.rpc"));
                row.getCell(13).setCellValue("");
                row.getCell(14).setCellValue(invoice.getItemValueString("invoice.currency"));
                row.getCell(15).setCellValue("" + invoice.getItemValueDouble("invoice.total"));
                row.getCell(16).setCellValue("Invoice No. " + invoice.getItemValueString("invoice.number"));
                row.getCell(17).setCellValue("");
                row.getCell(18).setCellValue("");
                row.getCell(19).setCellValue("");
                rowPos++;
            }
            // delete reference row A2
            // sheet.shiftRows(1, 1 + invoices.size(), -1, true, true);
            // sheet.shiftRows(1, lastRow + invoices.size(), -1, true, true);

            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
            workitem.addFileData(fileDataNew);
            logger.finest("......new document added");

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

    /**
     * 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;
    }

    /**
     * This helper method lookups the creditor in the CArgosoft dataset and builds a
     * address line needed for the export.
     * 
     * @param workitem
     * @return
     */
    private String buildAddressString(ItemCollection workitem) {
        String result = "";
        ItemCollection cdtrItemCol = null;

        String cdtrNumber = workitem.getItemValueString("cdtr.number");

        try {
            cdtrItemCol = kreditorDebitorService.findCreditor(cdtrNumber);
        } catch (PluginException e) {
            logger.warning("Unable to resolve address for " + cdtrNumber + " : " + e.getMessage());
        }
        if (cdtrItemCol != null) {
            result = result + cdtrItemCol.getItemValueString("_vendor_zip_code") + " ";
            result = result + cdtrItemCol.getItemValueString("_vendor_city") + " ";
            result = result + cdtrItemCol.getItemValueString("_vendor_str") + " ";
        }

        // length max 255
        result = result.trim();
        return result.length() > 255 ? result.substring(0, 255) : result;
    }

    private String getVendorField(ItemCollection workitem, String fieldname) {
        String result = "";
        ItemCollection cdtrItemCol = null;

        String cdtrNumber = workitem.getItemValueString("cdtr.number");

        try {
            cdtrItemCol = kreditorDebitorService.findCreditor(cdtrNumber);
        } catch (PluginException e) {
            logger.warning("Unable to resolve address for " + cdtrNumber + " : " + e.getMessage());
        }
        if (cdtrItemCol != null) {
            return cdtrItemCol.getItemValueString(fieldname);
        }

        return "";
    }
}