package com.capgemini.reports.definition.r21report;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.EmptyNomenclatureException;
import com.capgemini.framework.teamcenter.exceptions.KurtException;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.TcBomObject;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.TcBomObjectFactory;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.TcRevisionRule;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.TcRevisionRuleProperties;
import com.capgemini.framework.teamcenter.object.pom.workspace.bom.product.TcProduct;
import com.capgemini.framework.teamcenter.object.runtime.TcBomWindow;
import com.capgemini.framework.teamcenter.object.runtime.TcBomWindowConfiguration;
import com.capgemini.framework.teamcenter.object.runtime.TcRevisionRulesConfiguration;
import com.capgemini.reports.bomlineholder.BOMLineHolder;
import com.capgemini.reports.bomlineholder.BOMLineHolderFactory;
import com.capgemini.reports.definition.r21report.ExecutionR21.ReportDataR21;
import com.capgemini.reports.definition.r21report.visitors.R21Visitor;
import com.capgemini.reports.exceptions.InvalidUserInputException;
import com.capgemini.reports.exceptions.UserInputErrorEnum;
import com.capgemini.reports.execution.IDataReportPreparator;
import com.capgemini.reports.execution.strategies.IExecutionStrategy;
import com.capgemini.reports.filter.AllIsGoodFilter;
import com.capgemini.reports.printing.PrintParameters;
import com.capgemini.reports.reports.parameters.EffectivityDate;
import com.capgemini.reports.reports.parameters.Generics;
import com.capgemini.reports.teamcenter.resolution.structureservice.MultiStructureServiceCaller;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.Form;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * This class will allow to prepare data and findAll back data for making report R21.
 */
public class DataReportPreparatorR21 implements IDataReportPreparator<ExecutionR21> {
  private static final long serialVersionUID = -5941160681518152189L;
  private static final Logger LOGGER = LoggerFactory.getLogger(DataReportPreparatorR21.class);
  private static final AllIsGoodFilter ACCEPT_ALL = new AllIsGoodFilter();

  @Override
  public String print(TcSession session, ExecutionR21 execution, IExecutionStrategy executionStrategy) throws KurtException {
    PrintParameters printParameters = prepareData(session, execution);
    executionStrategy.print(printParameters, execution);
    return executionStrategy.getReportFileName(execution) + printParameters.getOuputFileExtension();
  }

  private PrintParameters prepareData(final TcSession session, final ExecutionR21 report) throws KurtException {
    LOGGER.debug("PrepareData started on: {}", report.getReportIdentifier().getReportTitle());

    BOMLineHolder topLineAssembly = prepareAssemblyOrTechnicalBOM(session, report.getDualGeneric().asGenericAssemb(), report);
    BOMLineHolder topLineTechnical = prepareAssemblyOrTechnicalBOM(session, report.getDualGeneric().asGeneric(), report);

    ReportDataR21 dataR21 = new ReportDataR21(topLineAssembly, topLineTechnical);

    R21Visitor visitor = new R21Visitor(report.getProductUnit(), report.getEffectivity());
    PrintParameters printParameters = visitor.makeMasterTemplate(report, dataR21);
    visitor.constructDataMaps(session, printParameters, dataR21);

    LOGGER.debug("PrepareData ended on: {}", report.getReportIdentifier().getReportTitle());

    return printParameters;
  }

  private BOMLineHolder prepareAssemblyOrTechnicalBOM(final TcSession session, final Generics generics, final ExecutionR21 report)
      throws KurtException {
    String stringType;
    switch (generics.getType()) {
      case GENERIC:
        stringType = "Technical view";
        break;
      case GENERIC_ASSEMB:
        stringType = "Assembly view";
        break;
      default:
        stringType = "Undefined view";
        break;
    }

    TcBomObject topLineObject = new TcBomObjectFactory(session, generics.getItem(session)).create();
    TcProduct productObject = new TcProduct(session, report.getProductUnit().getProduct(session));
    int unit = report.getProductUnit().getUnit();
    EffectivityDate effectivityDate = report.getEffectivity();

    TcRevisionRuleProperties revisionRuleProperties = new TcRevisionRuleProperties(productObject, unit, effectivityDate.isNow() ? null : effectivityDate.getDateTime());

    TcRevisionRule revisionRule = session.findRevRule(report.getRevisionRule());
    TcRevisionRulesConfiguration revRulesConfiguration = new TcRevisionRulesConfiguration(revisionRule, revisionRuleProperties);

    // Perform the job
    LOGGER.info("Data configured for the {}, now starting nomenclature resolution", stringType);
    try (TcBomWindow bomWindow = session.createBomWindow(new TcBomWindowConfiguration(topLineObject, revRulesConfiguration))) {
      session.setObjectPropPolicy(report.getFullPolicy());

      BOMLineHolder unbuiltTopLine = new MultiStructureServiceCaller(session).resolve(bomWindow.topLine(), ACCEPT_ALL);

      List<Form> itemRevMaster = unbuiltTopLine.getAllItemRevMasters();
      session.loadProperties(itemRevMaster.toArray(new ModelObject[0]));

      BOMLineHolderFactory.buildTopBom(session, unbuiltTopLine, ACCEPT_ALL);
      unbuiltTopLine.freeze();

      return unbuiltTopLine;
    }
    catch (EmptyNomenclatureException e) {
      switch (generics.getType()) {
        case GENERIC:
          throw new InvalidUserInputException(UserInputErrorEnum.NO_DATA_COULD_BE_EXTRACTED_FOR_GENERIC_PRODUCT_AND_UNIT, e);
        case GENERIC_ASSEMB:
          throw new InvalidUserInputException(UserInputErrorEnum.NO_DATA_COULD_BE_EXTRACTED_FOR_GENERIC_ASSEMB_PRODUCT_AND_UNIT, e);
        default:
          throw new KurtException("The TC Nomenclature Resolution returned null", e);
      }
    }
  }
}
