/*
 * Copyright (C) 2000 - 2024 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package org.silverpeas.components.datawarning.model;

import org.silverpeas.components.datawarning.DataWarningException;
import org.silverpeas.core.admin.service.OrganizationController;
import org.silverpeas.core.admin.service.OrganizationControllerProvider;
import org.silverpeas.core.admin.user.model.UserDetail;
import org.silverpeas.core.admin.user.model.UserFull;
import org.silverpeas.core.exception.SilverpeasException;
import org.silverpeas.core.exception.WithNested;
import org.silverpeas.kernel.logging.SilverLogger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DataWarningQueryResult {

  protected DataWarningQuery queryParent;
  // Query result
  protected ArrayList<String> columns;
  protected List<List<String>> values;
  protected boolean hasError;
  protected Exception errEx = null;
  protected String errQuery = "";
  protected String errLowestLevel = "";
  protected String errFullText = "";
  protected Map<String, List<List<String>>> valuesByUser;
  protected int persoColumnNumber;
  protected String persoUID;

  public DataWarningQueryResult(DataWarningQuery qp, boolean pe, int colNum, String puid) {
    queryParent = qp;
    columns = new ArrayList<>();
    values = new ArrayList<>();
    hasError = false;
    if (pe) {
      valuesByUser = new HashMap<>();
    } else {
      valuesByUser = null;
    }
    // Turn to Zero-based number
    persoColumnNumber = colNum - 1;
    persoUID = puid;
  }

  public DataWarningQuery getParent() {
    return queryParent;
  }

  // Error functions
  // -----------------
  public boolean hasError() {
    return hasError;
  }

  public String getErrorFullText() {
    return errFullText;
  }

  public Exception getErrorException() {
    return errEx;
  }

  public void addError(Exception ex, String sqlQuery) {
    StringBuilder sb = new StringBuilder();
    Throwable nested;
    hasError = true;
    errEx = ex;
    errQuery = sqlQuery;
    // Now, put the error messages as values
    ArrayList<String> dummyRow = new ArrayList<>();

    columns.clear();
    values.clear();
    addColumn("!!! ERROR !!! (QUERY)");
    dummyRow.add(sqlQuery);
    sb.append("!!! ERROR !!!\nQUERY :\n").append(sqlQuery);
    addColumn("!!! ERROR !!! (HIGH LVL MSG)");
    sb.append("\nMSG HAUT NIVEAU :\n");
    if (ex instanceof SilverpeasException) {
      boolean notTheSame = false;

      errLowestLevel = ((SilverpeasException) ex).getMessageLang();
      dummyRow.add(errLowestLevel);
      sb.append(errLowestLevel);
      nested = ex;
      while ((nested instanceof WithNested) && (((WithNested) nested).getNested() != null)) {
        nested = ((WithNested) nested).getNested();
        notTheSame = true;
      }
      if (notTheSame) {
        addColumn("!!! ERROR !!! (MSG BAS NIVEAU)");
        if (nested instanceof SilverpeasException) {
          errLowestLevel = ((SilverpeasException) nested).getMessageLang();
        } else {
          errLowestLevel = nested.getMessage();
        }
        dummyRow.add(errLowestLevel);
        sb.append("\nMSG BAS NIVEAU :\n").append(errLowestLevel);
      }
    } else {
      errLowestLevel = ex.getMessage();
      dummyRow.add(errLowestLevel);
      sb.append(errLowestLevel);
    }
    addRow(dummyRow);
    errFullText = sb.toString();
  }

  // Columns functions
  // -----------------
  public void addColumn(String columnName) {
    columns.add(columnName);
  }

  public List<String> getColumns() {
    return columns;
  }

  // Values functions
  // ----------------
  // Return all values (list of list)
  public List<List<String>> getValues() {
    return values;
  }

  public long returnTriggerValueFromResult() throws DataWarningException {

    try {
      if (isPersoEnabled()) {

        long maxVal = 0;
        long theVal;
        int theCol;

        if ((persoColumnNumber > 0) || (getNbColumns() <= 1)) {
          theCol = 0;
        } else {
          theCol = 1;
        }
        for (int i = 0; i < getNbRows(); i++) {
          theVal = Long.parseLong(getValue(i, theCol));
          if ((i == 0) || queryParent.checkTriggerSatisfied(theVal)) {
            maxVal = theVal;
          }
        }
        return maxVal;
      } else {
        return Long.parseLong(getValue(0, 0));
      }
    } catch (Exception e) {
      throw new DataWarningException(e);
    }
  }

  public void addRow(List<String> row) {
    // Add Full row to global result
    values.add(row);

    // Add reduced row to user specific results if needed
    if (isPersoEnabled()) {
      String userPersoValue = row.get(persoColumnNumber);
      List<List<String>> allUserRows = getValues(userPersoValue);
      List<String> clonedOne = new ArrayList<>(row);
      if (allUserRows == null) {
        allUserRows = new ArrayList<>();
      }
      // Remove User-specific id
      clonedOne.remove(persoColumnNumber);
      allUserRows.add(clonedOne);
      valuesByUser.put(userPersoValue, allUserRows);
    }
  }

  // Return entire row
  protected List<String> getRow(int row) {
    if (values.size() > row) {
      return values.get(row);
    } else {
      return new ArrayList<>();
    }
  }

  public String getValue(int row, int col) {
    var al = getRow(row);
    if (al.size() > col) {
      return al.get(col);
    } else {
      return "";
    }
  }

  public int getNbColumns() {
    return columns.size();
  }

  public int getNbRows() {
    return values.size();
  }

  // Values By User functions
  // ------------------------
  public boolean isPersoEnabled() {
    return (valuesByUser != null);
  }

  public long returnTriggerValueFromResult(String userId) throws DataWarningException {
    try {
      if (isPersoEnabled()) {
        return Long.parseLong(getValue(userId, 0, 0));
      } else {
        return Long.parseLong(getValue(0, 0));
      }
    } catch (Exception e) {
      throw new DataWarningException("Cannot get trigger value for user " + userId, e);
    }
  }

  public List<List<String>> getValues(String userId) {
    if (isPersoEnabled()) {
      // Translate user id to user Perso Value
      String userPersoValue = returnPersoValue(userId);
      List<List<String>> valret = new ArrayList<>();
      for (int i = 0; i < values.size(); i++) {
        List<String> valeur = getValues().get(i);
        if (valeur.size() > 1 && valeur.get(persoColumnNumber).equals(userPersoValue)) {
          valret.add(valeur);
        }
      }


      return valret;
    } else {
      return getValues();
    }
  }

  protected List<String> getRow(String userId, int row) {
    if (isPersoEnabled()) {
      List<List<String>> al = getValues(userId);

      if (al.size() > row) {
        return al.get(row);
      } else {
        return new ArrayList<>();
      }
    } else {
      if (values.size() > row) {
        return values.get(row);
      } else {
        return new ArrayList<>();
      }
    }
  }

  public String getValue(String userId, int row, int col) {
    var al = getRow(userId, row);

    if (al.size() > col) {
      return al.get(col);
    } else {
      return "";
    }
  }

  public List<String> getPersoColumns() {

    if (isPersoEnabled()) {
      var clo = new ArrayList<>(columns);
      if (clo.size() > persoColumnNumber) {
        clo.remove(persoColumnNumber);
      }
      return clo;
    } else {
      return columns;
    }
  }

  public int getPersoNbColumns() {
    if (isPersoEnabled()) {
      return columns.size() - 1;
    } else {
      return columns.size();
    }
  }

  public int getNbRows(String userId) {
    List<List<String>> allUserRows = getValues(userId);
    if (allUserRows != null) {
      return allUserRows.size();
    } else {
      return 0;
    }
  }

  public int getPersoColumnNumber() {
    return persoColumnNumber;
  }

  public String returnPersoValue(String userId) {
    try {
      OrganizationController oc = OrganizationControllerProvider.getOrganisationController();
      UserDetail ud = oc.getUserDetail(userId);

      if (ud == null) {
        return "";
      }
      if (DataWarningQuery.QUERY_PERSO_UID_ID.equals(persoUID)) {
        return ud.getId();
      } else if (DataWarningQuery.QUERY_PERSO_UID_LOGIN.equals(persoUID)) {
        return ud.getLogin();
      } else if (DataWarningQuery.QUERY_PERSO_UID_LASTNAME.equals(persoUID)) {
        return ud.getLastName();
      } else if (DataWarningQuery.QUERY_PERSO_UID_SPECIFICID.equals(persoUID)) {
        return ud.getSpecificId();
      } else if (DataWarningQuery.QUERY_PERSO_UID_EMAIL.equals(persoUID)) {
        return ud.getEmailAddress();
      } else {
        UserFull uf = oc.getUserFull(userId);
        return uf.getValue(persoUID, "");
      }
    } catch (Exception e) {
      SilverLogger.getLogger(this).error(e);
      return "";
    }
  }
}
