package com.capgemini.reports.definition.r26report;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.object.pom.status.TcEffectivity;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.Effectivity;
import com.teamcenter.soa.client.model.strong.Item;
import com.teamcenter.soa.client.model.strong.ItemRevision;
import com.teamcenter.soa.client.model.strong.ReleaseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * Helper for Effectivity for making report R26.
 */
class EffectivityHelper {
  private static final int BORNE_SUP = 2147483646;

  private static final Logger LOGGER = LoggerFactory.getLogger(EffectivityHelper.class);

  /**
   * Retourne true si l'effectivité "tcEffectivity" est comprise dans une des effectivités de l'item revision.
   */
  static boolean isEffectivityMatches(TcSession session, TcEffectivity tcEffectivity, ItemRevision itemRevision) {
    boolean effectivityMatches = false;

    Map<TcEffectivity, ItemRevision> tcEffectivities = getEffectivitiesWithUnitRange(session, itemRevision);
    for (Map.Entry<TcEffectivity, ItemRevision> currentEff : tcEffectivities.entrySet()) {
      int[] currentEffUnits = currentEff.getKey().unitNumbers();

      LOGGER.debug(" Checking effectivity {} of {}",currentEffUnits ,itemRevision.get_item_id());

      if (tcEffectivity.endItem().equals(currentEff.getKey().endItem())) {
        int[] tcEffUnits = tcEffectivity.unitNumbers();
        if ((currentEffUnits.length == 1 && currentEffUnits[0] <= tcEffUnits[0])
          ||
          (currentEffUnits.length == 2
            && currentEffUnits[0] <= tcEffUnits[0]
            && tcEffUnits[0] <= currentEffUnits[1])) {
          effectivityMatches = true;
          break;
        }
      }
    }

    return effectivityMatches;
  }

  /**
   * Retourne la liste des effectivités trouvées sur les statuts avec les units concernés.
   */
  static Map<TcEffectivity, ItemRevision> getEffectivitiesWithUnitRange(TcSession session, ItemRevision itemRevision) {
    Map<TcEffectivity, ItemRevision> tcEffectivities = new HashMap<>();

    // récuperer les effectivitées
    Map<ReleaseStatus, ItemRevision> statuses = getReleaseStatuses(session, itemRevision);
    for (Map.Entry<ReleaseStatus, ItemRevision> currentStatus : statuses.entrySet()) {
      for (ModelObject currentEff : currentStatus.getKey().get_effectivities()) {
        // pour chacune récupérer l'object tcEffectivity
        TcEffectivity tcEffectivity = new TcEffectivity(session, (Effectivity) currentEff);
        if (!tcEffectivity.unitRange().isEmpty()) {
          tcEffectivities.put(tcEffectivity, currentStatus.getValue());
        }
      }
    }
    return tcEffectivities;
  }

  /**
   * Retourne les status Effectivity de toutes les révisions de l'objet parent de la révision passée en paramètre.
   *
   * @param session,      la session courante
   * @param itemRevision, l'item révision courant
   */
  private static Map<ReleaseStatus, ItemRevision> getReleaseStatuses(TcSession session, ItemRevision itemRevision) {
    Map<ReleaseStatus, ItemRevision> statuses = new HashMap<>();

    getReleaseStatus(session, itemRevision, statuses);

    return statuses;
  }

  private static void getReleaseStatus(TcSession session, ItemRevision itemRevision, Map<ReleaseStatus, ItemRevision> statuses) {

    session.loadProperties(itemRevision, "release_status_list");
    for (ReleaseStatus status : itemRevision.get_release_status_list()) {

      session.loadProperties(status, "name");
      if (status.get_name().equals("Effectivity")) {
        statuses.put(status, itemRevision);
      }
    }
  }

  /**
   * Retourne true si au moins une des effectivités est en Produit-Unit, false sinon.
   *
   * @param session,  session courante
   * @param statuses, status
   */
  static boolean isEffectivityWithUnitRange(TcSession session, ReleaseStatus[] statuses) {
    // récuperer les effectivitées
    for (ReleaseStatus currentStatus : statuses) {
      for (ModelObject currentEff : currentStatus.get_effectivities()) {
        // pour chacune récupérer le produit et la page de unit
        Effectivity effectivity = (Effectivity) currentEff;
        session.loadProperties(effectivity, "unit_range_text");
        String unitRange = effectivity.get_unit_range_text();

        if (!unitRange.isEmpty()) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * Retourne les effectivités des statuts Effectivity.
   *
   * @param statuses, status
   */
  static ModelObject[] getEffectivities(ReleaseStatus[] statuses) {
    for (ReleaseStatus releaseStatus : statuses) {
      if (releaseStatus.get_name().equals("Effectivity")) {
        return releaseStatus.get_effectivities();
      }
    }
    return new ModelObject[0];
  }

  private static List<Range> getEffectivitiesOfAllItemRev(TcSession session, ItemRevision itemRevision, Item product) {
    List<Range> rangeList = new ArrayList<>();

    // récuperer les effectivitées
    for (ModelObject currentModelObject : itemRevision.get_revision_list()) {
      ItemRevision currentItemRev = (ItemRevision) currentModelObject;
      session.loadProperties(currentItemRev, "release_status_list");
      for (ReleaseStatus status : currentItemRev.get_release_status_list()) {

        for (ModelObject currentEff : status.get_effectivities()) {
          // pour chacune récupérer l'object tcEffectivity
          TcEffectivity tcEffectivity = new TcEffectivity(session, (Effectivity) currentEff);

          if (!tcEffectivity.unitRange().isEmpty() && tcEffectivity.endItem().equals(product)) {
            int[] unitNumbers = tcEffectivity.unitNumbers();
            int end = (unitNumbers.length == 2) ? unitNumbers[1] : BORNE_SUP;
            rangeList.add(new Range(currentItemRev, unitNumbers[0], end));
          }
        }
      }
    }

    List<Range> newRangeList = new ArrayList<>();
    boolean found = false;
    for (Range range_1 : rangeList) {
      for (Range range_2 : rangeList) {
        if (range_1 != range_2 && range_1.borneInf() == range_2.borneInf() && range_1.borneSup() == range_2.borneSup()) {
          if (!RfPUManager.isLastReleasedRfPURev(session, range_1.getItemRevion())) {
            found = true;
            break;
          }
        }
      }
      if (!found) {
        newRangeList.add(range_1);
      }
      found = false;
    }

    return newRangeList;
  }

  static String calculateEffectivity(TcSession session, ItemRevision itemRevision, Item product, Map<String, List<Range>> itemRangeMap) {

    List<Range> rangeList = itemRangeMap.get(itemRevision.get_item_id());
    if(rangeList == null) {
      rangeList = new ArrayList<>();
      LOGGER.debug("Effectivities ranges: ");
      for (Range eff : getEffectivitiesOfAllItemRev(session, itemRevision, product)) {
        rangeList.add(new Range(eff.getItemRevion().get_item_revision_id(), eff.borneInf(), eff.borneSup()));
        LOGGER.debug("range {}/{} {} {}",eff.getItemRevion().get_item_id(),eff.getItemRevion().get_item_revision_id(), eff.borneInf(), eff.borneSup());
      }

      Collections.sort(rangeList, new Comparator<Range>() {
        public int compare(Range r1, Range r2) {
          return Integer.compare(r1.borneInf(), r2.borneInf());
        }
      });

      itemRangeMap.put(itemRevision.get_item_id(),rangeList);
    }



    return new Ranges(rangeList).toString(itemRevision.get_item_revision_id());
  }
}
