/*
 * Arrebol Consultancy copyright.
 * 
 * This code belongs to Arrebol Consultancy
 * its use, redistribution or modification are prohibited 
 * without written authorization from Arrebol Consultancy.
 */
package com.arrebol.taxiservicios.controller.mutual;

import com.arrebol.taxiservicios.controller.admin.*;
import com.arrebol.taxiservicios.controller.connection.ConnectionManager;
import com.arrebol.taxiservicios.controller.util.HibernateUtil;
import com.arrebol.taxiservicios.model.admin.ClosingDay;
import com.arrebol.taxiservicios.model.admin.EmployeeSanction;
import com.arrebol.taxiservicios.model.admin.Expense;
import com.arrebol.taxiservicios.model.admin.ExpenseCompany;
import com.arrebol.taxiservicios.model.admin.StableGeneralBox;
import com.arrebol.taxiservicios.model.admin.Taxes;
import com.arrebol.taxiservicios.model.admin.TransferMoneyLocation;
import com.arrebol.taxiservicios.model.catalog.Location;
import com.arrebol.taxiservicios.model.core.User;
import com.arrebol.taxiservicios.model.enums.BasicType;
import com.arrebol.taxiservicios.model.mutual.MutualClosingDay;
import com.arrebol.taxiservicios.model.mutual.MutualExpenseCompany;
import com.arrebol.taxiservicios.model.mutual.MutualInicios;
import com.arrebol.taxiservicios.model.mutual.MutualStableGeneralBox;
import com.arrebol.taxiservicios.model.payroll.Adelantos;
import com.arrebol.taxiservicios.model.payroll.Payroll;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.Query;

/**
 *
 * @author Carlos Janitzio Zavala Lopez <janitzio.zavala@arrebol.com.mx>
 */
public class MutualStableGeneralBoxController extends ConnectionManager implements Serializable {

    /**
     *
     * @param date
     * @return
     */
    public boolean existNextPaidStableGeneralBoxByCreatedOn(Date date, String locationId) {
        logger.info("existNextPaidStableGeneralBoxByCreatedOn");

        boolean success = true;
        Transaction transaction = null;

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String query = "SELECT sgb.id "
                    + "FROM MUTUAL_STABLE_GENERAL_BOX sgb "
                    + "WHERE DATE(sgb.created_on) >= DATE_ADD(DATE(:date), INTERVAL 1 DAY) "
                    + "AND sgb.active_status = 'ENEBLED' AND sgb.id_location = :locationId ";

            List<String> records = session.createSQLQuery(query)
                    .setParameter("date", date)
                    .setParameter("locationId", locationId)
                    .getResultList();

            if (records.isEmpty()) {
                success = false;
            }

            transaction.commit();

            logger.info("Total of stable general box found: " + records.size());
        } catch (HibernateException e) {
            logger.error("Can not find next stable general box by created on", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method existNextPaidStableGeneralBoxByCreatedOn()", e);
            rollback(transaction);
        }

        return success;
    }

    /**
     * Find stable general box between Dates (Start date and End Date).
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public List<MutualStableGeneralBox> findStableGeneralBoxBetweenDates(Date startDate, Date endDate, String idLocation) {
        logger.info("findStableGeneralBoxBetweenDates");
        List<MutualStableGeneralBox> results = null;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaQuery<MutualStableGeneralBox> query = builder.createQuery(MutualStableGeneralBox.class);
            Root<MutualStableGeneralBox> root = query.from(MutualStableGeneralBox.class);

            Path<String> id = root.get("id");
            Path<BigDecimal> totalGeneralBox = root.get("totalGeneralBox");
            Path<BigDecimal> totalEnvelope = root.get("totalEnvelope");
            Path<BigDecimal> totalBankNote = root.get("totalBankNote");
            Path<BigDecimal> totalBank = root.get("totalBank");
            Path<BigDecimal> totalCoin = root.get("totalCoin");
            Path<BigDecimal> totalStable = root.get("totalStable");
            Path<String> description = root.get("description");
            Path<Date> createdOn = root.get("createdOn");
            Path<User> createdByPath = root.get("createdBy");
            Path<Location> locationPath = root.get("location");
            Path<Boolean> activeStatusPath = root.get("activeStatus");

            Predicate startDatePredicate = builder.greaterThanOrEqualTo(root.get("createdOn"), startDate);
            Predicate endDatePredicate = builder.lessThanOrEqualTo(root.get("createdOn"), endDate);
            Predicate locationPredicate = builder.equal(root.get("location"), new Location(idLocation));

            query.select(
                    builder.construct(
                            MutualStableGeneralBox.class,
                            id, totalGeneralBox, totalEnvelope,
                            totalBankNote, totalCoin, totalStable,
                            description, createdOn, createdByPath, locationPath, activeStatusPath, totalBank
                    )
            );
            query.where(builder.and(startDatePredicate, endDatePredicate, locationPredicate));
            query.orderBy(builder.asc(root.get("createdOn")));

            results = session.createQuery(query).getResultList();

            transaction.commit();

            logger.info("Stable General Box's list " + results.size());
        } catch (HibernateException e) {
            logger.error("Can not find stable general box between dates", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findStableGeneralBoxBetweenDates()", e);
            rollback(transaction);
        }
        return results;
    }

    /**
     * Update active status (ENEBLED/DISABLED) of stable general box by ID.
     *
     * @param id Primary key to be updated.
     * @param status Status to be updated (ENEBLED/DISABLED).
     * @param lastUpdatedBy User to apply update.
     * @return
     */
    public boolean updateStableGeneralBoxById(String id, BasicType status, User lastUpdatedBy) {
        logger.info("updateStableGeneralBoxById");

        boolean success = false;
        Transaction transaction = null;

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaUpdate<MutualStableGeneralBox> query = builder.createCriteriaUpdate(MutualStableGeneralBox.class);
            Root<MutualStableGeneralBox> root = query.from(MutualStableGeneralBox.class);

            query.set(root.get("activeStatus"), status);
            query.set(root.get("lastUpdatedBy"), lastUpdatedBy);
            query.set(root.get("lastUpdatedOn"), new Date());

            query.where(builder.equal(root.get("id"), id));

            int total = session.createQuery(query).executeUpdate();

            if (1 == total) {
                transaction.commit();
                success = true;
                logger.info("Update status successfully");
            } else {
                logger.info("CAN NOT update stable general box");
                rollback(transaction);
            }
        } catch (HibernateException e) {
            logger.info("CAN NOT update stable general box", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method updateStableGeneralBoxById()", e);
            rollback(transaction);
        }
        return success;
    }

    /**
     * Save a new stable general box entry.
     *
     * @param stableGeneralBox
     * @return
     */
    public boolean saveStableGeneralBox(MutualStableGeneralBox stableGeneralBox) {
        logger.info("saveStableGeneralBox");

        boolean success = false;
        Transaction transaction = null;

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            session.save(stableGeneralBox);

            transaction.commit();
            success = true;
        } catch (HibernateException e) {
            logger.error("Can not save stable general box", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method saveStableGeneralBox()", e);
            rollback(transaction);
        }
        return success;
    }

    /**
     * Find an entry that match with date (dd/mm/yyyy).
     *
     * @param date
     * @param locationId
     * @return True if exist a coincidence otherwise false.
     */
    public boolean existStableGeneralBoxEqToDate(Date date, String locationId) {
        logger.info("existStableGeneralBoxEqToDate");

        boolean success = false;
        Transaction transaction = null;
// cuadre de caja general -> creaer un cuadre, poner cualqueir dato elegir fecha y creaer otro para comprobar

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            String queryStr = "SELECT COUNT(sgb.id) FROM MutualStableGeneralBox sgb WHERE DATE(sgb.createdOn) = DATE(:date) AND sgb.activeStatus = 'ENEBLED' AND location = :location";

            Query query = session.createQuery(queryStr);
            query.setParameter("date", date);
            query.setParameter("location", new Location(locationId));
            success = (Long) (query.getSingleResult()) > 0;

            transaction.commit();
        } catch (HibernateException e) {
            logger.error("Can not load the stable general box", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method existStableGeneralBoxEqToDate()", e);
            rollback(transaction);
        }
        return success;
    }

    /**
     *
     * @return
     */
    public BigDecimal getTotalInBox(String idLocation) {
        logger.info("getTotalInBox");

        BigDecimal total = null;
        Transaction transaction = null;

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String sqlQuery = "SELECT\n"
                    + "(\n"
                    + "    (CASE\n"
                    + "        WHEN \n"
                    + "            (SELECT COUNT(total_general_box) AS total_in_box FROM MUTUAL_STABLE_GENERAL_BOX WHERE DATE(created_on) < CURDATE() AND id_location = '"+idLocation+"' AND active_status = 'ENEBLED' ORDER BY created_on DESC LIMIT 1) > 0\n"
                    + "        THEN \n"
                    + "            (SELECT IF(ISNULL(total_general_box),0,total_general_box) AS total_in_box FROM MUTUAL_STABLE_GENERAL_BOX WHERE DATE(created_on) < CURDATE() AND id_location = '"+idLocation+"' AND active_status = 'ENEBLED' ORDER BY created_on DESC LIMIT 1)\n"
                    + "        ELSE 0\n"
                    + "    END)\n"
                    + "    +\n"
                    + "    (SELECT IF(ISNULL(SUM(amount)),0,SUM(amount)) AS total_payment_in_today FROM MUTUAL_EXPENSE_COMPANY WHERE expense_company_type = 'PAYMENT_IN' AND id_location = '"+idLocation+"' AND DATE(created_on) > (SELECT DATE(sgb.created_on) FROM MUTUAL_STABLE_GENERAL_BOX sgb WHERE DATE(sgb.created_on) < CURDATE() AND id_location = '"+idLocation+"' AND sgb.active_status = 'ENEBLED' ORDER BY sgb.created_on DESC LIMIT 1) AND active_status = 'ENEBLED')\n"
                    + "    -\n"
                    + "    (SELECT IF(ISNULL(SUM(amount)),0,SUM(amount)) AS total_payment_out_today FROM MUTUAL_EXPENSE_COMPANY WHERE expense_company_type = 'PAYMENT_OUT' AND id_location = '"+idLocation+"' AND DATE(created_on) > (SELECT DATE(sgb.created_on) FROM MUTUAL_STABLE_GENERAL_BOX sgb WHERE DATE(sgb.created_on) < CURDATE() AND id_location = '"+idLocation+"' AND sgb.active_status = 'ENEBLED' ORDER BY sgb.created_on DESC LIMIT 1) AND active_status = 'ENEBLED')\n"
                    + "    -\n"
                    + "    (SELECT IF(ISNULL(SUM(amount)),0,SUM(amount)) AS payment FROM MUTUAL_INICIOS WHERE id_location = '"+idLocation+"' AND DATE(created_on) > (SELECT DATE(sgb.created_on) FROM MUTUAL_STABLE_GENERAL_BOX sgb WHERE DATE(sgb.created_on) < CURDATE() AND id_location = '"+idLocation+"' AND sgb.active_status = 'ENEBLED' ORDER BY sgb.created_on DESC LIMIT 1) )\n"
                    + "    +\n"
                    + "    (SELECT IF(ISNULL(SUM(total_stable)),0,SUM(total_stable)) AS payment FROM MUTUAL_STABLE_SMALL_BOX WHERE active_status = 'ENEBLED' AND id_location = '"+idLocation+"' AND DATE(created_on) > (SELECT DATE(sgb.created_on) FROM MUTUAL_STABLE_GENERAL_BOX sgb WHERE DATE(sgb.created_on) < CURDATE() AND id_location = '"+idLocation+"' AND sgb.active_status = 'ENEBLED' ORDER BY sgb.created_on DESC LIMIT 1))\n"
                    + ")AS TOTAL FROM DUAL";

            NativeQuery nativeQuery = session.createSQLQuery(sqlQuery);

            Object totalObj = (Object) (nativeQuery.uniqueResult());

            if (totalObj instanceof Double) {
                total = BigDecimal.valueOf(((Double) totalObj));
            } else if (totalObj instanceof Float) {
                total = BigDecimal.valueOf(((Float) totalObj));
            } else if (totalObj instanceof Long) {
                total = BigDecimal.valueOf(((Long) totalObj));
            } else if (totalObj instanceof BigDecimal) {
                total = (BigDecimal) totalObj;
            } else {
                total = BigDecimal.ZERO;
            }

            transaction.commit();
        } catch (HibernateException e) {
            logger.error("Can not load the total in stable general box", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getTotalInBox()", e);
            rollback(transaction);
        }

        return total;
    }
    
    public Date lastMutualStableGeneralBoxBySmallBox(MutualStableGeneralBox mutualStableGeneralBox) {
        logger.info("lastStableSmallBoxByDate");

        Date success = null;
        Transaction transaction = null;

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String query = "SELECT cd.createdOn FROM MutualStableGeneralBox cd "
                    + "WHERE DATE(cd.createdOn) < DATE(:date) AND cd.location.id = :locationID AND cd.activeStatus = 'ENEBLED' "
                    + "order by cd.createdOn DESC";

            success = (Date) session.createQuery(query)
                    .setParameter("date", mutualStableGeneralBox.getCreatedOn())
                    .setParameter("locationID", mutualStableGeneralBox.getLocation().getId()).setMaxResults(1)
                    .uniqueResult();

            transaction.commit();

            logger.info("Total of closing day found: ");
        } catch (HibernateException e) {
            logger.error("Can not find mutual stable general box by created on", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method lastMutualStableGeneralBoxByDate()", e);
            rollback(transaction);
        }

        return success;
    }
    
    public List<MutualClosingDay> findClosingDailyListByStableGeneralBox(Date startDate, String idLocation) {
        logger.info("findClosingDailyListByStableGeneralBox");

        List<MutualClosingDay> results = null;
        Transaction transaction = null;
        
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            transaction = session.beginTransaction();
            
            

            results = session.createQuery("FROM MutualClosingDay c WHERE c.activeStatus = 'ENEBLED' AND c.location.id = :idLocation AND DATE(c.createdOn) >= DATE(:fecha) ")
                    .setParameter("idLocation", idLocation)
                    .setParameter("fecha", startDate)
                    .getResultList();

            transaction.commit();

            logger.info("Closing daily detail's list " + results.size());
        } catch (HibernateException e) {
            logger.error("Can not find closing daily details list", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findDetailsFromClosingDayID()", e);
            rollback(transaction);
        }
        return results;
    }
    
    public List<MutualExpenseCompany> findExpenseCompanyInListByStableGeneralBox(Date startDate, String idLocation) {
        logger.info("findExpenseCompanyInListByStableGeneralBox");

        List<MutualExpenseCompany> results = null;
        Transaction transaction = null;
        
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            transaction = session.beginTransaction();
            
            

            results = session.createQuery("FROM MutualExpenseCompany c WHERE c.activeStatus = 'ENEBLED' AND c.expenseCompanyType = 'PAYMENT_IN' "
                    + "AND c.location.id = :idLocation AND DATE(c.createdOn) >= DATE(:fecha) ")
                    .setParameter("idLocation", idLocation)
                    .setParameter("fecha", startDate)
                    .getResultList();

            transaction.commit();

            logger.info("ExpenseCompany in detail's list " + results.size());
        } catch (HibernateException e) {
            logger.error("Can not find ExpenseCompany in details list", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findExpenseCompanyInListByStableGeneralBox()", e);
            rollback(transaction);
        }
        return results;
    }
    
    public List<MutualExpenseCompany> findExpenseCompanyOutListByStableGeneralBox(Date startDate, String idLocation) {
        logger.info("findExpenseCompanyOutListByStableGeneralBox");

        List<MutualExpenseCompany> results = null;
        Transaction transaction = null;
        
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            transaction = session.beginTransaction();
            
            

            results = session.createQuery("FROM MutualExpenseCompany c WHERE c.activeStatus = 'ENEBLED' AND c.expenseCompanyType = 'PAYMENT_OUT' "
                    + "AND c.location.id = :idLocation AND DATE(c.createdOn) >= DATE(:fecha) ")
                    .setParameter("idLocation", idLocation)
                    .setParameter("fecha", startDate)
                    .getResultList();

            transaction.commit();

            logger.info("ExpenseCompany out detail's list " + results.size());
        } catch (HibernateException e) {
            logger.error("Can not find ExpenseCompany out details list", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findExpenseCompanyOutListByStableGeneralBox()", e);
            rollback(transaction);
        }
        return results;
    }
    

    public List<MutualInicios> findIniciosListByStableGeneralBox(Date startDate, String idLocation) {
        logger.info("findIniciosListByStableGeneralBox");

        List<MutualInicios> results = null;
        Transaction transaction = null;
        
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            transaction = session.beginTransaction();
            
            

            results = session.createQuery("FROM MutualInicios c WHERE c.location.id = :idLocation AND DATE(c.createdOn) >= DATE(:fecha) ")
                    .setParameter("idLocation", idLocation)
                    .setParameter("fecha", startDate)
                    .getResultList();

            transaction.commit();

            logger.info("Expense detail's list " + results.size());
        } catch (HibernateException e) {
            logger.error("Can not find Expense details list", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findIniciosListByStableGeneralBox()", e);
            rollback(transaction);
        }
        return results;
    }
    
  
   
    public List<MutualExpenseCompany> findExpenseCompanyAllListByStableGeneralBox(Date startDate, String idLocation) {
        logger.info("findExpenseCompanyAllListByStableGeneralBox");

        List<MutualExpenseCompany> results = null;
        Transaction transaction = null;
        
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();

            transaction = session.beginTransaction();
            
            

            results = session.createQuery("FROM MutualExpenseCompany c WHERE c.activeStatus = 'ENEBLED' AND c.expenseCompanyType IN ('PAYMENT_OUT', 'PAYMENT_IN') "
                    + "AND c.location.id = :idLocation AND DATE(c.createdOn) >= DATE(:fecha) ")
                    .setParameter("idLocation", idLocation)
                    .setParameter("fecha", startDate)
                    .getResultList();

            transaction.commit();

            logger.info("ExpenseCompany all detail's list " + results.size());
        } catch (HibernateException e) {
            logger.error("Can not find ExpenseCompany all details list", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findExpenseCompanyAllListByStableGeneralBox()", e);
            rollback(transaction);
        }
        return results;
    }
    
    public BigDecimal getLastStableGeneralBox(Date startDate, String idLocation) {
        logger.info("getLastStableGeneralBox");

        BigDecimal total = null;
        Transaction transaction = null;

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            String sqlQuery = "SELECT\n"
                    + "(\n"
                    + "    (CASE\n"
                    + "        WHEN \n"
                    + "            (SELECT COUNT(total_general_box) AS total_in_box FROM MUTUAL_STABLE_GENERAL_BOX WHERE DATE(created_on) < DATE('"+startDate+"') AND id_location = '"+idLocation+"' AND active_status = 'ENEBLED' ORDER BY created_on DESC LIMIT 1) > 0\n"
                    + "        THEN \n"
                    + "            (SELECT IF(ISNULL(total_general_box),0,total_general_box) AS total_in_box FROM MUTUAL_STABLE_GENERAL_BOX WHERE DATE(created_on) < DATE('"+startDate+"') AND id_location = '"+idLocation+"' AND active_status = 'ENEBLED' ORDER BY created_on DESC LIMIT 1)\n"
                    + "        ELSE 0\n"
                    + "    END)\n"
                    + ")AS TOTAL FROM DUAL";

            NativeQuery nativeQuery = session.createSQLQuery(sqlQuery);

            Object totalObj = (Object) (nativeQuery.uniqueResult());

            if (totalObj instanceof Double) {
                total = BigDecimal.valueOf(((Double) totalObj));
            } else if (totalObj instanceof Float) {
                total = BigDecimal.valueOf(((Float) totalObj));
            } else if (totalObj instanceof Long) {
                total = BigDecimal.valueOf(((Long) totalObj));
            } else if (totalObj instanceof BigDecimal) {
                total = (BigDecimal) totalObj;
            } else {
                total = BigDecimal.ZERO;
            }

            transaction.commit();
        } catch (HibernateException e) {
            logger.error("Can not load the last stable general box", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method getLastStableGeneralBox()", e);
            rollback(transaction);
        }

        return total;
    }

    private static final long serialVersionUID = 895942543381249792L;
    final Logger logger = LogManager.getLogger(MutualStableGeneralBoxController.class);
}
