package com.capgemini.reports.execution.actions.ondataset;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.FailedRelationshipActionException;
import com.capgemini.framework.teamcenter.queries.saved.modelobject.IMANRelationQuery;
import com.capgemini.reports.database.javabean.DbReport;
import com.capgemini.reports.execution.AbstractExecutionReport;
import com.capgemini.reports.execution.actions.AbstractPostExecutionStep;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.ImanRelation;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;

import javax.inject.Provider;
import java.io.Serializable;
import java.util.List;

/**
 * This {@link AbstractPostExecutionStep} can destroy a Relationship between a Part and some of its datasets.
 *
 * @param <T> the report class
 */
@Slf4j
@EqualsAndHashCode(callSuper = true)
public class DestroyRelationshipsAction<T extends AbstractExecutionReport<T, ?>> extends AbstractPostExecutionStep<T, Void> {

    private static final long serialVersionUID = -3343435909197113891L;

    private final SerializableProvider<String> targetIdProvider;
    private final SerializableProvider<String> datasetNameProvider;

    /**
     * Constructs an action to be executed after the report generation is done.
     *
     * @param <NP>                a name provider class. Must be both {@link Serializable} and a {@link String} {@link Provider}.
     * @param reportClass         the report {@link Class}
     * @param targetIdProvider    a {@link Provider} which will eventually {@link Provider#get() provide} the partId (target)
     * @param datasetNameProvider a {@link Provider} which will eventually {@link Provider#get() provide} the dataset name
     */
    public <NP extends Provider<String> & Serializable> DestroyRelationshipsAction(Class<T> reportClass, NP targetIdProvider,
                                                                                   NP datasetNameProvider) {
        super(reportClass);
        // Both casts are safe because a SerializableProvider is both Serializable... and a Provider
        this.targetIdProvider = (SerializableProvider<String>) targetIdProvider;
        this.datasetNameProvider = (SerializableProvider<String>) datasetNameProvider;
    }

    /**
     * Destroys all relationship between a part and all dataset with specific name.
     *
     * @param session     an active Tc session
     * @param datasetName the name of the dataset
     * @param targetID    the name of the part which holds the dataset
     * @throws FailedRelationshipActionException in case the relationship could not be destroyed
     */
    public static void destroyConflictingDatasets(final TcSession session, final String datasetName, final String targetID)
            throws FailedRelationshipActionException {
        List<ImanRelation> existingRelations = new IMANRelationQuery(session).find(targetID, datasetName);
        log.debug("Wokflow launch: destroying conflicting datasets: {} relations found", existingRelations.size());
        // Let's browse all conflicting datasets
        for (ImanRelation relation : existingRelations) {
            ModelObject datasetMO = relation.get_secondary_object();

            // Let's destroy the relation first
            log.debug(" - Wokflow launch: destroying relation between Item {} and dataset {}", targetID, datasetMO.getUid());
            session.deleteObjects(relation);

            // Let's destroy the Dataset now that it is free from relationships
            log.debug(" - Wokflow launch: destroying dataset {}", datasetMO.getUid());
            session.deleteObjects(datasetMO);
        }
    }

    @Override
    protected Void performPostActionOnReport(final TcSession session, T reportRaw, DbReport theExecutingReport,
                                             String fileNameWithExtension) throws FailedRelationshipActionException {
        destroyConflictingDatasets(session, targetIdProvider.get(), datasetNameProvider.get());
        return null;
    }

}
