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

import com.arrebol.apc.controller.util.ConnectionManager;
import com.arrebol.apc.controller.util.HibernateUtil;
import com.arrebol.apc.model.ModelParameter;
import com.arrebol.apc.model.admin.ClosingDay;
import com.arrebol.apc.model.admin.ClosingDayDetail;
import com.arrebol.apc.model.admin.constance.ClosingDayCfg;
import com.arrebol.apc.model.admin.constance.ClosingDayDetailCfg;
import com.arrebol.apc.model.catalog.People;
import com.arrebol.apc.model.core.Office;
import com.arrebol.apc.model.core.User;
import com.arrebol.apc.model.enums.ActiveStatus;
import com.arrebol.apc.model.views.ClosingDailyDetailFromUserByCurdateView;
import com.arrebol.apc.model.views.ClosingDailyDetailFromUserByLastClosingDayView;
import com.arrebol.apc.model.views.LoanByUserPaymentZeroView;
import com.arrebol.apc.model.views.TotalCashByCurdateDashboardView;
import com.arrebol.apc.model.views.TotalCashByCurdateView;
import com.arrebol.apc.model.views.TotalCashByLastClosingDayView;
import com.arrebol.apc.model.views.TotalClosingDayByCurdateView;
import com.arrebol.apc.model.views.constance.ClosingDailyDetailFromUserByCurdateCfg;
import com.arrebol.apc.model.views.constance.ClosingDailyDetailFromUserByLastClosingDayCfg;
import com.arrebol.apc.model.views.constance.LoanByUserPaymentZeroViewCfg;
import com.arrebol.apc.model.views.constance.TotalLoansByOfficeViewCfg;
import com.arrebol.apc.repository.GenericEntityRepository;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
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 Oscar Armando Vargas Cardenas <oscar.vargas@arrebol.com.mx>
 */
public class ClosingDayController extends ConnectionManager implements Serializable {

   /**
    *
    * Searching all closing day by office.
    *
    * @param officeId
    * @param startDate
    * @param endDate
    * @return
    */
   public List<ClosingDay> fillClosingDayDatatable(String officeId, Date startDate, Date endDate) {
      logger.debug("fillClosingDayDatatable");
      List<ModelParameter> parameters = new ArrayList<>();

      Calendar calendar = Calendar.getInstance();
      calendar.setTime(startDate);
      calendar.add(Calendar.DAY_OF_YEAR, 1);
      startDate = calendar.getTime();

      Calendar calendarF = Calendar.getInstance();
      calendarF.setTime(endDate);
      calendarF.add(Calendar.DAY_OF_YEAR, 1);
      endDate = calendarF.getTime();

      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_OFFICE, new Office(officeId)));
      //parameters.add(new ModelParameter(ClosingDayCfg.FIELD_ACTIVE_STATUS, ActiveStatus.ENEBLED));
      parameters.add(new ModelParameter(ClosingDayCfg.PARAM_START_DATE, startDate));
      parameters.add(new ModelParameter(ClosingDayCfg.PARAM_END_DATE, endDate));

      return genericEntityRepository.xmlQueryAPCEntities(ClosingDay.class, ClosingDayCfg.QUERY_FIND_ALL_CLOSING_DAY_BY_OFFICE_BETWEEN_DATES, parameters);
   }

   /**
    *
    * @param status
    * @param closingDayIdToUpdate
    * @param lastUpdatedBy
    * @return
    */
   public boolean updateClosingDayByStatus(ActiveStatus status, String closingDayIdToUpdate, String lastUpdatedBy) {
      logger.debug("updateClosingDayByStatus");

      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_ACTIVE_STATUS, status));
      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_LAST_UPDATED_BY, lastUpdatedBy));
      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_LAST_UPDATED_ON, new Date()));
      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_ID, closingDayIdToUpdate));

      return genericEntityRepository.xmlUpdateOrDeleteAPCEntity(ClosingDayCfg.QUERY_UPDATE_CLOSING_DAY_BY_STATUS, parameters);
   }

   /**
    *
    * @param closingDay
    * @return boolean
    */
   public boolean saveClosingDay(ClosingDay closingDay) {
      logger.debug("saveClosingDay");
      boolean success = genericEntityRepository.insertAPCEntity(closingDay);

      return success;
   }

   /**
    *
    * Searching TotalCashByCurdateView by id.
    *
    * @param id
    * @return
    */
   public TotalCashByCurdateView getTotalCashByCurdateViewById(String id) {
      logger.debug("getTotalCashByCurdateViewById");

      return (TotalCashByCurdateView) genericEntityRepository.selectAPCEntityById(TotalCashByCurdateView.class, id);
   }

   /**
    *
    * Searching TotalCashByLastClosingDayView by id.
    *
    * @param id
    * @return
    */
   public TotalCashByLastClosingDayView getTotalCashByLastClosingDayViewById(String id) {
      logger.debug("getTotalCashByLastClosingDayViewById");
      return (TotalCashByLastClosingDayView) genericEntityRepository.selectAPCEntityById(TotalCashByLastClosingDayView.class, id);
   }

   /**
    *
    * Searching all total money day by office.
    *
    * @param officeId
    * @return
    */
   public List<TotalCashByCurdateView> getAllTotalCashByCurdateView(String officeId) {
      logger.debug("getAllTotalCashByCurdateView");
      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_VIEW_OFFICE, officeId));

      return genericEntityRepository.xmlQueryAPCEntities(TotalCashByCurdateView.class, ClosingDayCfg.QUERY_FIND_TOTAL_BY_OFFICE, parameters);
   }

   public List<TotalCashByCurdateDashboardView> getAllTotalCashByCurdateDashboardView(String officeId) {
      logger.debug("getAllTotalCashByCurdateDashboardView");
      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_VIEW_OFFICE, officeId));

      return genericEntityRepository.xmlQueryAPCEntities(TotalCashByCurdateDashboardView.class, ClosingDayCfg.QUERY_FIND_TOTAL_DASHBOARD_BY_OFFICE, parameters);
   }

   /**
    *
    * Searching all TotalClosingDayByCurdateView by curdate.
    *
    * @param officeId
    * @return
    */
   public List<TotalClosingDayByCurdateView> findAllClosingDayByCurdate(String officeId) {
      logger.debug("findAllClosingDayByCurdate");
      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_VIEW_OFFICE, officeId));

      return genericEntityRepository.xmlQueryAPCEntities(TotalClosingDayByCurdateView.class, ClosingDayCfg.QUERY_FIND_ALL_CLOSING_DAY_BY_CURDATE, parameters);
   }

   /**
    *
    * Searching all ClosingDailyDetailFromUserByCurdateView by curdate.
    *
    * @param userId
    * @return
    */
   public List<ClosingDailyDetailFromUserByCurdateView> findAllClosingDailyDetailFromUserByCurdateView(String userId) {
      logger.debug("findAllClosingDailyDetailFromUserByCurdateView");
      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(ClosingDailyDetailFromUserByCurdateCfg.FIELD_VIEW_USER, userId));

      return genericEntityRepository.xmlQueryAPCEntities(ClosingDailyDetailFromUserByCurdateView.class, ClosingDailyDetailFromUserByCurdateCfg.QUERY_FIND_ALL_DETAILS, parameters);
   }

   /**
    *
    * Searching all ClosingDailyDetailFromUserByLastClosingDayView by curdate.
    *
    * @param userId
    * @return
    */
   public List<ClosingDailyDetailFromUserByLastClosingDayView> findAllClosingDailyDetailFromUserByLastClosingDayView(String userId) {
      logger.debug("findAllClosingDailyDetailFromUserByLastClosingDayView");
      List<ModelParameter> parameters = new ArrayList<>();
      parameters.add(new ModelParameter(ClosingDailyDetailFromUserByLastClosingDayCfg.FIELD_VIEW_USER, userId));
      return genericEntityRepository.xmlQueryAPCEntities(ClosingDailyDetailFromUserByLastClosingDayView.class, ClosingDailyDetailFromUserByLastClosingDayCfg.QUERY_FIND_ALL_DETAILS, parameters);
   }

   /**
    *
    * Searching all ClosingDailyDetailFromUserByCurdateView by curdate.
    *
    * @param userId
    * @return
    */
   public List<ClosingDailyDetailFromUserByCurdateView> findAllClosingDailyDetailFromUserCertifierByCurdateView(String userId) {
      logger.debug("findAllClosingDailyDetailFromUserCertifierByCurdateView");
      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(ClosingDailyDetailFromUserByCurdateCfg.FIELD_VIEW_USER, userId));

      return genericEntityRepository.xmlQueryAPCEntities(ClosingDailyDetailFromUserByCurdateView.class, ClosingDailyDetailFromUserByCurdateCfg.QUERY_FIND_ALL_DETAILS_CERTIFIER, parameters);
   }

   /**
    *
    * Searching all ClosingDailyDetailFromUserByLastClosingDayView by curdate.
    *
    * @param userId
    * @return
    */
   public List<ClosingDailyDetailFromUserByLastClosingDayView> findAllClosingDailyDetailFromUserCertifierByLastClosingDayView(String userId) {
      logger.debug("findAllClosingDailyDetailFromUserCertifierByLastClosingDayView");
      List<ModelParameter> parameters = new ArrayList<>();
      parameters.add(new ModelParameter(ClosingDailyDetailFromUserByLastClosingDayCfg.FIELD_VIEW_USER, userId));
      return genericEntityRepository.xmlQueryAPCEntities(ClosingDailyDetailFromUserByLastClosingDayView.class, ClosingDailyDetailFromUserByLastClosingDayCfg.QUERY_FIND_ALL_DETAILS_CERTIFIER, parameters);
   }

   /**
    *
    * Searching the num by closing day
    *
    * @param num for closing day
    * @return
    */
   public Long getClosingDayCurdateByUserId(String userId, String officeid) {
      logger.debug("getClosingDayCurdateByUserId");
      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_USER, new User(userId)));
      parameters.add(new ModelParameter(ClosingDayCfg.FIELD_OFFICE, new Office(officeid)));

      return (Long) genericEntityRepository.xmlQueryAPCEntityUniqueResult(Long.class, ClosingDayCfg.QUERY_COUNT_CLOSING_DATE_BY_USER_AND_OFFICE, parameters);
   }

   public Long getLoansCountDailyByUser(String userId) {
      logger.debug("getLoansCountDailyByUser");
      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(TotalLoansByOfficeViewCfg.FIELD_VIEW_USER, userId));

      return (Long) genericEntityRepository.xmlQueryAPCEntityUniqueResult(Long.class, TotalLoansByOfficeViewCfg.QUERY_COUNT_ALL_LOANS_BY_USER, parameters);
   }

   public List<LoanByUserPaymentZeroView> getLoansPaymentZeroByUser(String userId) {
      logger.debug("getLoansPaymentZeroByUser");
      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(LoanByUserPaymentZeroViewCfg.FIELD_VIEW_USER, new User(userId)));

      return genericEntityRepository.xmlQueryAPCEntities(LoanByUserPaymentZeroView.class, LoanByUserPaymentZeroViewCfg.QUERY_FIND_ALL_LOANS_PAYMENT_ZERO_BY_USER, parameters);
   }

   /**
    *
    * @param details
    * @return boolean
    */
   public boolean saveClosingDayDetail(List<ClosingDayDetail> details) {
      logger.debug("saveClosingDayDetail");
      boolean success = genericEntityRepository.insertManyAPCEntity(details);

      return success;
   }

   /**
    *
    * Searching closing day by id.
    *
    * @param closingDayId
    * @return
    */
   public ClosingDay getClosingDayById(String closingDayId) {
      logger.debug("getClosingDayById");

      return (ClosingDay) genericEntityRepository.selectAPCEntityById(ClosingDay.class, closingDayId);
   }

   public List<ClosingDayDetail> getClosingDayDetailByIdClosingDay(String closingDay) {
      logger.debug("getClosingDayDetailByIdClosingDay");
      List<ModelParameter> parameters = new ArrayList<>();

      parameters.add(new ModelParameter(ClosingDayDetailCfg.FIELD_CLOSING_DAY, new ClosingDay(closingDay)));

      return genericEntityRepository.xmlQueryAPCEntities(ClosingDayDetail.class, ClosingDayDetailCfg.QUERY_FIND_ALL_CLOSING_DAY_DETAIL_BY_ID, parameters);
   }

   public User getUserById(String userId) {
      logger.debug("getUserById");

      return (User) genericEntityRepository.selectAPCEntityById(User.class, userId);
   }

   /**
    *
    * @param date
    * @return
    */
   public boolean existNextPaidClosingDayByCreatedOn(Date date) {
      logger.info("existNextPaidClosingDayByCreatedOn");

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

      return genericEntityRepository.existRecordsUsingSQLQueryFindByCreatedOnField(date, query);
   }

   /**
    *
    * @param date
    * @return
    */
   public boolean existStableSmallBoxByCreatedOn(Date date) {
      logger.info("existStableSmallBoxByCreatedOn");

      String query = "SELECT ssb.id "
              + "FROM APC_STABLE_SMALL_BOX ssb "
              + "WHERE DATE(ssb.created_on) = DATE(:date) "
              + "AND ssb.active_status = 'ENEBLED'";

      return genericEntityRepository.existRecordsUsingSQLQueryFindByCreatedOnField(date, query);
   }

   /**
    *
    * @param userId
    * @return
    */
   public List<Object> findDetailsFromClosingDayCurDate(String userId) {
      logger.debug("findDetailsFromClosingDayCurDate");

      List<Object> rows = new ArrayList<>();
      Transaction transaction = null;

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

         rows = session.createNativeQuery(queryClosingDayReport).setParameter("userId", userId).getResultList();

         transaction.commit();

      } catch (HibernateException e) {
         logger.error("Driver", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method findDetailsFromClosingDayCurDate(" + userId + " " + ") ", e);
         rollback(transaction);
      }

      return rows;
   }

   public List<Object> findDetailsFromClosingDayHisotry(String idClosingDay) {
      logger.debug("findDetailsFromClosingDayCurDate");

      List<Object> rows = new ArrayList<>();
      Transaction transaction = null;

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

         rows = session.createNativeQuery(queryClosingDayReportHistory).setParameter("idClosingDay", idClosingDay).getResultList();

         transaction.commit();

      } catch (HibernateException e) {
         logger.error("Driver", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method findDetailsFromClosingDayHistory(" + idClosingDay + " " + ") ", e);
         rollback(transaction);
      }

      return rows;
   }

   /**
    *
    * Searching people.
    *
    * @param peopleId
    * @return
    */
   public People findPeopleById(String peopleId) {
      logger.debug("findPeopleById");

      return (People) genericEntityRepository.selectAPCEntityById(User.class, peopleId);
   }

   final Logger logger = LogManager.getLogger(ClosingDayController.class);
   private final GenericEntityRepository genericEntityRepository;

   public ClosingDayController() {
      this.genericEntityRepository = new GenericEntityRepository();
   }

   private final String queryClosingDayReport
           = "SELECT  "
           + "cldd.comments, "
           + "cldd.amount, "
           + "cldd.type, "
           + "cldd.saldo, "
           + "cldd.created_on, "
           + "cldd.comisionApertura, "
           + "cldd.prestamoAnterior, "
           + "cldd.route, "
           + "CONCAT(hr.first_name,' ',IF(ISNULL(hr.second_name) ,'', CONCAT(hr.second_name, ' ')),hr.last_name,' ', hr.middle_name) AS username, "
           + "(SELECT sum(tc.total_amount_payment) FROM APC_TOTAL_CASH_BY_CURDATE_VIEW tc where id = :userId) as total_amount_payment, "
           + "(SELECT sum(tc.total_amount_deposit) FROM APC_TOTAL_CASH_BY_CURDATE_VIEW tc where id = :userId) as total_amount_deposit, "
           + "(SELECT sum(tc.transfer_sender) FROM APC_TOTAL_CASH_BY_CURDATE_VIEW tc where id = :userId) as transfer_sender, "
           + "(SELECT sum(tc.transfer_receiver) FROM APC_TOTAL_CASH_BY_CURDATE_VIEW tc where id = :userId) as transfer_receiver, "
           + "(SELECT sum(tc.money_daily) FROM APC_TOTAL_CASH_BY_CURDATE_VIEW tc where id = :userId) as money_daily, "
           + "(SELECT sum(tc.other_expense) FROM APC_TOTAL_CASH_BY_CURDATE_VIEW tc where id = :userId) as other_expense, "
           + "(SELECT sum(tc.delivery) FROM APC_TOTAL_CASH_BY_CURDATE_VIEW tc where id = :userId) as delivery, "
           + "(SELECT sum(tc.transfer_pending) FROM APC_TOTAL_CASH_BY_CURDATE_VIEW tc where id = :userId) as transfer_pending "
           + "FROM "
           + "	APC_CLOSING_DAILY_DETAIL_FROM_USER_BY_LAST_CLOSING_VIEW_REPORT cldd "
           + "INNER JOIN "
           + "	APC_USER u ON u.id = cldd.id_user "
           + "INNER JOIN "
           + "	APC_HUMAN_RESOURCE hr ON hr.id = u.id_human_resource "
           + "WHERE  "
           + "	cldd.id_user = :userId "
           + "ORDER BY "
           + "	cldd.created_on ASC";

   private final String queryClosingDayReportHistory
           = "   SELECT  "
           + "   cldd.comments, "
           + "   cldd.amount, "
           + "   cldd.type, "
           + "   cldd.created_on, "
           + "   cd.amount_expected, "
           + "   cd.amount_paid, "
           + "   CONCAT(hr.first_name,' ',IF(ISNULL(hr.second_name) ,'', CONCAT(hr.second_name, ' ')),hr.last_name,' ', hr.middle_name) AS username, "
           + "   (SELECT IFNULL(SUM(cld.amount),0) FROM APC_CLOSING_DAY_DETAIL cld WHERE cld.type='Abono' AND cld.id_closing_day = :idClosingDay) as total_amount_payment, "
           + "   (SELECT IFNULL(SUM(cld.amount),0) FROM APC_CLOSING_DAY_DETAIL cld WHERE cld.type='Depósito' AND cld.id_closing_day = :idClosingDay) as total_amount_deposit, "
           + "   (SELECT IFNULL(SUM(cld.amount),0) FROM APC_CLOSING_DAY_DETAIL cld WHERE cld.type='Transferencia enviada' AND cld.id_closing_day = :idClosingDay) as transfer_sender, "
           + "   (SELECT IFNULL(SUM(cld.amount),0) FROM APC_CLOSING_DAY_DETAIL cld WHERE cld.type='Transferencia recibida' AND cld.id_closing_day = :idClosingDay) as transfer_receiver, "
           + "   (SELECT IFNULL(SUM(cld.amount),0) FROM APC_CLOSING_DAY_DETAIL cld WHERE cld.type='Inicio' AND cld.id_closing_day = :idClosingDay) as money_daily, "
           + "   (SELECT IFNULL(SUM(cld.amount),0) FROM APC_CLOSING_DAY_DETAIL cld WHERE cld.type='Gasto' AND cld.id_closing_day = :idClosingDay) as other_expense, "
           + "   (SELECT IFNULL(SUM(cld.amount),0) FROM APC_CLOSING_DAY_DETAIL cld WHERE cld.type='Entrega de préstamo' AND cld.id_closing_day = :idClosingDay) as delivery, "
           + "   (SELECT IFNULL(SUM(cld.amount),0) FROM APC_CLOSING_DAY_DETAIL cld WHERE cld.type='Multa' AND cld.id_closing_day = :idClosingDay) as total_amount_fee, "
           + "   cldd.dateDetail "
           + "   FROM "
           + "   APC_CLOSING_DAY_DETAIL cldd "
           + "   INNER JOIN "
           + "   APC_CLOSING_DAY cd on cldd.id_closing_day = cd.id"
           + "   INNER JOIN"
           + "   APC_USER u ON u.id = cd.id_user "
           + "   INNER JOIN "
           + "   APC_HUMAN_RESOURCE hr ON hr.id = u.id_human_resource "
           + "   WHERE "
           + "   cldd.id_closing_day = :idClosingDay"
           + "   ORDER BY"
           + "   cldd.created_on ASC;  ";
}
