/*
 * MIT License
 *
 * Copyright (c) 2018-2019 Daniel Cortes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package danielcortes.xyz.informes;

import danielcortes.xyz.models.caja.SQLiteCajaDAO;
import danielcortes.xyz.models.informes.libro_de_ventas.InformeLibroDeVentasContent;
import danielcortes.xyz.models.informes.libro_de_ventas.SQLiteInformeLibroDeVentasContentDAO;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.PropertyTemplate;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;

public class InformeLibroDeVentas {
    private final String[] titles = {
            "", "",
            "BOLETA MANUAL", "", "",
            "BOLETA FISCAL", "", "", "", "",
            "BOLETAS EXENTAS", "", "",
            "SUB",
            "FACTURA", "", "",
            "GUIAS", "", "",
            "ESTADISTICAS"
    };

    private final String[] subtitles = {
            "DIA", "FECHA",
            "INICIAL", "FINAL", "VALOR",
            "Z DEL", "Z AL", "INCIAL", "FINAL", "VALOR",
            "INICIAL", "FINAL", "VALOR",
            "TOTAL",
            "INICIAL", "FINAL", "VALOR",
            "INICIAL", "FINAL", "VALOR",
            "TOTAL", "ACUMULADO", ""
    };

    private final String[] dias = {"Domingo", "Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado"};

    private ArrayList<InformeLibroDeVentasContent> informe;
    private int informeSize;
    private File saveFile;

    private Workbook wb;
    private Sheet sheet;
    private CreationHelper createHelper;
    private HashMap<String, CellStyle> styles;

    public InformeLibroDeVentas(LocalDate date, File saveFile) {
        new SQLiteCajaDAO().createCajasForMonth(date);

        this.informe = new ArrayList<>(new SQLiteInformeLibroDeVentasContentDAO().getInformeMensual(date));
        this.informeSize = this.informe.size();
        this.saveFile = saveFile;

        this.wb = new HSSFWorkbook();
        this.sheet = wb.createSheet();
        this.createHelper = wb.getCreationHelper();

        this.styles = this.generateStyles();
    }

    private void sortInforme(){
        this.informe.sort(Comparator.comparing(InformeLibroDeVentasContent::getFecha));
    }

    private void fillHeaders() {
        Row titles = sheet.createRow(0);
        Row subtitles = sheet.createRow(1);

        for (int x = 0; x < this.titles.length; x++) {
            titles.createCell(x).setCellValue(this.titles[x]);
        }

        for (int x = 0; x < this.subtitles.length; x++) {
            subtitles.createCell(x).setCellValue(this.subtitles[x]);
        }
    }

    private void fillData() {
        int x = 2;

        for(InformeLibroDeVentasContent data : this.informe){
            int y = 0;
            Row dataRow = sheet.createRow(x);

            Date fecha = Date.from(data.getFecha().atStartOfDay(ZoneId.systemDefault()).toInstant());

            dataRow.createCell(y++).setCellValue(this.dias[data.getDia()]);
            dataRow.createCell(y++).setCellValue(fecha);

            dataRow.createCell(y++).setCellValue(data.getManualesInicial() == null ? "0": data.getManualesInicial());
            dataRow.createCell(y++).setCellValue(data.getManualesFinal() == null ? "0" : data.getManualesFinal());
            dataRow.createCell(y++).setCellValue(data.getManuales());

            dataRow.createCell(y++).setCellValue(data.getFiscalesZInicial() == null ? "0" : data.getFiscalesZInicial());
            dataRow.createCell(y++).setCellValue(data.getFiscalesZFinal() == null ? "0" : data.getFiscalesZFinal());
            dataRow.createCell(y++).setCellValue(data.getFiscalesInicial() == null ? "0" : data.getFiscalesInicial());
            dataRow.createCell(y++).setCellValue(data.getFiscalesFinal() == null ? "0" : data.getFiscalesFinal());
            dataRow.createCell(y++).setCellValue(data.getFiscales());

            dataRow.createCell(y++).setCellValue(data.getExentasInicial() == null ? "0" : data.getExentasInicial());
            dataRow.createCell(y++).setCellValue(data.getExentasFinal() == null ? "0" : data.getExentasFinal());
            dataRow.createCell(y++).setCellValue(data.getExentas());

            dataRow.createCell(y++).setCellValue(data.getSubTotal());

            dataRow.createCell(y++).setCellValue(data.getFacturasInicial() == null ? "0" : data.getFacturasInicial());
            dataRow.createCell(y++).setCellValue(data.getFacturasFinal() == null ? "0" : data.getFacturasFinal());
            dataRow.createCell(y++).setCellValue(data.getFacturas());

            dataRow.createCell(y++).setCellValue(data.getGuiasInicial() == null ? "0" : data.getGuiasInicial());
            dataRow.createCell(y++).setCellValue(data.getGuiasFinal() == null ? "0" : data.getGuiasFinal());
            dataRow.createCell(y++).setCellValue(data.getGuias());

            dataRow.createCell(y++).setCellValue(data.getTotal());

            if (x == 2) {
                dataRow.createCell(y).setCellFormula(("U" + (x + 1)));
            } else {
                dataRow.createCell(y).setCellFormula(("U" + (x + 1)) + ("+") + ("V" + (x)));
            }
            x++;
        }
    }

    private void fillTotales() {
        int row = 2 + this.informeSize;
        Row totalesRow = sheet.createRow(row);

        totalesRow.createCell(0).setCellValue("TOTALES");

        totalesRow.createCell(4).setCellFormula("SUM(E" + 3 + ":E" + row + ")");
        totalesRow.createCell(9).setCellFormula("SUM(J" + 3 + ":J" + row + ")");
        totalesRow.createCell(12).setCellFormula("SUM(M" + 3 + ":M" + row + ")");
        totalesRow.createCell(16).setCellFormula("SUM(Q" + 3 + ":Q" + row + ")");
        totalesRow.createCell(19).setCellFormula("SUM(T" + 3 + ":T" + row + ")");
    }

    private void joinCells() {
        this.sheet.addMergedRegion(new CellRangeAddress(0, 0, 2, 4));
        this.sheet.addMergedRegion(new CellRangeAddress(0, 0, 5, 9));
        this.sheet.addMergedRegion(new CellRangeAddress(0, 0, 10, 12));
        this.sheet.addMergedRegion(new CellRangeAddress(0, 0, 14, 16));
        this.sheet.addMergedRegion(new CellRangeAddress(0, 0, 17, 19));
        this.sheet.addMergedRegion(new CellRangeAddress(0, 0, 20, 22));

    }

    private void freezeCells() {
        this.sheet.createFreezePane(2,2);
    }

    private void addBorders() {
        int row = 2 + informe.size();
        PropertyTemplate pt = new PropertyTemplate();

        //Bordes internos
        pt.drawBorders(new CellRangeAddress(2, row - 1, 0, 22), BorderStyle.THIN, BorderExtent.ALL);

        //Bordes de los headers
        pt.drawBorders(new CellRangeAddress(0, 1, 0, 1), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(0, 1, 2, 4), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(0, 1, 5, 9), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(0, 1, 10, 12), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(0, 1, 13, 13), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(0, 1, 14, 16), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(0, 1, 17, 19), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(0, 1, 20, 22), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);

        //Bordes que agrupan
        pt.drawBorders(new CellRangeAddress(2, row, 0, 1), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(2, row, 2, 4), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(2, row, 5, 9), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(2, row, 10, 12), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(2, row, 13, 13), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(2, row, 14, 16), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(2, row, 17, 19), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
        pt.drawBorders(new CellRangeAddress(2, row, 20, 22), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);


        //Bordes del total
        pt.drawBorders(new CellRangeAddress(row, row, 0, 22), BorderStyle.MEDIUM, BorderExtent.OUTSIDE);

        pt.applyBorders(this.sheet);
    }

    private void setStyles() {
        //Estilos para los 2 filas de titulos
        Iterator<Cell> cellIterator = this.sheet.getRow(0).cellIterator();
        while (cellIterator.hasNext()) {
            Cell cell = cellIterator.next();
            cell.setCellStyle(this.styles.get("header"));
        }

        cellIterator = this.sheet.getRow(1).cellIterator();
        while (cellIterator.hasNext()) {
            Cell cell = cellIterator.next();
            cell.setCellStyle(this.styles.get("header"));
        }

        //Estilos para las celdas de los datos
        for (int x = 2; x < this.informeSize + 2; x++) {
            Row row = this.sheet.getRow(x);
            int y = 0;

            //Primeras Celdas
            row.getCell(y++).setCellStyle(this.styles.get("dia"));
            row.getCell(y++).setCellStyle(this.styles.get("date"));

            //Boletas Manuales
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("money"));

            //Boletas Fiscales
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("money"));

            //Boletas Exentas
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("money"));

            //Sub Total
            row.getCell(y++).setCellStyle(this.styles.get("money"));

            //Facturas
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("money"));

            //Guias
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("regular"));
            row.getCell(y++).setCellStyle(this.styles.get("money"));

            //Estadisticas
            row.getCell(y++).setCellStyle(this.styles.get("money"));
            row.getCell(y).setCellStyle(this.styles.get("money"));
        }


        //Estilos para los totales del footer
        Row footerRow = this.sheet.getRow(33);
        for (int x = 0; x < 23; x++) {
            footerRow.getCell(x, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).setCellStyle(this.styles.get("footer"));
        }
        footerRow.getCell(4).setCellStyle(this.styles.get("footer_money"));
        footerRow.getCell(9).setCellStyle(this.styles.get("footer_money"));
        footerRow.getCell(12).setCellStyle(this.styles.get("footer_money"));
        footerRow.getCell(16).setCellStyle(this.styles.get("footer_money"));
        footerRow.getCell(19).setCellStyle(this.styles.get("footer_money"));

        //Setea la altura para todas las filas
        this.sheet.getRow(0).setHeightInPoints(20);
        this.sheet.getRow(1).setHeightInPoints(30);
        this.sheet.getRow(33).setHeightInPoints(20);

        for (int x = 2; x < 32; x++) {
            this.sheet.getRow(x).setHeightInPoints(15);
        }

        //Coloca el ancho como automatico en todas las columnas
        for (int x = 0; x <= 23; x++) {
            sheet.autoSizeColumn(x);
        }
    }

    public File generarInforme() {
        sortInforme();
        fillData();
        fillHeaders();
        fillTotales();
        joinCells();
        freezeCells();
        setStyles();
        addBorders();

        try (OutputStream fileOut = new FileOutputStream(this.saveFile)) {
            wb.write(fileOut);
            return this.saveFile;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private HashMap<String, CellStyle> generateStyles() {
        Font font = this.wb.createFont();
        font.setBold(true);
        font.setColor(IndexedColors.WHITE.getIndex());

        CellStyle regularStyle = this.wb.createCellStyle();

        CellStyle grayStyle = this.wb.createCellStyle();
        grayStyle.setFont(font);
        grayStyle.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
        grayStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

        CellStyle diaStyle = this.wb.createCellStyle();
        diaStyle.cloneStyleFrom(grayStyle);

        CellStyle dateStyle = this.wb.createCellStyle();
        dateStyle.cloneStyleFrom(grayStyle);
        dateStyle.setDataFormat(this.createHelper.createDataFormat().getFormat("dd/mm/yyyy"));

        CellStyle moneyStyle = this.wb.createCellStyle();
        moneyStyle.setDataFormat(this.createHelper.createDataFormat().getFormat("\"$\"#,##0_);(\"$\"#,##0)"));

        CellStyle headerStyle = this.wb.createCellStyle();
        headerStyle.cloneStyleFrom(grayStyle);
        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        headerStyle.setAlignment(HorizontalAlignment.CENTER);

        CellStyle footerStyle = this.wb.createCellStyle();
        footerStyle.cloneStyleFrom(grayStyle);
        footerStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        CellStyle footerMoneyStyle = this.wb.createCellStyle();
        footerMoneyStyle.cloneStyleFrom(footerStyle);
        footerMoneyStyle.setDataFormat(this.createHelper.createDataFormat().getFormat("\"$\"#,##0_);(\"$\"#,##0)"));

        HashMap<String, CellStyle> styles = new HashMap<>();
        styles.put("regular", regularStyle);
        styles.put("date", dateStyle);
        styles.put("dia", diaStyle);
        styles.put("money", moneyStyle);
        styles.put("header", headerStyle);
        styles.put("footer", footerStyle);
        styles.put("footer_money", footerMoneyStyle);

        return styles;
    }
}
