package com.capgemini.reports.changeobjects.change;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.object.pom.status.TcReleaseStatus;
import com.capgemini.reports.exceptions.NotSupportedException;
import com.capgemini.reports.snecmaobjects.SnecmaObjectFactory;
import com.capgemini.reports.snecmaobjects.generic.AbstractGeneric;
import com.capgemini.reports.teamcenter.queries.saved.WorkflowOnChangeQuery;
import com.capgemini.reports.users.SnecmaUser;
import com.capgemini.reports.workflows.Task;
import com.google.common.base.Joiner;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.*;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.*;

import static com.capgemini.framework.teamcenter.TeamcenterObjectProperties.ITEM_ID;
import static com.capgemini.framework.teamcenter.TeamcenterObjectProperties.PROP_LIGNE_PRODUIT;
import static com.capgemini.framework.teamcenter.object.SnecmaObjectProperties.CM_HAS_SOLUTION_ITEM;
import static com.capgemini.framework.teamcenter.object.SnecmaObjectProperties.OBJECT_TYPE;
import static com.capgemini.framework.teamcenter.object.types.SnecmaObjectTypes.*;

/**
 * Abstract class representing a ChangeObject.
 */
@Slf4j
public abstract class AbstractChangeObject implements Serializable {
    private static final long serialVersionUID = -133060583639527983L;

    protected final TcSession session;
    protected final ItemRevision itemRevision;
    protected SnecmaUser user;

    protected AbstractChangeObject(final TcSession session, final ItemRevision itemRevision) {
        this.session = session;
        this.itemRevision = itemRevision;
    }

    private static Item obtainItem(final TcSession session, final ModelObject currentObject) throws NotSupportedException {
        Item item;
        if (currentObject instanceof Item) {
            // Normal behavior: master in the pseudo-folder
            item = (Item) currentObject;
        } else if (currentObject instanceof ItemRevision) {
            // There might still be ItemRevisions here! We must try to findAll their Item, and pursue
            ItemRevision tempRev = (ItemRevision) currentObject;
            session.loadProperties(tempRev, "items_tag");
            item = tempRev.get_items_tag();
            session.loadProperties(item);
        } else {
            throw new NotSupportedException("Found a {} (with UID = {}) in the pseudo-folder. Not supported object, skipping...");
        }
        return item;

    }

    public String getItemId() {
        return itemRevision.get_item_id();
    }

    public String getRevisionId() {
        return itemRevision.get_item_revision_id();
    }

    public int getSequenceId() {
        return itemRevision.get_sequence_id();
    }

    public String getChangeName() {
        return itemRevision.get_object_name();
    }

    public Calendar getDateCreation() {
        return itemRevision.get_creation_date();
    }

    public String getChangeType() {
        return itemRevision.getTypeObject().getName();
    }

    public SnecmaUser getUser() {
        session.loadProperties(itemRevision, "owning_user");
        User owningUser = (User) itemRevision.get_owning_user();
        session.loadProperties(owningUser);
        return new SnecmaUser(session, owningUser);
    }

    public List<String> getLignesProduit() {
        List<String> lineList = new ArrayList<>();
        Collections.addAll(lineList, itemRevision.getPropertyObject(PROP_LIGNE_PRODUIT).getStringArrayValue());
        return lineList;
    }

    public List<TcReleaseStatus> getReleaseStatusContainingEffectivity() {
        List<TcReleaseStatus> releaseStatusContainingEffectivity = new ArrayList<>();
        session.loadProperties(itemRevision, "release_status_list");
        ReleaseStatus[] releaseStatuses = itemRevision.get_release_status_list();
        session.loadProperties(releaseStatuses);
        for (ReleaseStatus status : releaseStatuses) {
            TcReleaseStatus statusSnecma = new TcReleaseStatus(session, status);
            if (statusSnecma.isEffectivity()) {
                releaseStatusContainingEffectivity.add(statusSnecma);
            }
        }
        return releaseStatusContainingEffectivity;
    }


    public List<TcReleaseStatus> getReleaseStatusContainingMaturity() {
        List<TcReleaseStatus> releaseStatusContainingMaturity = new ArrayList<>();
        session.loadProperties(itemRevision, "release_status_list");
        ReleaseStatus[] releaseStatuses = itemRevision.get_release_status_list();
        session.loadProperties(releaseStatuses);
        for (ReleaseStatus status : releaseStatuses) {
            TcReleaseStatus statusSnecma = new TcReleaseStatus(session, status);
            if (statusSnecma.isChangeMaturity()) {
                releaseStatusContainingMaturity.add(statusSnecma);
            }
        }
        return releaseStatusContainingMaturity;
    }

    /**
     * Set the list of all Workflows associated to this change.
     */
    public List<Task> getWkfList() {
        List<EPMTask> result = new WorkflowOnChangeQuery(session).searchIDAndRevision(this.getItemId(), this.getRevisionId()).searchSequenceId(String.valueOf(getSequenceId())).findAll();
        List<Task> taskList = new ArrayList<>();
        for (EPMTask task : result) {
            taskList.add(new Task(session, task));
        }
        return taskList;
    }

    /**
     * Get the list of the generic 'with/without rupture of interchangeabiliy' from an ItemRevision representing a ChangeNotice/Request.
     */
    AbstractGeneric loadGeneric(final ModelObject genericObject) throws NotSupportedException {
        return SnecmaObjectFactory.build(session, obtainItem(session, genericObject));
    }

    @Override
    public String toString() {
        return this.getChangeType() + " " + this.getItemId() + "/" + this.getRevisionId();
    }

    /**
     * Method to get Parts from CR/CN or FPP inside the apres pseudo folder
     * If input param is not null it gets the apres pseudo folder of this ModelObject (case of a FPP)
     * If it is null it gets the apres pseudo folder of itemRevision attribute of the current instance (Case of CR/CN)
     *
     * @param object, ModelObject where the apres pseudo folder is
     * @return Set<ModelObject>
     */
    public Set<ModelObject> getPartsFromApresPseudoFolder(ModelObject object) {
        List<ModelObject> apresPseudoFolderContent;

        // sous modification pseudo folder for fpp
        if (object != null) {
            session.loadProperties(object, CM_HAS_SOLUTION_ITEM);
            apresPseudoFolderContent = object.getPropertyObject(CM_HAS_SOLUTION_ITEM).getModelObjectListValue();
        } else {
            // for cr/cn
            session.loadProperties(itemRevision, CM_HAS_SOLUTION_ITEM);
            apresPseudoFolderContent = itemRevision.getPropertyObject(CM_HAS_SOLUTION_ITEM).getModelObjectListValue();
        }
        Set<ModelObject> result = new HashSet<>();
        for (ModelObject part : apresPseudoFolderContent) {
            // get parts type
            session.loadProperties(part, OBJECT_TYPE, ITEM_ID);
            String part_type = part.getPropertyObject(OBJECT_TYPE).getStringValue();
            if (PART_AERO_REVISION.equals(part_type) || PART_NO_AERO_REVISION.equals(part_type) || STANDARD_REVISION.equals(part_type) || DET_REVISION.equals(part_type) || PART_MAT_REVISION.equals(part_type) || ALTERNATE_REVISION.equals(part_type) || PART_ASSEMB_REVISION.equals(part_type)) {
                result.add(part);
            }
        }
        return result;
    }

    /**
     * Return a String with all itemsID of the modelObjects in the set separated by ;
     *
     * @param set
     * @return String
     */
    public static String getItemIdFromModelObjectSetToString(Set<ModelObject> set) {
        Set<String> result = new HashSet<>();
        for (ModelObject model : set) {
            result.add(model.getPropertyObject(ITEM_ID).getStringValue());
        }
        return Joiner.on(";").join(result);
    }

}
