package com.capgemini.reports.definition.r07report.data.extractor;

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.exceptions.NoSuchBomViewException;
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.bomlineholder.processor.DefaultAltOrDetBehaviour;
import com.capgemini.reports.definition.r07report.ExecutionR07;
import com.capgemini.reports.exceptions.InvalidInputException;
import com.capgemini.reports.exceptions.InvalidUserInputException;
import com.capgemini.reports.exceptions.MalformedNomenclatureException;
import com.capgemini.reports.exceptions.UserInputErrorEnum;
import com.capgemini.reports.filter.AllIsGoodFilter;
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 lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * Extracts both raw nemenclature from Teamcenter, puts the result into the ExecutionR07.
 */
@Slf4j
public class DataExtractorR07 {

    private static final AllIsGoodFilter ACCEPT_ALL = new AllIsGoodFilter();
    private final TcSession session;
    private final ExecutionR07 report;

    // Data gathered
    @Getter
    private BOMLineHolder topBomConf1;
    @Getter
    private BOMLineHolder topBomConf2;

    /**
     * Creates a {@link DataExtractorR07} object.
     *
     * @param session the Tc session
     * @param report  the report holding the necessary bom data
     */
    public DataExtractorR07(final TcSession session, final ExecutionR07 report) {
        this.session = session;
        this.report = report;
    }

    /**
     * Extracts the two configurations to be compared in the R07 report.
     *
     * @throws InvalidInputException          if the parameters are incorrect
     * @throws EmptyNomenclatureException     if the nomeclature is empty
     * @throws NoSuchBomViewException         if the bomview could not be found
     * @throws MalformedNomenclatureException if the nomeclature cannot be formed
     */
    public void extractData() throws KurtException {
        log.debug("PrepareData started on: " + report.getReportIdentifier().getReportTitle());
        long start = System.currentTimeMillis();

        // Setup the policy
        session.setObjectPropPolicy(report.getFullPolicy());

        String revisionRule = report.getRevisionRule();

        log.debug("Check the data structures using revision rule {}.", revisionRule);
        Generics genericParam1 = report.getGenericParam1();
        Generics genericParam2 = report.getGenericParam2();

        log.debug("Resolve Configuration 1");
        topBomConf1 = resolveTopBom(genericParam1);

        log.debug("Resolve Configuration 2");
        topBomConf2 = resolveTopBom(genericParam2);

        log.debug("Data gathering time : {} ms", System.currentTimeMillis() - start);
    }

    private BOMLineHolder resolveTopBom(final Generics generic) throws KurtException {
        TcBomObject topLineObject = new TcBomObjectFactory(session, generic.getItem(session)).create();
        TcProduct productObject = new TcProduct(session, generic.getProductUnit().getProduct(session));
        int unit = generic.getProductUnit().getUnit();

        TcRevisionRuleProperties revisionRuleProperties = new TcRevisionRuleProperties(productObject, unit);

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

        // Perform the job
        log.debug("Data configured, now starting nomenclature resolution");
        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[itemRevMaster.size()]));

            log.debug("Building the Snecma Objects");
            BOMLineHolderFactory.buildTopBom(session, unbuiltTopLine, ACCEPT_ALL);

            log.debug("Loading the correct revisions of all Items in the pseudo-folders of Alt And DET parts");
            DefaultAltOrDetBehaviour<?, ?> altOrDetBehaviour = new DefaultAltOrDetBehaviour<>(session, null);
            unbuiltTopLine.process(altOrDetBehaviour);

            log.debug("All required data was loaded, freezing the BomLineHolder...");
            unbuiltTopLine.freeze();
            return unbuiltTopLine;
        } catch (EmptyNomenclatureException e) {
            switch (generic.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 e;
            }
        }
    }
}
