/*
 * 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.
 */

/*
 * 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.models.informes.libro_de_ventas;

import danielcortes.xyz.data.SQLiteConnectionHolder;
import danielcortes.xyz.utils.NaturalOrderComparator;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SQLiteInformeLibroDeVentasContentDAO extends InformeLibroDeVentasContentDAO {
    private static final Logger LOGGER = Logger.getLogger(SQLiteInformeLibroDeVentasContentDAO.class.getName());

    private HashMap<Integer, InformeLibroDeVentasContent> map;

    public SQLiteInformeLibroDeVentasContentDAO() {
        this.connectionHolder = new SQLiteConnectionHolder();
    }

    @Override
    public Collection<InformeLibroDeVentasContent> getInformeMensual(LocalDate date) {
        this.map = new HashMap<>();
        try (Connection conn = connectionHolder.getConnection()) {
            String queryTotales =
                    "select " +
                            "caja.id as \"caja\"," +
                            "strftime(\"%w\", caja.fecha) as \"dia\"," +
                            "caja.fecha as \"fecha\"," +
                            "sum(case when ingresos.tipo_ingreso_id = 2 then ingresos.valor else 0 end) as \"manuales\"," +
                            "sum(case when ingresos.tipo_ingreso_id = 1 then ingresos.valor else 0 end) as \"fiscales\"," +
                            "sum(case when ingresos.tipo_ingreso_id = 5 then ingresos.valor else 0 end) as \"exentas\"," +
                            "sum(case when ingresos.tipo_ingreso_id in (2, 1, 5) then ingresos.valor else 0 end) as \"sub_total\"," +
                            "sum(case when ingresos.tipo_ingreso_id = 3 then ingresos.valor else 0 end) as \"facturas\"," +
                            "sum(case when ingresos.tipo_ingreso_id = 4 then ingresos.valor else 0 end) as \"guias\"," +
                            "sum(case when ingresos.valor not null then ingresos.valor else 0 end) as \"total\" " +
                            "from caja left join ingresos on (caja.id = ingresos.caja_id) " +
                            "where caja.fecha between date(?) and date(?) " +
                            "group by caja.fecha;";
            PreparedStatement ps = conn.prepareStatement(queryTotales);
            ps.setString(1, date.withDayOfMonth(1).toString());
            ps.setString(2, date.withDayOfMonth(date.lengthOfMonth()).toString());
            ResultSet rs = ps.executeQuery();

            LOGGER.log(Level.FINE, "QUERY: {0} | values: [{1},{2}]", new Object[]{queryTotales, date.withDayOfMonth(1), date.withDayOfMonth(date.lengthOfMonth())});

            this.fillTotalesFromResultSet(rs);

            String queryNumeros =
                    "select\n" +
                            "caja.id as \"caja\"," +
                            "caja.fecha as \"fecha\"," +
                            "ingresos.nro_inicial as \"inicial\"," +
                            "ingresos.nro_final as \"final\"," +
                            "ingresos.nro_z_inicial as \"z_inicial\"," +
                            "ingresos.nro_z_final as \"z_final\"," +
                            "ingresos.tipo_ingreso_id as \"tipo_ingreso\" " +
                            "from caja join ingresos on (caja.id = ingresos.caja_id) " +
                            "where caja.fecha between date(?) and date(?);";
            ps = conn.prepareStatement(queryNumeros);
            ps.setString(1, date.withDayOfMonth(1).toString());
            ps.setString(2, date.withDayOfMonth(date.lengthOfMonth()).toString());
            rs = ps.executeQuery();

            LOGGER.log(Level.FINE, "QUERY: {0} | values: [{1},{2}]", new Object[]{queryNumeros, date.withDayOfMonth(1), date.withDayOfMonth(date.lengthOfMonth())});

            this.fillBoletasFromResultSet(rs);

            rs.close();
            ps.close();
        } catch (SQLException e) {
            LOGGER.log(Level.SEVERE, e.toString(), e);
        }

        return map.values();
    }

    private void fillTotalesFromResultSet(ResultSet rs) throws SQLException {
        while (rs.next()) {
            InformeLibroDeVentasContent informe = new InformeLibroDeVentasContent();
            informe.setDia(rs.getInt("dia"));
            informe.setFecha(LocalDate.parse(rs.getString("fecha")));
            informe.setManuales(rs.getInt("manuales"));
            informe.setFiscales(rs.getInt("fiscales"));
            informe.setExentas(rs.getInt("exentas"));
            informe.setSubTotal(rs.getInt("sub_total"));
            informe.setFacturas(rs.getInt("facturas"));
            informe.setGuias(rs.getInt("guias"));
            informe.setTotal(rs.getInt("total"));
            LOGGER.log(Level.FINER, "Se a creado una linea del informe con los totales {0}", informe);
            map.put(rs.getInt("caja"), informe);
        }
    }

    private void fillBoletasFromResultSet(ResultSet rs) throws SQLException {
        Comparator<String> comparator = new NaturalOrderComparator();
        while (rs.next()) {
            InformeLibroDeVentasContent informe = map.get(rs.getInt("caja"));
            int tipoIngreso = rs.getInt("tipo_ingreso");
            switch (tipoIngreso) {
                case 1:
                    String fiscalesInicial = rs.getString("inicial");
                    String fiscalesFinal = rs.getString("final");
                    String fiscalesZInicial = rs.getString("z_inicial");
                    String fiscalesZFinal = rs.getString("z_final");

                    if (informe.getFiscalesInicial() != null) {
                        if (comparator.compare(fiscalesInicial, informe.getFiscalesInicial()) < 0) {
                            informe.setFiscalesInicial(fiscalesInicial);
                        }
                    } else {
                        informe.setFiscalesInicial(fiscalesInicial);
                    }

                    if (informe.getFiscalesFinal() != null) {
                        if (comparator.compare(fiscalesFinal, informe.getFiscalesFinal()) > 0) {
                            informe.setFiscalesFinal(fiscalesFinal);
                        }
                    } else {
                        informe.setFiscalesFinal(fiscalesFinal);
                    }

                    if (informe.getFiscalesZInicial() != null) {
                        if (comparator.compare(fiscalesZInicial, informe.getFiscalesZInicial()) < 0) {
                            informe.setFiscalesZInicial(fiscalesZInicial);
                        }
                    } else {
                        informe.setFiscalesZInicial(fiscalesZInicial);
                    }

                    if (informe.getFiscalesZFinal() != null) {
                        if (comparator.compare(fiscalesZFinal, informe.getFiscalesZFinal()) > 0) {
                            informe.setFiscalesZFinal(fiscalesZFinal);
                        }
                    } else {
                        informe.setFiscalesZFinal(fiscalesZFinal);
                    }

                    break;
                case 2:
                    String manualesInicial = rs.getString("inicial");
                    String manualesFinal = rs.getString("final");

                    if (informe.getManualesInicial() != null) {
                        if (comparator.compare(manualesInicial, informe.getManualesInicial()) < 0) {
                            informe.setManualesInicial(manualesInicial);
                        }
                    } else {
                        informe.setManualesInicial(manualesInicial);
                    }

                    if (informe.getManualesFinal() != null) {
                        if (comparator.compare(manualesFinal, informe.getManualesFinal()) > 0) {
                            informe.setManualesFinal(manualesFinal);
                        }
                    } else {
                        informe.setManualesFinal(manualesFinal);
                    }

                    break;
                case 3:
                    String facturasInicial = rs.getString("inicial");
                    String facturasFinal = rs.getString("final");

                    if (informe.getFacturasInicial() != null) {
                        if (comparator.compare(facturasInicial, informe.getFacturasInicial()) < 0) {
                            informe.setFacturasInicial(facturasInicial);
                        }
                    } else {
                        informe.setFacturasInicial(facturasInicial);
                    }
                    if (informe.getFacturasFinal() != null) {
                        if (comparator.compare(facturasFinal, informe.getFacturasFinal()) > 0) {
                            informe.setFacturasFinal(facturasFinal);
                        }
                    } else {
                        informe.setFacturasFinal(facturasFinal);
                    }

                    break;
                case 4:
                    String guiasInicial = rs.getString("inicial");
                    String guiasFinal = rs.getString("final");

                    if (informe.getGuiasInicial() != null) {
                        if (comparator.compare(guiasInicial, informe.getGuiasInicial()) < 0) {
                            informe.setGuiasInicial(guiasInicial);
                        }
                    } else {
                        informe.setGuiasInicial(guiasInicial);
                    }
                    if (informe.getGuiasFinal() != null) {
                        if (comparator.compare(guiasFinal, informe.getGuiasFinal()) > 0) {
                            informe.setGuiasFinal(guiasFinal);
                        }
                    } else {
                        informe.setGuiasFinal(guiasFinal);
                    }
                    break;
                case 5:
                    String exentasInicial = rs.getString("inicial");
                    String exentasFinal = rs.getString("final");

                    if (informe.getExentasInicial() != null) {
                        if (comparator.compare(exentasInicial, informe.getExentasInicial()) < 0) {
                            informe.setExentasInicial(exentasInicial);
                        }
                    } else {
                        informe.setExentasInicial(exentasInicial);
                    }
                    if (informe.getExentasFinal() != null) {
                        if (comparator.compare(exentasFinal, informe.getExentasFinal()) > 0) {
                            informe.setExentasFinal(exentasFinal);
                        }
                    } else {
                        informe.setExentasFinal(exentasFinal);
                    }
                    break;
            }
            LOGGER.log(Level.FINER, "Se termino de llenar la linea del informe resultando en: {0}", informe);
        }
    }
}
