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

import com.arrebol.taxiservicios.controller.connection.ConnectionManager;
import com.arrebol.taxiservicios.controller.util.HibernateUtil;
import com.arrebol.taxiservicios.model.admin.Expense;
import com.arrebol.taxiservicios.model.admin.ExpenseCompany;
import com.arrebol.taxiservicios.model.catalog.ExpenseIncomeCatalog;
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.enums.BoxType;
import com.arrebol.taxiservicios.model.enums.ExpenseCatalogType;
import com.arrebol.taxiservicios.model.enums.ExpenseCompanyType;
import com.arrebol.taxiservicios.model.enums.ExpenseIncomeType;
import com.arrebol.taxiservicios.model.enums.ExpenseStatusType;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
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;

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

    /**
     * Find company's expenses by type (PAYMENT IN/OUT) and between start date
     * and end date.
     *
     * @param type
     * @param startDate
     * @param endDate
     * @param idLocation
     * @return List of all company expenses.
     */
    public List<ExpenseCompany> findExpenseCompanyByTypeAndBetweenDates(ExpenseCompanyType type, Date startDate, Date endDate, String idLocation) {
        logger.info("findExpenseCompanyByTypeAndBetweenDates");
        List<ExpenseCompany> results = new ArrayList<>();

        Transaction transaction = null;

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

            /*
            Path<String> id = root.get("id");
            Path<BigDecimal> amount = root.get("amount");
            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");
            Path<ExpenseIncomeCatalog> expenseIncomeCatalagoPath = root.get("idExpenseType");
            */
            Predicate searchType = builder.equal(root.get("expenseCompanyType"), type);
            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(ExpenseCompany.class, id, amount, description, createdOn, createdByPath, locationPath, activeStatusPath,expenseIncomeCatalagoPath));
            query.where(builder.and(
                    searchType, startDatePredicate, endDatePredicate, locationPredicate
            ));
            query.orderBy(builder.asc(root.get("createdOn")));

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

            transaction.commit();

            logger.info("Expense Company's list " + results.size());
        } catch (HibernateException e) {
            logger.error("Can not load  expense company list ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method findExpenseCompanyByTypeAndBetweenDates() ", e);
            rollback(transaction);
        }
        return results;
    }
    
    public List<ExpenseCompany> fillExpenseCompanyDataTableBetweenDatesIngresosGeneral(Date starDate, Date endDate, String location) {
        logger.info("fillExpenseDataTableBetweenDates");

        List<ExpenseCompany> results = null;
        Transaction transaction = null;

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

           
            Predicate predicateType = builder.equal(root.get("idExpenseType").get("expenseIncomeCatalogType"),ExpenseIncomeType.EARNING);
            Predicate predicateBox = builder.or(builder.equal(root.get("idExpenseType").get("activeBox"), BoxType.GENERAL),builder.equal(root.get("idExpenseType").get("activeBox"), BoxType.BOTH));
            Predicate startDatePredicate = builder.greaterThanOrEqualTo(root.get("createdOn"), starDate);
            Predicate endDatePredicate = builder.lessThanOrEqualTo(root.get("createdOn"), endDate);
            Predicate locationPredicate = builder.equal(root.get("location").get("id"), location);
            
             query.where(builder.and(
                  predicateBox,predicateType, startDatePredicate, endDatePredicate, locationPredicate
            ));
            query.orderBy(builder.asc(root.get("createdOn")));

            results = session.createQuery(query).getResultList();
            transaction.commit();
            logger.info("Total of Expense founds in auto complete search " + results.size());
        } catch (HibernateException e) {
            rollback(transaction);
            logger.error("Can not load auto complete list of Expense", e);
        } catch (Exception e) {
            rollback(transaction);
            logger.error("Method fillExpenseDataTableBetweenDates", e);
        }

        return results;
    }
    
     public List<ExpenseCompany> fillExpenseCompanyDataTableBetweenDatesEgresosGeneral(Date starDate, Date endDate, String location) {
        logger.info("fillExpenseDataTableBetweenDates");

        List<ExpenseCompany> results = null;
        Transaction transaction = null;

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

           
            Predicate predicateType = builder.equal(root.get("idExpenseType").get("expenseIncomeCatalogType"),ExpenseIncomeType.EXPENSE);
            Predicate predicateInversion = builder.equal(root.get("idExpenseType").get("expenseCatalogType"),ExpenseCatalogType.EXPENSE);
            Predicate predicateBox = builder.or(builder.equal(root.get("idExpenseType").get("activeBox"), BoxType.GENERAL),builder.equal(root.get("idExpenseType").get("activeBox"), BoxType.BOTH));
            Predicate startDatePredicate = builder.greaterThanOrEqualTo(root.get("createdOn"), starDate);
            Predicate endDatePredicate = builder.lessThanOrEqualTo(root.get("createdOn"), endDate);
            Predicate locationPredicate = builder.equal(root.get("location").get("id"), location);
            
             query.where(builder.and(
                  predicateBox,predicateType,predicateInversion,startDatePredicate, endDatePredicate, locationPredicate
            ));
            query.orderBy(builder.asc(root.get("createdOn")));

            results = session.createQuery(query).getResultList();
            transaction.commit();
            logger.info("Total of Expense founds in auto complete search " + results.size());
        } catch (HibernateException e) {
            rollback(transaction);
            logger.error("Can not load auto complete list of Expense", e);
        } catch (Exception e) {
            rollback(transaction);
            logger.error("Method fillExpenseDataTableBetweenDates", e);
        }

        return results;
    }
     
     public List<ExpenseCompany> fillExpenseCompanyDataTableBetweenDatesEgresosGeneralInversion(Date starDate, Date endDate, String location) {
        logger.info("fillExpenseDataTableBetweenDates");

        List<ExpenseCompany> results = null;
        Transaction transaction = null;

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

           
            Predicate predicateType = builder.equal(root.get("idExpenseType").get("expenseIncomeCatalogType"),ExpenseIncomeType.EXPENSE);
            Predicate predicateInversion = builder.equal(root.get("idExpenseType").get("expenseCatalogType"),ExpenseCatalogType.INVESTMENT);
            Predicate predicateBox = builder.or(builder.equal(root.get("idExpenseType").get("activeBox"), BoxType.GENERAL),builder.equal(root.get("idExpenseType").get("activeBox"), BoxType.BOTH));
            Predicate startDatePredicate = builder.greaterThanOrEqualTo(root.get("createdOn"), starDate);
            Predicate endDatePredicate = builder.lessThanOrEqualTo(root.get("createdOn"), endDate);
            Predicate locationPredicate = builder.equal(root.get("location").get("id"), location);
            
             query.where(builder.and(
                  predicateBox,predicateType,predicateInversion, startDatePredicate, endDatePredicate, locationPredicate
            ));
            query.orderBy(builder.asc(root.get("createdOn")));

            results = session.createQuery(query).getResultList();
            transaction.commit();
            logger.info("Total of Expense founds in auto complete search " + results.size());
        } catch (HibernateException e) {
            rollback(transaction);
            logger.error("Can not load auto complete list of Expense", e);
        } catch (Exception e) {
            rollback(transaction);
            logger.error("Method fillExpenseDataTableBetweenDates", e);
        }

        return results;
    }

    /**
     * Update active status (ENEBLED/DISABLED) of Expense company ID.
     *
     * @param id
     * @param status
     * @param lastUpdatedBy
     * @return
     */
    public boolean updateExpenseCompanyStatus(String id, BasicType status, User lastUpdatedBy) {
        logger.info("updateExpenseCompanyStatus");

        boolean success = false;
        Transaction transaction = null;

        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();
            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaUpdate<ExpenseCompany> query = builder.createCriteriaUpdate(ExpenseCompany.class);
            Root<ExpenseCompany> root = query.from(ExpenseCompany.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 expense company status");
                rollback(transaction);
            }
        } catch (HibernateException e) {
            logger.error("Can not update expense company status", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method updateExpenseCompanyStatus() ", e);
            rollback(transaction);
        }
        return success;
    }

    /**
     * Save a new expense company entry.
     *
     * @param expenseCompany
     * @return
     */
    public boolean saveExpenseCompany(ExpenseCompany expenseCompany) {
        logger.info("saveExpenseCompany");

        boolean success = false;
        Transaction transaction = null;

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

            session.save(expenseCompany);

            transaction.commit();
            success = true;
        } catch (HibernateException e) {
            logger.error("Can not save expense company entry", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method saveExpenseCompany()", e);
            rollback(transaction);
        }
        return success;
    }
    
    public List<ExpenseCompany> fillExpenseDataTablePendingDeposit(ExpenseCompanyType type,Location location) {
        logger.info("fillExpenseDataTablePendingDeposit");

        List<ExpenseCompany> results = null;
        Transaction transaction = null;

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

            
            Predicate expenseCompanyType = builder.equal(root.get("expenseCompanyType"), type);
            Predicate pending = builder.equal(root.get("depositStatus"), ExpenseStatusType.PENDING);
            Predicate bankCardNotNull = builder.isNotNull(root.get("bankCard"));
           
            Predicate locationPredicate = builder.equal(root.get("location"), location);
            
             query.where(builder.and( expenseCompanyType,locationPredicate, pending,bankCardNotNull
            ));
            query.orderBy(builder.asc(root.get("createdOn")));

            results = session.createQuery(query).getResultList();
            transaction.commit();
            logger.info("Total of Expense founds in auto complete search " + results.size());
        } catch (HibernateException e) {
            rollback(transaction);
            logger.error("Can not load auto complete list of Expense", e);
        } catch (Exception e) {
            rollback(transaction);
            logger.error("Method fillExpenseDataTableBetweenDates", e);
        }

        return results;
    }
    
      public boolean autorizarExpense(String expenseId, User user) {
        logger.info("autorizar");

        boolean success = false;
        Transaction transaction = null;
        try {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            transaction = session.beginTransaction();

            CriteriaBuilder builder = session.getCriteriaBuilder();
            CriteriaUpdate<ExpenseCompany> update = builder.createCriteriaUpdate(ExpenseCompany.class);
            Root<ExpenseCompany> rootUpdate = update.from(ExpenseCompany.class);

            update.set("depositStatus", ExpenseStatusType.AUTHORIZED);
            update.set("lastUpdatedBy", user);
            update.set("lastUpdatedOn", new Date());
            update.where(builder.equal(rootUpdate.get("id"), expenseId));

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

            if (1 == total) {
                transaction.commit();
                success = true;
                logger.info("ExpenseCompany " + expenseId + " updated");
            } else {
                transaction.rollback();
                logger.error("Error ExpenseCompany updated total: " + total);
            }
        } catch (HibernateException e) {
            logger.error("ExpenseCompany can not be updated ", e);
            rollback(transaction);
        } catch (Exception e) {
            logger.error("Method autorizar() ", e);
            rollback(transaction);
        }
        return success;
    }

    private static final long serialVersionUID = -8772793021819350069L;
    final Logger logger = LogManager.getLogger(ExpenseCompanyController.class);
}
