/**
 * ***************************************************************************
 * Copyright (c) 2010 Qcadoo Limited
 * Project: Qcadoo MES
 * Version: 1.4
 *
 * This file is part of Qcadoo.
 *
 * Qcadoo is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation; either version 3 of the License,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * ***************************************************************************
 */
package com.qcadoo.mes.warehouseMinimalState.print;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.primitives.Ints;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.qcadoo.localization.api.TranslationService;
import com.qcadoo.mes.basic.constants.CompanyFields;
import com.qcadoo.mes.basic.constants.ProductFields;
import com.qcadoo.mes.deliveries.DeliveriesService;
import com.qcadoo.mes.deliveries.constants.CompanyProductFields;
import com.qcadoo.mes.materialFlow.constants.LocationFields;
import com.qcadoo.mes.warehouseMinimalState.WarehouseMinimalStateHelper;
import com.qcadoo.mes.warehouseMinimalState.constants.WarehouseMinimalStateConstants;
import com.qcadoo.mes.warehouseMinimalState.constants.WarehouseMinimumStateFields;
import com.qcadoo.model.api.BigDecimalUtils;
import com.qcadoo.model.api.DataDefinition;
import com.qcadoo.model.api.DataDefinitionService;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.NumberService;
import com.qcadoo.model.api.search.SearchRestrictions;
import com.qcadoo.model.api.search.SearchResult;
import com.qcadoo.report.api.FontUtils;
import com.qcadoo.report.api.pdf.HeaderAlignment;
import com.qcadoo.report.api.pdf.PdfHelper;
import com.qcadoo.report.api.pdf.ReportPdfView;

@Component(value = "warehouseMinimalStateReportPdf")
public class DocumentPdf extends ReportPdfView {

    @Autowired
    private TranslationService translationService;

    @Autowired
    private PdfHelper pdfHelper;

    @Autowired
    private DataDefinitionService dataDefinitionService;

    @Autowired
    private NumberService numberService;

    @Autowired
    private WarehouseMinimalStateHelper warehouseMinimalStateHelper;

    @Autowired
    private DeliveriesService deliveriesService;

    private Set<Entity> warehouses;

    private Multimap<Long, Entity> warehouseToMinimumStateMap;

    private Locale locale;

    @Override
    protected String addContent(Document document, Map<String, Object> model, Locale locale, PdfWriter writer)
            throws DocumentException, IOException {
        this.locale = locale;
        String documentTitle = translationService.translate("warehouseMinimalState.report.title", locale);
        String documentAuthor = translationService.translate("qcadooReport.commons.generatedBy.label", locale);
        pdfHelper.addDocumentHeader(document, "", documentTitle, documentAuthor, new Date());

        DataDefinition warehouseMinimumStateDD = dataDefinitionService.get(WarehouseMinimalStateConstants.PLUGIN_IDENTIFIER,
                WarehouseMinimalStateConstants.MODEL_WAREHOUSE_MINIMUM_STATE);
        SearchResult searchResult = warehouseMinimumStateDD.find().add(SearchRestrictions.gt("minimumState", BigDecimal.ZERO))
                .list();
        List<Entity> warehouseMinimumStates = searchResult.getEntities();
        fillWarehousesLists(warehouseMinimumStates);
        addWarehouseTables(document);

        return "raport";
    }

    @Override
    protected void addTitle(Document document, Locale locale) {
        document.addTitle(translationService.translate("warehouseMinimalState.report.title", locale));
    }

    private void fillWarehousesLists(List<Entity> warehouseMinimumStates) {
        warehouses = new HashSet<Entity>();
        warehouseToMinimumStateMap = ArrayListMultimap.create();
        for (Entity warehouseMinimumState : warehouseMinimumStates) {
            Entity warehouse = warehouseMinimumState.getBelongsToField(WarehouseMinimumStateFields.LOCATION);
            warehouses.add(warehouse);
            warehouseToMinimumStateMap.put(warehouse.getId(), warehouseMinimumState);
        }
    }

    private void addWarehouseTables(Document document) throws DocumentException {
        List<Entity> warehousesList = warehouses.stream().sorted((w1, w2) -> w1.getStringField(LocationFields.NUMBER)
                .compareToIgnoreCase(w2.getStringField(LocationFields.NUMBER))).collect(Collectors.toList());
        for (Entity warehouse : warehousesList) {
            addWarehouseTable(document, warehouse);
        }
    }

    private void addWarehouseTable(Document document, Entity warehouse) throws DocumentException {
        String subtitle = translationService.translate("warehouseMinimalState.report.subtitle", locale,
                warehouse.getStringField(LocationFields.NUMBER), warehouse.getStringField(LocationFields.NAME));

        PdfPTable warehouseTable = createTable();


        Collection<Entity> minimumStates = warehouseToMinimumStateMap.get(warehouse.getId());
        minimumStates = minimumStates.stream()
                .sorted((ms1, ms2) -> ms1.getBelongsToField("product").getStringField(ProductFields.NUMBER)
                        .compareToIgnoreCase(ms2.getBelongsToField("product").getStringField(ProductFields.NUMBER)))
                .collect(Collectors.toList());

        List<Entity> stocks = warehouseMinimalStateHelper.getWarehouseStockWithTooSmallMinState(warehouse,minimumStates.stream().map(res -> res.getBelongsToField("product")).collect(Collectors.toList()));
        Map<Long, Entity> stocksByProduct = stocks.stream()
                .collect(Collectors.toMap(res -> res.getIntegerField("product_id").longValue(), res -> res));
        boolean rowsWereAdded = false;
        for (Entity minimumState : minimumStates) {
            rowsWereAdded |= addRow(minimumState, stocksByProduct, warehouseTable);
        }
        if (rowsWereAdded) {
            document.add(new Paragraph(subtitle, FontUtils.getDejavuBold11Light()));
            document.add(warehouseTable);
        }
    }

    private PdfPTable createTable() {
        int[] headerWidths = Ints.toArray(getHeaderWidths());
        Map<String, HeaderAlignment> headerValues = getHeaderValues(locale);
        PdfPTable warehouseTable = pdfHelper.createTableWithHeader(headerWidths.length, Lists.newArrayList(headerValues.keySet()),
                false, headerWidths, headerValues);
        warehouseTable.getDefaultCell().disableBorderSide(PdfPCell.RIGHT);
        warehouseTable.getDefaultCell().disableBorderSide(PdfPCell.LEFT);
        warehouseTable.setHeaderRows(1);
        warehouseTable.setSpacingAfter(12.0f);
        return warehouseTable;
    }

    private List<Integer> getHeaderWidths() {
        return Lists.newArrayList(40, 65, 25, 40, 40, 40, 40, 60);
    }

    private boolean addRow(Entity minimalState, Map<Long, Entity> stocksByProduct, PdfPTable table) {
        boolean rowAdded = false;
        Entity product = minimalState.getBelongsToField(WarehouseMinimumStateFields.PRODUCT);
        Entity warehouse = minimalState.getBelongsToField(WarehouseMinimumStateFields.LOCATION);
        Entity stock = stocksByProduct.get(product.getId());
        if (stock == null) {
            BigDecimal ordered = warehouseMinimalStateHelper.getOrderedQuantityForProductAndLocation(warehouse.getId(),
                    product.getId());

            if (warehouseMinimalStateHelper.checkIfLowerThanMinimum(product.getId(), ordered,
                    minimalState.getDecimalField("minimumState"))) {
                addCells(table, minimalState, null, ordered);
                rowAdded = true;
            }
        } else {
            BigDecimal statePlusOrder = BigDecimalUtils.convertNullToZero(stock.getDecimalField("orderedQuantity"))
                    .add(BigDecimalUtils.convertNullToZero(stock.getDecimalField("quantity")), numberService.getMathContext());
            if (warehouseMinimalStateHelper.checkIfLowerThanMinimum(product.getId(), statePlusOrder,
                    stock.getDecimalField("minimumState"))) {
                addCells(table, minimalState, stock, null);
                rowAdded = true;
            }
        }
        return rowAdded;
    }

    private void addCells(PdfPTable table, Entity warehouseMinimumState, Entity stock, BigDecimal ordered) {
        Entity product = warehouseMinimumState.getBelongsToField(WarehouseMinimumStateFields.PRODUCT);
        addSmallCell(table, product.getStringField(ProductFields.NUMBER));
        addSmallCell(table, product.getStringField(ProductFields.NAME));
        addSmallCell(table, product.getStringField(ProductFields.UNIT));
        addSmallCell(table, warehouseMinimumState.getDecimalField(WarehouseMinimumStateFields.MINIMUM_STATE));
        if (stock != null) {
            addSmallCell(table, stock.getDecimalField("quantity"));
            addSmallCell(table, stock.getDecimalField("orderedQuantity"));
        } else {
            addSmallCell(table, BigDecimal.ZERO);
            addSmallCell(table, ordered);
        }
        addAdditionalCells(table, product);
        addSmallCell(table, warehouseMinimumState.getDecimalField(WarehouseMinimumStateFields.OPTIMAL_ORDER_QUANTITY));
        Optional<Entity> supplier = deliveriesService.getDefaultSupplier(product.getId());
        if (supplier.isPresent()) {
            Entity company = supplier.get().getBelongsToField(CompanyProductFields.COMPANY);
            addSmallCell(table, company.getStringField(CompanyFields.NAME));
        } else {
            table.completeRow();
        }
    }

    private void addAdditionalCells(PdfPTable table, Entity product) {
    }

    public void addSmallCell(PdfPTable table, String content) {
        table.addCell(new Phrase(content, FontUtils.getDejavuRegular7Dark()));
    }

    public void addSmallCell(PdfPTable table, BigDecimal content) {
        PdfPCell cell = new PdfPCell(table.getDefaultCell());
        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        String value = numberService.formatWithMinimumFractionDigits(content, 0);
        cell.setPhrase(new Phrase(value, FontUtils.getDejavuRegular7Dark()));
        table.addCell(cell);
    }

    private Map<String, HeaderAlignment> getHeaderValues(Locale locale) {
        Map<String, HeaderAlignment> headerLabels = Maps.newLinkedHashMap();

        headerLabels.put(translationService.translate("warehouseMinimalState.report.columnHeader.productNumber", locale),
                HeaderAlignment.LEFT);
        headerLabels.put(translationService.translate("warehouseMinimalState.report.columnHeader.productName", locale),
                HeaderAlignment.LEFT);
        headerLabels.put(translationService.translate("warehouseMinimalState.report.columnHeader.unit", locale),
                HeaderAlignment.LEFT);
        headerLabels.put(translationService.translate("warehouseMinimalState.report.columnHeader.minimumState", locale),
                HeaderAlignment.RIGHT);
        headerLabels.put(translationService.translate("warehouseMinimalState.report.columnHeader.warehouseState", locale),
                HeaderAlignment.RIGHT);
        headerLabels.put(translationService.translate("warehouseMinimalState.report.columnHeader.orderQuantity", locale),
                HeaderAlignment.RIGHT);
        addAdditionalHeaders(headerLabels, locale);
        headerLabels.put(translationService.translate("warehouseMinimalState.report.columnHeader.optimalOrderQuantity", locale),
                HeaderAlignment.RIGHT);
        headerLabels.put(translationService.translate("warehouseMinimalState.report.columnHeader.contractor", locale),
                HeaderAlignment.LEFT);
        return headerLabels;
    }

    private void addAdditionalHeaders(Map<String, HeaderAlignment> headerLabels, Locale locale) {

    }
}
