package com.capgemini.reports.definition.r15r16r17reports.r15r17reports.r15report;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.KurtException;
import com.capgemini.framework.teamcenter.object.SnecmaObjectProperties;
import com.capgemini.framework.teamcenter.queries.saved.modelobject.ItemRevisionQuery;
import com.capgemini.reports.definition.r15r16r17reports.r15r17reports.AbstractDataReportPreparatorR15R17;
import com.capgemini.reports.exceptions.InvalidUserInputException;
import com.capgemini.reports.execution.strategies.IExecutionStrategy;
import com.capgemini.reports.printing.PrintParameters;
import com.capgemini.reports.releasestatus.RevisionStatusAndDate;
import com.capgemini.reports.snecmaobjects.admin.RFAdminRevision;
import com.capgemini.reports.teamcenter.ObjectInfoHelper;
import com.google.common.base.Optional;
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 org.slf4j.Logger;

import java.util.*;

import static com.capgemini.framework.teamcenter.TeamcenterObjectProperties.ASSEMBLY_BOM;
import static com.capgemini.framework.teamcenter.TeamcenterObjectProperties.RF_PART_BOM;
import static com.capgemini.framework.teamcenter.object.types.SnecmaObjectTypes.*;


/**
 * This class will allow to prepare data and findAll back data for making report R15.
 */
public class DataReportPreparatorR15 extends AbstractDataReportPreparatorR15R17<ExecutionR15> {
    private static final long serialVersionUID = -1615860676063048572L;

    private static final String NON_NO = "Non / No";
    private static final String OUI_YES = "Oui / Yes";
    private static final String DASH_STRING = "-";
    private static final Logger log = org.slf4j.LoggerFactory.getLogger(DataReportPreparatorR15.class);

    @Override
    public String print(TcSession session, ExecutionR15 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 ExecutionR15 report) throws KurtException {
        log.trace("PrepareData started on: " + report.getReportIdentifier().getReportTitle());
        long start = System.currentTimeMillis();

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

        List<ItemRevision> revisionsSnecmaObject = new ItemRevisionQuery(session).searchID(report.getItemId()).findAll();
        if (revisionsSnecmaObject.isEmpty()) {
            throw new InvalidUserInputException("Unabled to retrieve the list of revisons of the Part " + report.getItemId());
        }

        // Retrieve topItemRev
        ItemRevision topItemRevision = retrieveTopItemRevision(report.getItemRevision(), revisionsSnecmaObject);

        // Retrieve all common data
        doPrepareCommonDataForAllReports(session, topItemRevision, revisionsSnecmaObject, report);

        // Specificities of the report
        filterDocTechnic(report);
        if (PART_ASSEMB_REVISION.equals(report.getSnecmaObject().getType())) {
            retrieveFrontiereAvionAndFrontiereCooperant(topItemRevision, RF_PART_ASSEMB_REVISION, ASSEMBLY_BOM,
                    session, report);
        } else {
            retrieveFrontiereAvionAndFrontiereCooperant(topItemRevision, RF_PART_REVISION, RF_PART_BOM,
                    session, report);
        }
        report.setProduitUnit("");

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

        log.debug(EXECUTION_TIME_XXX_MS_FOR_YYY, System.currentTimeMillis() - start, "data gathering");
        return printParameters;
    }

    private void retrieveFrontiereAvionAndFrontiereCooperant(final ItemRevision topItemRevision, final String typeTopItemRev,
                                                             final String propertyPseudoFolder, final TcSession session, final ExecutionR15 report)
            throws KurtException {
        List<ItemRevision> repereResults = session.whereUsed(topItemRevision, typeTopItemRev);
        if (repereResults.isEmpty()) {

            report.setFrontiereAvion(DASH_STRING);
            report.setFrontiereCooperant(DASH_STRING);
            return;
        }
        Set<ItemRevision> selectedRfAdminList = new HashSet<>();
        for (ItemRevision rfPartAssemb : repereResults) {
            List<ModelObject> resultsPseudoFolder =
                    ObjectInfoHelper.getModelObjectsInPseudoFolderUnderItemRevision(session, rfPartAssemb, propertyPseudoFolder);
            if (resultsPseudoFolder.isEmpty()) {
                continue;
            }
            treatRfAdminInPseudofolder(session, selectedRfAdminList, resultsPseudoFolder);
        }
        putvaluesFrontiereCooperantAndAvion(session, selectedRfAdminList, report);
    }

    private void treatRfAdminInPseudofolder(final TcSession session, final Set<ItemRevision> selectedRfAdminList, final List<ModelObject> resultsPseudoFolder) {
        for (ModelObject objectItem : resultsPseudoFolder) {
            Item item = (Item) objectItem;
            if (!RF_ADMIN.equals(item.getTypeObject().getName())) {
                continue;
            }

            session.loadProperties(item, "revision_list");
            ModelObject[] listRevision = item.get_revision_list();
            session.loadProperties(listRevision);

            // Convert List<ModelObject> to List<ItemRevision>
            List<ItemRevision> interList = new ArrayList<>();
            for (ModelObject object : listRevision) {
                interList.add((ItemRevision) object);
            }

            // Find the appropriate Admin for the current RF Part or RF Part Assemb
            Optional<ItemRevision> selectedRfAdmin = retrieveAdminWithMostRecentDateRelease(session, interList);

            if (selectedRfAdmin.isPresent()) {
                selectedRfAdminList.add(selectedRfAdmin.get());
            }
        }
    }

    private Optional<ItemRevision> retrieveAdminWithMostRecentDateRelease(final TcSession session, final List<ItemRevision> interList) {
        return ObjectInfoHelper.getRevisionFromStatusWithMostRecentReleaseDateNotCancelled(session, interList, Calendar.getInstance());
    }

    private void putvaluesFrontiereCooperantAndAvion(final TcSession session, final Set<ItemRevision> selectedRfAdminList,
                                                     final ExecutionR15 report) {
        if (selectedRfAdminList.isEmpty()) {
            report.setFrontiereAvion(DASH_STRING);
            report.setFrontiereCooperant(DASH_STRING);

        } else {

            report.setFrontiereAvion(getFrontiereAvionOrCooperantProperty(session, selectedRfAdminList, SnecmaObjectProperties.PV_FRONTIERE_AVION));
            report.setFrontiereCooperant(getFrontiereAvionOrCooperantProperty(session, selectedRfAdminList, SnecmaObjectProperties.PV_FRONTIERE_COOPERANT));

        }
    }

    private String getFrontiereAvionOrCooperantProperty(final TcSession session, final Set<ItemRevision> selectedRfAdminList, final String prop) {
        boolean isYes = findValueOfPropertyInList(session, selectedRfAdminList, prop, OUI_YES);

        if (isYes) {
            return OUI_YES;
        } else {
            boolean isNo = findValueOfPropertyInList(session, selectedRfAdminList, prop, NON_NO);
            if (isNo) {
                return NON_NO;
            } else {
                return DASH_STRING;
            }
        }
    }

    private boolean findValueOfPropertyInList(final TcSession session, final Set<ItemRevision> selectedRfAdmins, final String property, final String wantedValue) {
        for (ItemRevision rfAdmin : selectedRfAdmins) {
            rfAdmin.get_item_id();
            Form form = rfAdmin.get_item_master_tag();
            if (form != null) {
                session.loadProperties(form);
                String valueProp = form.getPropertyObject(property).getStringValue();
                if (wantedValue.equals(valueProp)) {
                    return true;
                }
            }
        }
        return false;
    }
}
