package com.capgemini.reports.database.manager;

import com.capgemini.framework.common.DateHelper;
import com.capgemini.reports.database.javabean.DbReport;
import com.capgemini.reports.database.javabean.DbUser;
import com.capgemini.reports.database.javabean.DbUser.UserData;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

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

/**
 * Hibernate manager for DBUser JAVA bean.
 **/
@Slf4j
public class MngUser extends AbstractHibernateManager<DbUser> {

  private static final String KEY_REPORT = "DbReportObject";
  private static final String QUERY_REPORT_OWNERS =
    "select T2 from DbReport T0, DbReportOwner T1, DbUser T2 where T0.id = T1.reportId and T1.userId = T2.id and T0.id = :" + KEY_REPORT
      + " order by T2.id desc";

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

  /**
   * Create new user instance settings its mandatory attributes.
   */
  public DbUser insert(Session session, final String aLogin, final String aFirstName, final String aSurname, final String anEmail) {
    log.debug("Encountered new user : " + aLogin);
    Transaction transaction = session.beginTransaction();
    DbUser theCreatedUser = new DbUser();

    // Assign default attribute values
    theCreatedUser.setLogin(aLogin);
    theCreatedUser.setFirstname(aFirstName);
    theCreatedUser.setSurname(aSurname);
    theCreatedUser.setEmail(anEmail);
    theCreatedUser.setLastConnectionDate(DateHelper.getCurrentSqlDate());

    // Persist object
    session.save(theCreatedUser);
    transaction.commit();
    return theCreatedUser;
  }

  /**
   * Search query for retrieving User using its login.
   *
   *
   * @param session
   * @param aLogin Searched login value
   * @return Found user (or null of not founr)
   */
  private DbUser searchByLogin(Session session, final String aLogin) {
    return searchBy(session, "login", aLogin);
  }

  /**
   * Updates a DbUser's data to match the provided UserData (assumed to be up-to-date, freshly provided by the ActiveDirectory).
   */
  private void updateUser(Session session, final DbUser dbUser, final UserData user) {
    log.debug("Updating user data for " + dbUser.getLogin());
    Transaction transaction = session.beginTransaction();
    dbUser.setEmail(user.getEmail());
    dbUser.setFirstname(user.getFirstName());
    dbUser.setSurname(user.getSurname());
    session.update(dbUser);
    transaction.commit();
  }

  /**
   * Returns the DbUser from the database.<br>
   * If the user did not exist, creates the user, then returns it.<br>
   * If the dbUser did exist, but did not have the same data as the adUser, updates the DbUser to match the adUser, then returns the dbUSer.
   */
  public DbUser fetchOrUpdateOrCreateUser(Session session, final UserData adUser) {
    // Fetch or Register the requesting user in the DB
    MngUser mngUser = new MngUser();
    DbUser dbUser = mngUser.searchByLogin(session, adUser.getLogin());
    if (dbUser == null) {
      // User does not already exist, we must create a new one
      dbUser = mngUser.insert(session, adUser.getLogin(), adUser.getFirstName(), adUser.getSurname(), adUser.getEmail());
    }
    else {
      // User has been found, now we need to make sure all its data are up to date
      if (!dbUser.sameAs(adUser)) {
        mngUser.updateUser(session, dbUser, adUser);
      }
    }
    return dbUser;
  }

  /**
   * Returns the DbUser(s) from the database.<br>
   * If any dbUser did not exist, creates that user.<br>
   * If any dbUser did exist, but did not have the same data as the adUser, updates the DbUser to match the adUser.
   */
  public List<DbUser> fetchOrUpdateOrCreateUsers(Session session, final Collection<UserData> adUsers) {
    // Fetch or Register the requesting user in the DB
    MngUser mngUser = new MngUser();
    List<DbUser> dbUSers = new ArrayList<>();
    for (UserData adUser : adUsers) {
      DbUser dbUser = mngUser.searchByLogin(session, adUser.getLogin());
      if (dbUser == null) {
        // User does not already exist, we must create a new one
        dbUser = mngUser.insert(session, adUser.getLogin(), adUser.getFirstName(), adUser.getSurname(), adUser.getEmail());
      }
      else {
        // User has been found, now we need to make sure all its data are up to date
        if (!dbUser.sameAs(adUser)) {
          mngUser.updateUser(session, dbUser, adUser);
        }
      }
      dbUSers.add(dbUser);
    }
    return dbUSers;
  }

  /**
   * Search in database the list of users that requested a specific report to be executed (using its login as search criteria).
   */
  public List<DbUser> searchReportOwners(Session session, final DbReport dbReport) {
    Query theQuery = session.createQuery(QUERY_REPORT_OWNERS);
    theQuery.setEntity(KEY_REPORT, dbReport);
    return castAll(theQuery.list());
  }
}
