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

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.status.TcEffectivity;
import com.capgemini.framework.teamcenter.object.pom.status.TcReleaseStatus;
import com.capgemini.framework.teamcenter.object.pom.workspace.TcItemRevision;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.generic.TcGeneric;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.generic.TcGenericAssemb;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.generic.TcGenericPv;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.Form;
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.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.List;

import static com.capgemini.framework.teamcenter.object.SnecmaObjectProperties.PV_GENERIC;
import static com.capgemini.framework.teamcenter.object.pom.status.StatusEnum.*;
import static com.capgemini.framework.teamcenter.object.types.SnecmaObjectTypes.GENERIC;
import static com.capgemini.framework.teamcenter.object.types.SnecmaObjectTypes.GENERIC_ASSEMB;

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

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

  public boolean hasStatus(final StatusEnum... statuses) {
    for (TcReleaseStatus releaseStatus : releaseStatuses()) {
      if (releaseStatus.isOneOf(statuses)) {
        return true;
      }
    }
    return false;
  }

  public int unitNumber() throws KurtException {
    for (TcReleaseStatus releaseStatus : releaseStatuses()) {
      if (releaseStatus.isEffectivity()) {
        TcEffectivity tcEffectivity = releaseStatus.getEffectivity();
        if (tcEffectivity != null) {
          return tcEffectivity.unitNumbers()[0];
        }
      }
    }
    throw new KurtException("Aucune effectivité portant un numéro de unit trouvé sur le Unit " + itemRevision.getUid());
  }

  public Calendar dateReleased() {
    session.loadProperties(itemRevision, "date_released");
    return itemRevision.get_date_released();
  }

  public boolean isExceptionnel() {
    Form formRev = getMasterFormRev();
    session.loadProperties(formRev, "pv_unit_exceptionnel");
    return "Oui / Yes".equalsIgnoreCase(formRev.getPropertyObject("pv_unit_exceptionnel").getStringValue());
  }


  public List<TcGenericPv> genericPvs() {
    List<TcGenericPv> generics = new ArrayList<>();
    session.loadProperties(itemRevision, PV_GENERIC);
    ModelObject[] genericObjs = itemRevision.getPropertyObject(PV_GENERIC).getModelObjectArrayValue();
    session.loadProperties(genericObjs);
    for (ModelObject generic : genericObjs) {
      String typeName = generic.getTypeObject().getName();
      if (GENERIC.equals(typeName)) {
        generics.add(new TcGeneric((Item) generic));
      }
      else if (GENERIC_ASSEMB.equals(typeName)) {
        generics.add(new TcGenericAssemb((Item) generic));
      }
    }
    return generics;
  }

  private List<TcReleaseStatus> releaseStatuses() {
    List<TcReleaseStatus> releaseStatuses = new ArrayList<>();
    session.loadProperties(itemRevision, "release_status_list");
    ReleaseStatus[] statuses = itemRevision.get_release_status_list();
    session.loadProperties(statuses);
    for (ReleaseStatus releaseStatus : statuses) {
      releaseStatuses.add(new TcReleaseStatus(session, releaseStatus));
    }
    return releaseStatuses;
  }

  private TcReleaseStatus frozenStatus() {
    for (TcReleaseStatus releaseStatus : releaseStatuses()) {
      if (releaseStatus.isOneOf(StatusEnum.FROZEN)) {
        return releaseStatus;
      }
    }
    return null;
  }

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

  public boolean isValidUnitToR23(boolean withFrozenUnits, String nbDaysSinceFrozenStr) {
    int unit = 0;
    String rev = this.getUnitRev();
    try {
    unit = this.unitNumber();
    } catch (KurtException e) {
      LOGGER.debug("Error message when trying access unitNumber of TcUnitRevision : [{}]", e.getMessage());
    }
    LOGGER.debug("Check validity of unit [{}] rev [{}] for R23, withFrozenUnit = [{}], nbDaysSinceFrozen = [{}]",
              unit, rev, withFrozenUnits, nbDaysSinceFrozenStr);
    if(withFrozenUnits) {
      if(!nbDaysSinceFrozenStr.isEmpty()) {
        int nbJour = Integer.parseInt(nbDaysSinceFrozenStr);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Calendar c = Calendar.getInstance(); // Get current date
        c.add(Calendar.DAY_OF_MONTH, -nbJour);
        LOGGER.debug("Date after soustraction of {} day(s) : {}", nbJour, sdf.format(c.getTime()));
        LOGGER.debug("Unit [{}] has status OPEN OR (date release after [{}] AND a status FROZEN, FROZEN_IN_PROCESS, J3A or J3B) ? : [{}]", unit, sdf.format(c.getTime()), hasStatus(J3A, J3B, FROZEN, FROZEN_IN_PROCESS));
        return hasStatus(OPEN) || (dateReleased().after(c) && hasStatus(J3A, J3B, FROZEN, FROZEN_IN_PROCESS));
      }
      LOGGER.debug("Unit [{}] has status OPEN or FROZEN, FROZEN_IN_PROCESS, J3A or J3B ? : [{}]", unit, hasStatus(OPEN, J3A, J3B, FROZEN, FROZEN_IN_PROCESS));
      return hasStatus(OPEN, J3A, J3B, FROZEN, FROZEN_IN_PROCESS);
    } else {
      LOGGER.debug("Unit [{}] has status OPEN ? : [{}]", unit, hasStatus(OPEN));
      return hasStatus(OPEN);
    }
  }

  /** Get unit revision "item_revision_id" attribute */
  public String getUnitRev() {
    session.loadProperties(itemRevision, "item_revision_id");
    return itemRevision.get_item_revision_id();
  }

  public enum Order implements Comparator<TcUnitRevision> {
    BY_FROZEN_RELEASE_DATE() {
      @Override
      public int compare(final TcUnitRevision unitRevision1, final TcUnitRevision unitRevision2) {
        TcReleaseStatus frozenStatus1 = unitRevision1.frozenStatus();
        TcReleaseStatus frozenStatus2 = unitRevision2.frozenStatus();
        return TcReleaseStatus.Order.BY_RELEASE_DATE.compare(frozenStatus1, frozenStatus2);
      }
    },
    BY_RELEASE_DATE() {
      @Override
      public int compare(final TcUnitRevision unitRevision1, final TcUnitRevision unitRevision2) {
        return unitRevision1.dateReleased().compareTo(unitRevision2.dateReleased());
      }
    },
    BY_UNIT_NUMBER() {
      @Override
      public int compare(final TcUnitRevision unitRevision1, final TcUnitRevision unitRevision2) {
        try {
          return Integer.compare(unitRevision1.unitNumber(), unitRevision2.unitNumber());
        }
        catch (KurtException e) {
          return -1;
        }
      }
    }
  }
}
