package com.capgemini.reports.database.manager;

import com.capgemini.reports.database.HibernateUtil;
import com.capgemini.reports.database.javabean.DbCriteria;
import com.capgemini.reports.database.javabean.DbCriteriaVal;
import com.capgemini.reports.database.javabean.enumeration.ReportCriteriaEnum;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * Hibernate manager for DbCriteriaVal JAVA bean.
 **/
@Slf4j
public class MngCriteriaVal extends AbstractHibernateManager<DbCriteriaVal> {

  // Keys
  private static final String KEY_REPORT_UID = "reportUID";
  private static final String KEY_REPORT_ID = "reportId";

  // Queries
  private static final String QUERY_CRITERIA_BY_REPORT_UID;
  private static final String QUERY_CRITERIA_BY_REPORT_ID;

  static {
    String select = "select T2.criteriaName, T1.criteriaValue ";
    String from = "from DbReport T0, DbCriteriaVal T1, DbCriteria T2 ";
    String whereCoherence = "where T0.id = T1.reportId and T1.criteria = T2.id ";
    String whereReportUID = "and T0.reportUid = :" + KEY_REPORT_UID + " ";
    String whereReportID = "and T0.id = :" + KEY_REPORT_ID + " ";
    String orderBy = "order by T2.id";
    QUERY_CRITERIA_BY_REPORT_UID = select + from + whereCoherence + whereReportUID + orderBy;
    QUERY_CRITERIA_BY_REPORT_ID = select + from + whereCoherence + whereReportID + orderBy;
  }

  @Override
  public Class<DbCriteriaVal> getManagedClass() {
    return DbCriteriaVal.class;
  }

  /**
   * Create a new criteria value.
   */
  private DbCriteriaVal insert(Session session, final DbCriteria aCriteria, final String aCriteriaValue, final Long aReportId) {
    log.trace("Inserting DbCriteria entry {}({}) = {} for report {}", aCriteria.getId(), aCriteria.getCriteriaName(), aCriteriaValue, aReportId);
    DbCriteriaVal theCriteriaVal = new DbCriteriaVal();

    theCriteriaVal.setCriteria(aCriteria);
    theCriteriaVal.setCriteriaValue(aCriteriaValue);
    theCriteriaVal.setReportId(aReportId);

    // Persist object
    session.save(theCriteriaVal);

    return theCriteriaVal;
  }

  /**
   * Create a new criteria value using the criteria name as key.
   */
  DbCriteriaVal insertByCriteriaName(Session session, final String aCriteriaName, final String aCriteriaValue, final Long aReportId) {
    // Search the corresponding criteria using its name
    DbCriteria theCriteria = new MngCriteria().searchByName(session, aCriteriaName);

    return insert(session, theCriteria, aCriteriaValue, aReportId);
  }

  /**
   * Search in database the list of criteria for a specific report using its UID.
   */
  public List<CriteriaValue> searchCriteriasByReportUID(final String aReportUid) {
    SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
    Session session = sessionFactory.openSession();
    try {
      Query theQuery = session.createQuery(QUERY_CRITERIA_BY_REPORT_UID);
      theQuery.setString(KEY_REPORT_UID, aReportUid);

      List<CriteriaValue> result = new ArrayList<>();
      for (Object[] reportData : (List<Object[]>) theQuery.list()) {
        result.add(new CriteriaValue((String) reportData[0], (String) reportData[1]));
      }
      return result;
    }
    finally {
      session.close();
    }
  }

  /**
   * Search in database the list of criteria for a specific report using its identifier.
   */
  List<CriteriaValue> searchCriteriasByReportID(Session session, final long aReportID) {
    Query theQuery = session.createQuery(QUERY_CRITERIA_BY_REPORT_ID);
    theQuery.setLong(KEY_REPORT_ID, aReportID);

    List<CriteriaValue> result = new ArrayList<>();
    for (Object[] reportData : (List<Object[]>) theQuery.list()) {
      result.add(new CriteriaValue((String) reportData[0], (String) reportData[1]));
    }
    return result;
  }

  /**
   * Bean object carrying a report criteria's name and value.
   */
  @Getter
  public static class CriteriaValue {

    private static final String NULL_STRING = "null";
    private final String name;
    private final String value;

    /**
     * Builds a Criteria Name-Value object.
     *
     * @param name  if null, will be 'null'
     * @param value if null, will be 'null'
     */
    protected CriteriaValue(final String name, final String value) {
      this.name = name == null ? NULL_STRING : name;
      this.value = (value == null || value.isEmpty() || "null".equals(value)) ? NULL_STRING : value;
    }

    /**
     * Builds a Criteria Name-Value object.
     *
     * @param criteria if null, will be 'null'
     * @param value    if null, will be 'null'
     */
    public CriteriaValue(final ReportCriteriaEnum criteria, final String value) {
      this.name = criteria == null ? NULL_STRING : criteria.toString();
      this.value = value == null ? NULL_STRING : value;
    }

    @Override
    public String toString() {
      return name + " = " + value;
    }
  }
}
