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

import com.arrebol.taxiservicios.controller.connection.ConnectionManager;
import com.arrebol.taxiservicios.controller.util.HibernateUtil;
import com.arrebol.taxiservicios.model.catalog.Location;
import com.arrebol.taxiservicios.model.core.Address;
import com.arrebol.taxiservicios.model.core.User;
import com.arrebol.taxiservicios.model.enums.EstatusSolicitud;
import com.arrebol.taxiservicios.model.enums.GenericEnumType;
import com.arrebol.taxiservicios.model.enums.PolizaEstatus;
import com.crov.prase.model.prase.DetellePagoPoliza;
import com.crov.prase.model.prase.PagosPoliza;
import com.crov.prase.model.prase.Poliza;
import com.crov.prase.model.prase.SolicitudCancelacionPoliza;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import javax.persistence.NoResultException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
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 Oscar Vargas
 */
public class PolizaController extends ConnectionManager implements Serializable {

   public PolizaController() {
   }

   public Poliza findPoliza(String idPoliza) {
      logger.info("findPoliza");
      Poliza results = null;
      Transaction transaction = null;
      try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         CriteriaBuilder builder = session.getCriteriaBuilder();

         CriteriaQuery<Poliza> query = builder.createQuery(Poliza.class);
         Root<Poliza> root = query.from(Poliza.class);

         Predicate id = builder.equal(root.get("id"), idPoliza);
         query.where(builder.and(id));
         results = session.createQuery(query).getSingleResult();
         transaction.commit();

         logger.info("Poliza" + results.getId());
      } catch (HibernateException e) {
         logger.error("Can not load Poliza", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method findPoliza() ", e);
         rollback(transaction);
      }
      return results;
   }

   public Address findAddress(String idPerson) {
      logger.info("findAddress");
      Address results = null;
      Transaction transaction = null;
      try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         CriteriaBuilder builder = session.getCriteriaBuilder();

         CriteriaQuery<Address> query = builder.createQuery(Address.class);
         Root<Address> root = query.from(Address.class);

         Predicate id = builder.equal(root.get("person").get("id"), idPerson);
         query.where(builder.and(id));
         results = session.createQuery(query).getSingleResult();
         transaction.commit();

         logger.info("Address" + results.getId());
      } catch (NoResultException e) {
         transaction.rollback();
         logger.error("No se encontro el address: ");
      } catch (HibernateException e) {
         rollback(transaction);
         logger.error("Error al encontrar el address: ", e);
      } catch (Exception e) {
         rollback(transaction);
         logger.error("Método findAddress: ", e);
      }
      return results;
   }

   public List<Poliza> getAllPolizasByLocation(Location location) {
      logger.info("findDisable");
      List<Poliza> results = new ArrayList<>();

      Transaction transaction = null;

      try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         CriteriaBuilder builder = session.getCriteriaBuilder();
         CriteriaQuery<Poliza> query = builder.createQuery(Poliza.class);
         Root<Poliza> root = query.from(Poliza.class);
         Predicate locations = builder.equal(root.get("location"), location);

         query.where(builder.and(locations));
         query.orderBy(builder.asc(root.get("createdOn")));

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

         transaction.commit();

         logger.info("Poliza's list " + results.size());
      } catch (HibernateException e) {
         logger.error("Can not load Poliza list ", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method findDisable() ", e);
         rollback(transaction);
      }
      return results;
   }

   /**
    * @param location
    * @return List of all Poliza.
    */
   public List<Poliza> findActive(Location location) {
      logger.info("findActive");
      List<Poliza> results = new ArrayList<>();

      Transaction transaction = null;

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

         Predicate policyActives = builder.equal(root.get("active"), Boolean.TRUE);
         Predicate locations = builder.equal(root.get("location"), location);

         query.where(builder.and(
                 policyActives, locations
         ));
         query.orderBy(builder.asc(root.get("createdOn")));

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

         transaction.commit();

         logger.info("Poliza's list " + results.size());
      } catch (HibernateException e) {
         logger.error("Can not load Poliza list ", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method findActive() ", e);
         rollback(transaction);
      }
      return results;
   }

   public List<Poliza> findDisable(Location location) {
      logger.info("findDisable");
      List<Poliza> results = new ArrayList<>();

      Transaction transaction = null;

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

         Predicate policyDisables = builder.equal(root.get("active"), Boolean.FALSE);
         Predicate locations = builder.equal(root.get("location"), location);

         query.where(builder.and(
                 policyDisables, locations
         ));
         query.orderBy(builder.asc(root.get("createdOn")));

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

         transaction.commit();

         logger.info("Poliza's list " + results.size());
      } catch (HibernateException e) {
         logger.error("Can not load Poliza list ", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method findDisable() ", e);
         rollback(transaction);
      }
      return results;
   }

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

      boolean success = false;
      Transaction transaction = null;

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

         query.set(root.get("comentarios"), comentario);
         query.set(root.get("active"), false);
         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 Poliza status to disable");
         } else {
            logger.info("CAN NOT update Poliza status");
            rollback(transaction);
         }
      } catch (HibernateException e) {
         logger.error("Can not update Poliza status", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method delete() ", e);
         rollback(transaction);
      }
      return success;
   }

   /**
    * Save a new poliza entry.
    *
    * @param poliza
    * @return
    */
   public boolean save(Poliza poliza) {
      logger.info("saveHospitales");

      boolean success = false;
      Transaction transaction = null;

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

         session.save(poliza);

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

   public boolean save(PagosPoliza pagoPoliza) {
      logger.info("saveHospitales");

      boolean success = false;
      Transaction transaction = null;

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

         session.save(pagoPoliza);

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

   public boolean save(DetellePagoPoliza detallePagoPoliza) {
      logger.info("saveHospitales");
      boolean success = false;
      Transaction transaction = null;
      try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         session.save(detallePagoPoliza);
         transaction.commit();
         success = true;
      } catch (HibernateException e) {
         logger.error("Can not save poliza entry", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method save()", e);
         rollback(transaction);
      }
      return success;
   }

   public boolean saveSolicitudCancelacionPoliza(SolicitudCancelacionPoliza poliza, User user) {
      logger.info("saveSolicitudCancelacionPoliza");
      boolean success = false;
      Transaction transaction = null;
      try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         poliza.setId(UUID.randomUUID().toString());
         poliza.setCreatedBy(user);
         poliza.setCreatedOn(correcciónDeHr(new Date(), -7));
         session.save(poliza);
         transaction.commit();
         success = true;
      } catch (HibernateException e) {
         logger.error("Can not save solicitud entry", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method saveSolicitudCancelacionPoliza()", e);
         rollback(transaction);
      }
      return success;
   }

   public boolean updateSolicitudCancelacionPoliza(SolicitudCancelacionPoliza poliza, User user) {
      logger.info("saveSolicitudCancelacionPoliza");
      boolean success = false;
      Transaction transaction = null;
      try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         poliza.setLastUpdatedBy(user);
         poliza.setLastUpdatedOn(correcciónDeHr(new Date(), -7));
         session.update(poliza);
         transaction.commit();
         success = true;
      } catch (HibernateException e) {
         logger.error("Can not save solicitud entry", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method saveSolicitudCancelacionPoliza()", e);
         rollback(transaction);
      }
      return success;
   }

   public List<SolicitudCancelacionPoliza> findSolicitudCancelacionPolizaByLocation(Location location) {
      logger.info("findSolicitudCancelacionPolizaByLocation");
      List<SolicitudCancelacionPoliza> results = new ArrayList<>();
      Transaction transaction = null;
      try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         CriteriaBuilder builder = session.getCriteriaBuilder();
         CriteriaQuery<SolicitudCancelacionPoliza> query = builder.createQuery(SolicitudCancelacionPoliza.class);
         Root<SolicitudCancelacionPoliza> root = query.from(SolicitudCancelacionPoliza.class);

         Predicate predicate1 = builder.equal(root.get("estatusActivo"), GenericEnumType.ENABLED);
         Predicate predicate2 = builder.equal(root.get("location"), location);
         query.where(builder.and(predicate1, predicate2));
         query.orderBy(builder.asc(root.get("createdOn")));

         results = session.createQuery(query).getResultList();
         transaction.commit();
         logger.info("Solicitudes size: " + results.size());
      } catch (HibernateException e) {
         logger.error("Can not load solicitudes list ", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method findSolicitudCancelacionPolizaByLocation() ", e);
         rollback(transaction);
      }
      return results;
   }

   public List<SolicitudCancelacionPoliza> findSolicitudCancelacionPolizaByPolizaAndEstatus(Poliza poliza, EstatusSolicitud estatus) {
      logger.info("findSolicitudCancelacionPolizaByLocation");
      List<SolicitudCancelacionPoliza> results = new ArrayList<>();
      Transaction transaction = null;
      try {
         Session session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();
         CriteriaBuilder builder = session.getCriteriaBuilder();
         CriteriaQuery<SolicitudCancelacionPoliza> query = builder.createQuery(SolicitudCancelacionPoliza.class);
         Root<SolicitudCancelacionPoliza> root = query.from(SolicitudCancelacionPoliza.class);

         Predicate predicate1 = builder.equal(root.get("estatusActivo"), GenericEnumType.ENABLED);
         Predicate predicate2 = builder.equal(root.get("poliza"), poliza);
         Predicate predicate3 = builder.equal(root.get("estatusSolicitud"), estatus);
         query.where(builder.and(predicate1, predicate2, predicate3));
         query.orderBy(builder.asc(root.get("createdOn")));

         results = session.createQuery(query).getResultList();
         transaction.commit();
         logger.info("Solicitudes size: " + results.size());
      } catch (HibernateException e) {
         logger.error("Can not load solicitudes list ", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method findSolicitudCancelacionPolizaByLocation() ", e);
         rollback(transaction);
      }
      return results;
   }

   public boolean aprovarCanselacion(SolicitudCancelacionPoliza solisitud, User user) {
      logger.info("aprovarCanselacion()");
      Session session = null;
      Transaction transaction = null;
      Date fechaActual = correcciónDeHr(new Date(), -7);
      try {
         session = HibernateUtil.getSessionFactory().getCurrentSession();
         transaction = session.beginTransaction();

         // Se actuliza la poliza
         Poliza selectPolisa = solisitud.getPoliza();
         selectPolisa.setEstatus(PolizaEstatus.CANCELADO);
         selectPolisa.setLastUpdatedBy(user);
         selectPolisa.setLastUpdatedOn(fechaActual);
         session.update(selectPolisa);
         // Se actualiza la solicitud
         solisitud.setEstatusSolicitud(EstatusSolicitud.APROBADO);
         solisitud.setFechaPago(fechaActual);
         solisitud.setLastUpdatedBy(user);
         solisitud.setLastUpdatedOn(fechaActual);
         session.update(solisitud);

         transaction.commit();
         return true;

      } catch (Exception e) {
         if (transaction != null) {
            transaction.rollback();
         }
         logger.error("aprovarCanselacion(): ", e);
         return false;
      } finally {
         if (session != null) {
            session.close();
         }
      }
   }

   public Poliza fillFoliosByPolizas(String abreviacionFolio) {
      Poliza result = null;
      Transaction transaction = null;
      try {
         logger.info("Search last generated folio..");

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

         String query = "SELECT tap FROM Poliza tap "
                 + "WHERE tap.folio LIKE '" + abreviacionFolio + "%' "
                 + "ORDER BY "
                 + "SUBSTRING_INDEX(tap.folio, '-', 1) DESC, "
                 + "SUBSTRING_INDEX(SUBSTRING_INDEX(tap.folio, '-', -1), '-', -6) DESC ";

         result = (Poliza) session.createQuery(query)
                 .setMaxResults(1)
                 .uniqueResult();

         transaction.commit();
      } catch (HibernateException e) {
         logger.error("Search for the last generated folio " + e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method fillFoliosByPagos() " + e);
         rollback(transaction);
      }
      return result;
   }

   public Poliza fillFoliosByPolizas2(String abreviacionFolio) {
      Poliza result = null;
      Transaction transaction = null;
      try {
         logger.info("Search last generated folio..");

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

         String query = "SELECT tap FROM Poliza tap "
                 + "ORDER BY "
                 + "SUBSTRING_INDEX(SUBSTRING_INDEX(tap.folio, '-', -1), '-', -6) DESC ";

         result = (Poliza) session.createQuery(query)
                 .setMaxResults(1)
                 .uniqueResult();

         transaction.commit();
      } catch (HibernateException e) {
         logger.error("Search for the last generated folio " + e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method fillFoliosByPagos() " + e);
         rollback(transaction);
      }
      return result;
   }

   public List<Poliza> findPolizasByLocationAndEndDate(Location location, Date startDate, Date endDate) {
      logger.info("findActive");
      List<Poliza> results = new ArrayList<>();

      Transaction transaction = null;

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

         LocalDate localStartDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
         LocalDate localEndDate = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

         Predicate criterio = builder.equal(root.get("active"), Boolean.TRUE);
         Predicate criterio2 = builder.equal(root.get("active"), true);
         Predicate criterio3 = builder.equal(root.get("location"), location);
         Predicate criterio4 = builder.notEqual(root.get("estatus"), PolizaEstatus.CANCELADO);
         Predicate startDatePredicate = builder.greaterThanOrEqualTo(root.get("endDate").as(LocalDate.class), localStartDate);
         Predicate endDatePredicate = builder.lessThanOrEqualTo(root.get("endDate").as(LocalDate.class), localEndDate);

         query.where(builder.and(criterio, criterio2, criterio3, criterio4, startDatePredicate, endDatePredicate));
         query.orderBy(builder.asc(root.get("createdOn")));

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

         transaction.commit();

         logger.info("Poliza's list " + results.size());
      } catch (HibernateException e) {
         logger.error("Can not load Poliza list ", e);
         rollback(transaction);
      } catch (Exception e) {
         logger.error("Method findActive() ", e);
         rollback(transaction);
      }
      return results;
   }

   public boolean update(Poliza poliza) {
      logger.info("saveHospitales");

      boolean success = false;
      Transaction transaction = null;

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

         session.update(poliza);

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

   private Date correcciónDeHr(Date fecha, int horas) {
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(fecha);
      calendar.add(Calendar.HOUR_OF_DAY, horas);
      Date nuevaFecha = calendar.getTime();
      return nuevaFecha;
   }

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