package com.capgemini.reports.definition.r23report;

import com.capgemini.framework.common.*;
import com.capgemini.framework.teamcenter.policies.PolicyLocation;
import com.capgemini.reports.JasperTemplate;
import com.capgemini.reports.ReportEnum;
import com.capgemini.reports.ReportLaunchTypeEnum;
import com.capgemini.reports.bomlineholder.BOMLineHolder;
import com.capgemini.reports.builder.R23Builder;
import com.capgemini.reports.builder.ReportBuilder;
import com.capgemini.reports.database.javabean.enumeration.ReportCriteriaEnum;
import com.capgemini.reports.database.manager.MngCriteriaVal.CriteriaValue;
import com.capgemini.reports.exceptions.FileLoadingException;
import com.capgemini.reports.exceptions.ReportPrintException;
import com.capgemini.reports.execution.AbstractExecutionReport;
import com.capgemini.reports.execution.IDataReportPreparator;
import com.capgemini.reports.printing.PrintParameters;
import com.capgemini.reports.printing.Printer;
import com.capgemini.reports.printing.Printer.PrintFormat;
import com.capgemini.reports.reports.parameters.*;
import com.capgemini.reports.reports.parameters.Generics.GenericType;
import com.capgemini.reports.reports.parameters.checker.*;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * Obtain data for report R23.<br>
 * Contain {@link IChecker} for a report's construction
 */
@Getter
@EqualsAndHashCode(callSuper = true)
public class ExecutionR23 extends AbstractExecutionReport<ExecutionR23, BOMLineHolder> {
  /**
   * Policy for inspecting Alternates and DETs more closely (i.e. looking into their pseudo-folders for alternative parts).
   */
  public static final PolicyLocation R23_POLICY_XML_ALT = PolicyLocation.make("R23_policy_alt", "/xml/policies/r23report/R23_policy_alt.xml");
  private static final long serialVersionUID = -1413103619999188847L;
  private static final Logger LOGGER = LoggerFactory.getLogger(ExecutionR23.class);
  private static final DataReportPreparatorR23 DATA_REPORT_PREPARATOR_R23 = new DataReportPreparatorR23();
  // Policies parameters
  private static final PolicyLocation R23_POLICY_XML_LIGHT = PolicyLocation.make("R23_policy_light", "/xml/policies/r23report/R23_policy_light.xml");
  private static final PolicyLocation R23_POLICY_XML_FULL = PolicyLocation.make("R23_policy_full", "/xml/policies/r23report/R23_policy_full.xml");
  // Launch type parameter
  private static final ReportLaunchTypeEnum REPORT_LAUNCH_TYPE = ReportLaunchTypeEnum.getReportLaunchType("common.launch.launchType.REPORT_R23");

  // Revision rule parameter
  private static final String REPORT_REVISION_RULE = SettingsHelper.getPropertyWithDefaultValue("common.bom.revisionRule.REPORT_R23", DEFAULT_REVISION_RULE);

  // Priority parameter
  private static final int REPORT_PRIORITY = SettingsHelper.getPriority(ReportEnum.REPORT_R23);

  private static final String FILE_SEPARATOR = "_";
  private static final String PATTERN_DATE = "dd-MM-yyyy";

  // Internal working data
  private final Generics generic;
  private final MonoMulti monoMulti;
  private final ProductUnit productUnit;
  private final EffectivityDate effectivityDate;
  private final boolean isSuperModelBOM;
  private final boolean isWithFrozenUnit;
  private final ProductUnit productUnitForUnit;
  private final ProductUnit productUnitFromUnit;
  private final String nbDaysSinceFrozenStr;


  @Setter
  private BOMLineHolder topLine;
  private String productLine;
  private String unitRevision;
  private boolean isFrozenUnit = false;

  /**
   * Constructor.
   *
   * @param aProductUnit    {@link ProductUnit} - the product
   * @param aGeneric        le generic {@link Generics} - represent the generic. Can be null if the wanted configuration is only with the Product and the Unit.
   * @param aMonoMulti      {@link MonoMulti} - represent the type of resolution structure
   * @param effectivityDate {@link EffectivityDate} - represent the effectivity date
   * @param isSuperModelBOM boolean
   * @param isWithFrozenUnit boolean. If true ask to include frozen units
   * @param productUnitForUnit {@link ProductUnit} - the product and unit. Can be null if the wanted configuration is only with the Product
   * @param productUnitFromUnit  {@link ProductUnit} - the product and from_unit. Can be null if the wanted configuration is only with the Product or only with one unit
   * @param nbDaysSinceFrozen nb of days since frozen status set on unit.
   */

  public ExecutionR23(final ProductUnit aProductUnit, final Generics aGeneric, final MonoMulti aMonoMulti, final EffectivityDate effectivityDate, final boolean isSuperModelBOM, final boolean isWithFrozenUnit, final ProductUnit productUnitForUnit, final ProductUnit productUnitFromUnit, final String nbDaysSinceFrozen) {
    super(REPORT_PRIORITY, REPORT_REVISION_RULE, REPORT_LAUNCH_TYPE);
    this.productUnit = aProductUnit;
    this.generic = aGeneric;
    this.monoMulti = aMonoMulti;
    this.effectivityDate = effectivityDate;
    this.isSuperModelBOM = isSuperModelBOM;
    this.isWithFrozenUnit = isWithFrozenUnit;
    this.productUnitForUnit = productUnitForUnit;
    this.productUnitFromUnit = productUnitFromUnit;
    this.nbDaysSinceFrozenStr = nbDaysSinceFrozen;
  }

  @Override
  public List<CriteriaValue> getLaunchData() {
    List<CriteriaValue> theCriteria = new ArrayList<>();
    theCriteria.add(new CriteriaValue(ReportCriteriaEnum.PRODUCT, productUnit.getProductID()));
    if (generic.getType() == GenericType.GENERIC) {
      theCriteria.add(new CriteriaValue(ReportCriteriaEnum.GENERIC, generic.getID()));
    }
    if (generic.getType() == GenericType.GENERIC_ASSEMB) {
      theCriteria.add(new CriteriaValue(ReportCriteriaEnum.GENERIC_ASSEMB, generic.getID()));
    }
    theCriteria.add(new CriteriaValue(ReportCriteriaEnum.MONO_MULTI, monoMulti.toString()));
    theCriteria.add(new CriteriaValue(ReportCriteriaEnum.SUPERMODEL, Boolean.toString(isSuperModelBOM)));
    theCriteria.add(new CriteriaValue(ReportCriteriaEnum.EFFECTIVITY_DATE, effectivityDate.formatAsInputCriteria()));
    theCriteria.add(new CriteriaValue(ReportCriteriaEnum.WITH_FROZEN_UNIT, Boolean.toString(isWithFrozenUnit)));
    theCriteria.add(new CriteriaValue(ReportCriteriaEnum.UNIT, productUnitForUnit.getUnitStr()));
    theCriteria.add(new CriteriaValue(ReportCriteriaEnum.FROM_UNIT, productUnitFromUnit.getUnitStr()));
    theCriteria.add(new CriteriaValue(ReportCriteriaEnum.UNITS_FROZEN_SINCE_LESS_THAN, nbDaysSinceFrozenStr));

    return theCriteria;
  }

  @Override
  public String getSpecifiedFileName() {
    StringBuilder builder = new StringBuilder("R23").append(FILE_SEPARATOR);
    if (generic.getType() != GenericType.ONLY_PRODUCT) {
      builder.append(generic.getID()).append(FILE_SEPARATOR);
    }
    builder.append(productUnit.getProductID()).append(FILE_SEPARATOR);
    builder.append(productLine.replaceAll(StringHelper.REGEXP_ALPHANUM, " ")).append(FILE_SEPARATOR);
    builder.append(TeamcenterDateFormatter.formatCalendarToPattern(Calendar.getInstance(), PATTERN_DATE)).append(FILE_SEPARATOR);
    builder.append(monoMulti.toString());
    return builder.toString();
  }

  /**
   * Returns the file name as it will be saved to disc locally.
   * FEB459 added unit revision and GELE suffix
   *
   * @return the file name
   */
  private String getSubReportFileName() {
    StringBuilder builder = new StringBuilder("R23").append(FILE_SEPARATOR);
    if (generic.getType() != GenericType.ONLY_PRODUCT) {
      builder.append(generic.getID()).append(FILE_SEPARATOR);
    }
    builder.append(productUnit.getProductID()).append(FILE_SEPARATOR);
    builder.append(productLine.replaceAll(StringHelper.REGEXP_ALPHANUM, " ")).append(FILE_SEPARATOR);
    builder.append(productUnit.getUnitStr()).append(FILE_SEPARATOR);
    builder.append(unitRevision).append(FILE_SEPARATOR);
    builder.append(TeamcenterDateFormatter.formatCalendarToPattern(Calendar.getInstance(), PATTERN_DATE)).append(FILE_SEPARATOR);
    builder.append(monoMulti.toString());
    if(isFrozenUnit) {
      builder.append(FILE_SEPARATOR).append("GELE");
    }
    return builder.toString();
  }

  @Override
  public Printer getPrinter() {
    return new Printer(Printer.AvailablePrinter.ZIP);
  }

  @Override
  public PrintFormat getPrintingFormat() {
    return PrintFormat.XLSX;
  }

  @Override
  public ReportEnum getReportIdentifier() {
    return ReportEnum.REPORT_R23;
  }

  @Override
  public List<IChecker> getQuickChecks() {
    List<IChecker> quickChecks = new ArrayList<>();
    quickChecks.add(new GenericExistenceChecker(generic));
    quickChecks.add(new ProductChecker(productUnit.getProductID()));
    quickChecks.add(new EffectivityChecker(effectivityDate));
    if(!productUnitForUnit.getUnitStr().isEmpty()) { // If a Unit is filled -> check product/unit correspondance
      quickChecks.add(new ProductUnitChecker(productUnitForUnit));
    }
    return quickChecks;
  }

  @Override
  public List<IChecker> getLongChecks() {
    List<IChecker> longChecks = new ArrayList<>();
    longChecks.add(new GenericUnderProductUnitChecker(generic));
    return longChecks;
  }

  @Override
  public PolicyLocation getPropertyPolicyForLongChecks() {
    return R23_POLICY_XML_LIGHT;
  }

  @Override
  public PolicyLocation getFullPolicy() {
    return R23_POLICY_XML_FULL;
  }

  @Override
  public String getTemplateLocation() {
    return JasperTemplate.TEMPLATE_R23.getFilepath();
  }

  @Override
  public IDataReportPreparator<ExecutionR23> getDataPreparator() {
    return DATA_REPORT_PREPARATOR_R23;
  }

  @Override
  public AbstractExecutionReport<?, ?> cloneTemplateToExecution() {
    return new ExecutionR23(productUnit, generic, monoMulti, effectivityDate, isSuperModelBOM, isWithFrozenUnit, productUnitForUnit, productUnitFromUnit, nbDaysSinceFrozenStr);
  }

  @Override
  public Class<? extends ReportBuilder> getReportBuilderClass() {
    return R23Builder.class;
  }

  public PrintParameters reportParameters() {
    return new PrintParameters(Printer.PrintFormat.ZIP, getFileNameOnDisc(), ReportFolderOutputHelper.getStorageFolderLocation(getLaunchType()));
  }

  public PrintParameters subReportParameters() throws ReportPrintException, FileLoadingException {
    String storageFolder = ReportFolderOutputHelper.getStorageFolderLocation(getLaunchType());
    String templateLocation = getTemplateLocation();
    try (InputStream templateStream = ResourceHelper.getResourceAsStream(templateLocation)) {
      PrintParameters printParameters = PrintParameters.createMainReport(getPrintingFormat(), getSubReportFileName(), storageFolder, templateStream);
      printParameters.setReportTitle(getReportIdentifier().getReportTitle());
      return printParameters;
    }
    catch (IOException e) {
      LOGGER.error("Unable to close the template file properly from " + templateLocation, e);
      return null; // IOException is only thrown by close() which, if it ever fails, means you have much bigger problems!
    }
  }

  public void setProductLine(String productLine) {
    this.productLine = productLine;
  }

  public void setUnitRevision(String unitRevision) {
    this.unitRevision = unitRevision;
  }

  public void setFrozenUnit(boolean frozenUnit) {
    isFrozenUnit = frozenUnit;
  }
}
