package com.capgemini.framework.teamcenter.object.pom.workspace.bom.product;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.KurtException;
import com.capgemini.framework.teamcenter.object.pom.status.StatusEnum;
import com.capgemini.framework.teamcenter.object.pom.workspace.TcItemRevision;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.TcBomObject;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.unit.TcUnitRevision;
import com.capgemini.framework.teamcenter.object.types.SnecmaObjectTypes;
import com.capgemini.reports.reports.parameters.ProductUnit;
import com.google.common.base.Optional;
import com.google.common.collect.ArrayListMultimap;
import com.teamcenter.services.strong.cad._2013_05.StructureManagement.CreateWindowsInfo2;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

import static com.capgemini.framework.teamcenter.object.SnecmaObjectProperties.*;
import static com.capgemini.framework.teamcenter.object.pom.status.StatusEnum.*;

public class TcProductRevision implements TcItemRevision, TcBomObject {
  private final TcSession session;
  private final ItemRevision itemRevision;
  private static final Logger LOGGER = LoggerFactory.getLogger(TcProductRevision.class);

  public TcProductRevision(final TcSession session, final ItemRevision itemRevision) {
    this.session = session;
    this.itemRevision = itemRevision;
  }

  @Override
  public void addInfo(final CreateWindowsInfo2 createWindowsInfo2) {
    createWindowsInfo2.itemRev = itemRevision;
  }

  /**
   * Method specific to r23.
   * Returns a list of TcUnitRevision corresponding to R23 criteria
   * */
  public List<TcUnitRevision> r23UnitRevisions(boolean isWithFrozenUnit, ProductUnit productUnitForUnit, ProductUnit productUnitFromUnit, String nbDaysSinceFrozenStr) throws KurtException {
    List<TcUnitRevision> r23UnitRevisions = new ArrayList<>();
    LOGGER.debug("== r23UnitRevisions method -> START");
    List<TcUnitRevision> filteredUnitRevision = filterOnlyMostRecentReleasedUnitRevisions();
    if(!productUnitForUnit.getUnitStr().isEmpty()) { // Case : We search a specific unit
      productUnitForUnit.parseUserUnit();
      int unitNumber = productUnitForUnit.getUnit();
      LOGGER.debug("We search only the unit : [{}]", unitNumber);
      for (TcUnitRevision unitRevision : filteredUnitRevision) {
        if(unitRevision.unitNumber() == unitNumber) {
          r23UnitRevisions.add(unitRevision);
        }
      }
    } else { // Case : we first get all units
      Collections.sort(filteredUnitRevision, TcUnitRevision.Order.BY_UNIT_NUMBER);
      List<TcUnitRevision> finalUnitRevisionList = new ArrayList<>();
      if(!productUnitFromUnit.getUnitStr().isEmpty()) { // Case : We take units "from a given one"
        productUnitFromUnit.parseUserUnit();
        int unit = productUnitFromUnit.getUnit();
        LOGGER.debug("We search units from unit : [{}]", unit);
          for (TcUnitRevision unitRevision : filteredUnitRevision) {
            if(unitRevision.unitNumber() >= unit) {
              finalUnitRevisionList.add(unitRevision);
            }
          }
      } else {
        finalUnitRevisionList = filteredUnitRevision;
      }
      for (TcUnitRevision unitRevision : finalUnitRevisionList) {
        if (unitRevision.isValidUnitToR23(isWithFrozenUnit, nbDaysSinceFrozenStr)) {
          r23UnitRevisions.add(unitRevision);
        }
      }
    }
    StringBuilder stringBuilder = new StringBuilder();
    for(TcUnitRevision unitRevision : r23UnitRevisions) {
      if(stringBuilder.length() > 0) {
        stringBuilder.append(";");
      }
      stringBuilder.append(unitRevision.unitNumber());
    }
    LOGGER.info("List of unit selected : [{}]", stringBuilder);
    LOGGER.debug("== r23UnitRevisions method -> END");
    return r23UnitRevisions;
  }

  /**
   * Return a list of TcUnitRevision containing the revision with the most recent release date of each unit of the the Product
   * */
  private List<TcUnitRevision> filterOnlyMostRecentReleasedUnitRevisions() throws KurtException {
    List<TcUnitRevision> filteredUnitRevisions = new ArrayList<>();
    ArrayListMultimap<Integer, TcUnitRevision> unitMap = ArrayListMultimap.create();
    for (TcUnitRevision unitRevision : unitRevisions()) {
      unitMap.put(unitRevision.unitNumber(), unitRevision);
    }
    for (Integer unitNum : unitMap.keySet()) {
      List<TcUnitRevision> tcUnitRevisions = unitMap.get(unitNum);
      Collections.sort(tcUnitRevisions, TcUnitRevision.Order.BY_RELEASE_DATE);
      filteredUnitRevisions.add(tcUnitRevisions.get(tcUnitRevisions.size() - 1));
    }
    return filteredUnitRevisions;
  }

  private TcUnitRevision refUnitRevision(List<TcUnitRevision> filteredUnitRevision) {
    for (int i = 0; i < filteredUnitRevision.size(); i++) {
      TcUnitRevision unitRevision = filteredUnitRevision.get(i);
      if (unitRevision.hasStatus(OPEN) && !unitRevision.isExceptionnel()) {
        for (int j = i - 1; j >= 0; j--) {
          TcUnitRevision searchedUnitRevision = filteredUnitRevision.get(j);
          if (searchedUnitRevision.hasStatus(J3A, J3B, FROZEN) && !searchedUnitRevision.isExceptionnel()) {
            return searchedUnitRevision;
          }
        }
      }
    }
    return null;
  }

  public List<TcUnitRevision> unitRevisionsWithStatus(final StatusEnum... statuses) {
    List<TcUnitRevision> unitRevisions = new ArrayList<>();
    for (TcUnitRevision unitRevision : unitRevisions()) {
      if (unitRevision.hasStatus(statuses)) {
        unitRevisions.add(unitRevision);
      }
    }
    return unitRevisions;
  }

  public Optional<TcUnitRevision> unitRevision(int unitNumber) throws KurtException {
    for (TcUnitRevision unitRevision : unitRevisions()) {
      if (unitRevision.unitNumber() == unitNumber) {
        return Optional.of(unitRevision);
      }
    }
    return Optional.absent();
  }

  /**
   * Method to retrieve the latest open status unit revision
   * @param unitNumber Unit number of the unit to retrieve
   * @return the Unit Revision as an Optional.
   * @throws KurtException
   */
  public Optional<TcUnitRevision> openUnitRevision(int unitNumber) throws KurtException {
    for (TcUnitRevision unitRevision : unitRevisions()) {
      if (unitRevision.unitNumber() == unitNumber && unitRevision.hasStatus(OPEN)) {
        return Optional.of(unitRevision);
      }
    }
    return Optional.absent();
  }

  /**
   * Method that returns the latest uncancelled unit revision for a given unit number
   * @param unitNumber Unit number of the unit to retrieve
   * @return the Unit Revision as an Optional.
   * @throws KurtException
   */
  public Optional<TcUnitRevision> notCancelledRevision(int unitNumber) throws KurtException {
    for (TcUnitRevision unitRevision : unitRevisions()) {
      if (unitRevision.unitNumber() == unitNumber && !unitRevision.hasStatus(CANCELLED)) {
        return Optional.of(unitRevision);
      }
    }
    return Optional.absent();
  }

  public String ligneProduit() {
    Form masterForm = masterForm();
    session.loadProperties(masterForm, PV_LIGNE_PRODUIT);
    return masterForm.getPropertyObject(PV_LIGNE_PRODUIT).getStringValue();
  }

  private List<TcUnitRevision> unitRevisions() {
    List<TcUnitRevision> unitRevisions = new ArrayList<>();
    session.loadProperties(itemRevision, PV_UNIT);
    ModelObject[] listOfUnitsBelowProduct = itemRevision.getPropertyObject(PV_UNIT).getModelObjectArrayValue();
    session.loadProperties(listOfUnitsBelowProduct);
    for (ModelObject unitRevision : listOfUnitsBelowProduct) {
      if (SnecmaObjectTypes.UNIT_REVISION.equals(unitRevision.getTypeObject().getName())) {
        unitRevisions.add(new TcUnitRevision(session, (ItemRevision) unitRevision));
      }
    }
    return unitRevisions;
  }

  public String productType() {
    Form masterForm = masterForm();
    session.loadProperties(masterForm, PV_TYPE_PRODUIT);
    return masterForm().getPropertyObject(PV_TYPE_PRODUIT).getStringValue();
  }

  private Form masterForm() {
    session.loadProperties(itemRevision, "item_master_tag");
    return itemRevision.get_item_master_tag();
  }

  public PSBOMView bomViewRev(){
    PSBOMView modelBom = null;
    session.loadProperties(itemRevision ,"structure_revisions");
    for (ModelObject psBomViewRev : itemRevision.get_structure_revisions() ) {
      session.loadProperties(psBomViewRev, "bom_view");
      PSBOMView bomView = ((PSBOMViewRevision) psBomViewRev).get_bom_view();
      session.loadProperties(bomView, "view_type");
      PSViewType viewType = bomView.get_view_type();
      session.loadProperties(viewType,"name");
      String bvrTypeName = viewType.get_name();
      if ("ModelListBom".equals(bvrTypeName)) {
        modelBom = bomView;
        break;
      }
    }
    return modelBom;
  }
}
