package com.capgemini.reports.definition.r15r16r17reports.r16report;

import com.capgemini.framework.teamcenter.authent.TcSession;
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.framework.teamcenter.object.types.SnecmaObjectTypes;
import com.capgemini.framework.teamcenter.queries.saved.modelobject.ItemRevisionQuery;
import com.capgemini.reports.bomlineholder.BOMLineHolder;
import com.capgemini.reports.definition.r15r16r17reports.AbstractDataReportPreparatorR15R16R17;
import com.capgemini.reports.documentationobjects.DocTechnic;
import com.capgemini.reports.exceptions.InvalidUserInputException;
import com.capgemini.reports.execution.strategies.IExecutionStrategy;
import com.capgemini.reports.printing.PrintParameters;
import com.capgemini.reports.snecmaobjects.admin.RFAdminRevision;
import com.capgemini.reports.snecmaobjects.generic.GenericAssembRevision;
import com.capgemini.reports.snecmaobjects.generic.GenericRevision;
import com.capgemini.reports.snecmaobjects.part.PartAeroRevision;
import com.capgemini.reports.snecmaobjects.rf.RFGenericRevision;
import com.capgemini.reports.teamcenter.ObjectInfoHelper;
import com.capgemini.reports.teamcenter.ObjectInfoLoader;
import com.capgemini.reports.teamcenter.resolution.structureservice.SingleStructureServiceCaller;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.Form;
import com.teamcenter.soa.client.model.strong.ItemRevision;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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


/**
 * This class will allow to prepare data and findAll back data for making report R16.
 */
public class DataReportPreparatorR16 extends AbstractDataReportPreparatorR15R16R17<ExecutionR16> {
    private static final long serialVersionUID = 3714231249482402544L;
    private static final Logger LOGGER = LoggerFactory.getLogger(DataReportPreparatorR16.class);
    private static final String EMPTY_STR = "";

    @Override
    public String print(TcSession session, ExecutionR16 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 ExecutionR16 report) throws KurtException {
        LOGGER.debug("PrepareData started on: {}", report.getReportIdentifier().getReportTitle());
        // Retrieve the ProductRevision and the UnitRevision
        // Note: a check performed earlier made sure that this list is NOT empty
        session.setObjectPropPolicy(report.getFullPolicy());
        ItemRevision productRev =
                new ItemRevisionQuery(session).searchID(report.getTheProduitUnitObject().getProductID()).findAll().get(0);
        report.setTypeProduit(ObjectInfoLoader.getProductType(session, productRev));
        report.setClausePropriete(ObjectInfoLoader.getClausePropriete(report.getTypeProduit(), session));
        report.setProduitUnit(report.getTheProduitUnitObject().getProductID() + "-" + report.getTheProduitUnitObject().getUnitStr());

        // All the checks are realised (Product-Unit-Generic)
        // The appropriate revision is selected via the method getGenericItemRevision
        ItemRevision generic = getGenericItemRevision(session, report);
        String typeGeneric = generic.getTypeObject().getName();
        if (SnecmaObjectTypes.GENERIC_REVISION.equals(typeGeneric)) {
            report.setGenericRev(new GenericRevision(session, generic));
        } else if (SnecmaObjectTypes.GENERIC_ASSEMB_REVISION.equals(typeGeneric)) {
            report.setGenericRev(new GenericAssembRevision(session, generic));
        } else {
            throw new KurtException("Object should be a Generic but is a: " + typeGeneric);
        }

        TcBomObject topLineObject = new TcBomObjectFactory(session, generic).create();
        TcProduct productObject = new TcProduct(session, report.getTheProduitUnitObject().getProduct(session));
        int unit = report.getTheProduitUnitObject().getUnit();

        TcRevisionRuleProperties revisionRuleProperties = new TcRevisionRuleProperties(productObject, unit);

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

        // Build the Generic and return the GenericBom
        try (TcBomWindow bomWindow = session.createBomWindow(new TcBomWindowConfiguration(topLineObject, revRulesConfiguration))) {
            session.setObjectPropPolicy(report.getFullPolicy());

            BOMLineHolder rfGenericBom = new SingleStructureServiceCaller(session).resolve(bomWindow.topLine(), new R16BOMLineExplorer());
            List<Form> itemRevMaster = rfGenericBom.getAllItemRevMasters();
            session.loadProperties(itemRevMaster.toArray(new ModelObject[itemRevMaster.size()]));

            report.setArticleId(report.getGenericRev().getItemId());
            report.setArticleRevision(report.getGenericRev().getRevisionId());

            // Treatment of the RFGenericBom
            ItemRevision rfGeneric = rfGenericBom.getItemRevision();
            report.setRfGenericRev(new RFGenericRevision(session, rfGeneric));

            List<BOMLineHolder> bomChildMap = rfGenericBom.getBomChilds();
            if (!bomChildMap.isEmpty()) {
                for (BOMLineHolder currentChild : bomChildMap) {
                    ItemRevision itemRevChild = currentChild.getItemRevision();
                    String childType = itemRevChild.getTypeObject().getName();
                    if (SnecmaObjectTypes.PART_AERO_REVISION.equals(childType)) {
                        report.getPartNumberRevList().add(new PartAeroRevision(session, itemRevChild));
                    } else if (SnecmaObjectTypes.DESCRIPTIF_REVISION.equals(childType)) {
                        retrieveDocNormAndDocNormStandardLists(session, itemRevChild, report);
                        retrieveDocTechnicList(session, itemRevChild, report);
                    } else if (SnecmaObjectTypes.RF_ADMIN_REVISION.equals(childType)) {
                        report.setRfAdminRev(new RFAdminRevision(session, itemRevChild));
                        report.setFrontiereAvion(report.getRfAdminRev().getFrontiereAvion());
                        report.setFrontiereCooperant(report.getRfAdminRev().getFrontiereCooperant());
                    } else {
                        LOGGER.debug("The child {} have a type {} which has not selected", itemRevChild.getUid(), childType);
                    }
                }
            } else {
                LOGGER.debug("No child has been found under the RF Generic Bom {}.", rfGenericBom.getUid());
            }

            // Specific treatment of DocTechnic
            filterDocTechnic(report);
            filterForDefinitionCertifiee(report);

            // Empty parameters
            report.setRevisionArticle(EMPTY_STR);
            report.setDateReleased(EMPTY_STR);

            R16Printer printer = new R16Printer();
            PrintParameters printParameters = printer.makeMasterTemplate(report, report);
            printer.constructDataMaps(session, printParameters, report);

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

    /**
     * Get the appropriate Generic Revision.
     *
     * @param session {@link TcSession} - the tc session
     * @param report  - the execution report which provide all informations for the report
     * @return the generic {@link ItemRevision}
     * @throws InvalidUserInputException for the throwable exception
     * @throws KurtException             for the throwable exception
     */
    ItemRevision getGenericItemRevision(final TcSession session, final ExecutionR16 report) throws KurtException {
        List<ItemRevision> genericRevisions = new ItemRevisionQuery(session).searchID(report.getItemId()).findAll();

        // Keep only the ItemRevision whose end item is the Product
        List<ItemRevision> checkedGenerics = ObjectInfoHelper.getRevisionWithSameEndItem(session, genericRevisions, report.getTheProduitUnitObject().getProductID());
        genericRevisions.clear();

        // Compare effectivities
        List<ItemRevision> results = ObjectInfoHelper.getRevisionWithHighestRange(session, checkedGenerics, report.getTheProduitUnitObject().getUnitStr());
        checkedGenerics.clear();
        if (results.isEmpty()) {
            // This error should not occur because it was already tested
            throw new InvalidUserInputException("No generic found for item Id= " + report.getItemId());
        }
        return results.get(0);
    }

    private void filterForDefinitionCertifiee(final ExecutionR16 report) {
        if (report.isDefCertifiee()) {
            List<DocTechnic> transitList = new ArrayList<>();
            for (DocTechnic docTechnic : report.getDocTechnicList()) {
                // These values are forbidden
                if ("Définition / Design".equals(docTechnic.getTypeDocTechnic1())) {
                    transitList.add(docTechnic);
                }
            }
            report.getDocTechnicList().clear();
            report.getDocTechnicList().addAll(transitList);
        }
    }
}
