package com.capgemini.reports.workflows;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.exceptions.KurtException;
import com.capgemini.reports.changeobjects.change.AbstractChangeObject;
import com.capgemini.reports.users.SnecmaUser;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.EPMTask;
import com.teamcenter.soa.client.model.strong.ItemRevision;
import com.teamcenter.soa.client.model.strong.Signoff;
import com.teamcenter.soa.client.model.strong.User;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import static com.capgemini.framework.teamcenter.TeamcenterObjectTypes.SIGNOFF;
import static com.capgemini.framework.teamcenter.TeamcenterObjectTypes.USER;

/**
 * Represents a TC EPMTask.
 */
@Slf4j
public class Task implements Serializable {
  private static final long serialVersionUID = 2426203736278592266L;

  private static final int VAL_DECISION_PENDING = 0;
  private final TcSession session;
  private final EPMTask task;

  public Task(final TcSession session, final EPMTask task) {
    this.session = session;
    this.task = task;
  }

  public String getType() {
    return task.getTypeObject().getName();
  }

  public String getName() {
    return task.get_object_name();
  }

  public SnecmaUser getOwningUser() {
    session.loadProperties(task, "owning_user");
    User user = (User) task.get_owning_user();
    session.loadProperties(user);
    return new SnecmaUser(session, user);
  }

  public SnecmaUser getResponsibleUser() throws KurtException {
    try {
      session.loadProperties(task, "fnd0Assignee");
      User user = (User) task.get_fnd0Assignee();
      session.loadProperties(user);
      return new SnecmaUser(session, user);
    }
    catch (ClassCastException e) {
      throw new KurtException("Erreur lors de la récupération du User responsable.", e);
    }
  }


  public SnecmaUser getLastModUser() {

    session.loadProperties(task, "last_mod_user");
    User user = (User) task.get_last_mod_user();
    session.loadProperties(user);
    return new SnecmaUser(session, user);
  }

  public Calendar getLastModDate() {
    return task.get_last_mod_date();
  }

  public Calendar getCreationDate() {
    return task.get_creation_date();
  }

  public Calendar getDueDate() {
    return task.get_due_date();
  }

  public TaskState getState() {
    return TaskState.findState(task.get_state());
  }

  public boolean isInTargetAttachments(final AbstractChangeObject change) {
    session.loadProperties(task, "root_target_attachments");
    ModelObject[] rootTargetObjects = task.get_root_target_attachments();
    session.loadProperties(rootTargetObjects);
    for (ModelObject object : rootTargetObjects) {
      if (object instanceof ItemRevision) {
        ItemRevision itemRevision = (ItemRevision) object;
        if (itemRevision.get_item_id().equals(change.getItemId()) && itemRevision.get_item_revision_id().equals(change.getRevisionId())) {
          return true;
        }
      }
    }
    return false;
  }

  public List<Task> getActiveChildTasks() {
    return getChildren(TaskState.STARTED);
  }

  public List<Task> getCompletedChildTasks() {
    return getChildren(TaskState.COMPLETED);
  }

  public List<Task> getChildren(final TaskState state) {
    List<Task> children = new ArrayList<>();
    session.loadProperties(task, "child_tasks");
    ModelObject[] childTasks = task.get_child_tasks();
    session.loadProperties(childTasks);
    for (ModelObject childTask : childTasks) {
      if (childTask instanceof EPMTask) {
        if (state.equals(TaskState.findState(((EPMTask) childTask).get_state()))) {
          children.add(new Task(session, (EPMTask) childTask));
        }
      }
    }

    return children;
  }

  public List<SnecmaUser> getPendingSignoffsUser() {
    List<SnecmaUser> pendingSignoffsUser = new ArrayList<>();
    session.loadProperties(task, "signoff_attachments");
    ModelObject[] signoffAttachments = task.get_signoff_attachments();
    session.loadProperties(signoffAttachments);
    for (ModelObject signoff : signoffAttachments) {
      if (SIGNOFF.equals(signoff.getTypeObject().getName()) && ((Signoff) signoff).get_decision() == VAL_DECISION_PENDING) {
        session.loadProperties(signoff, "owning_user");
        User signoffOwner = (User) ((Signoff) signoff).get_owning_user();
        session.loadProperties(signoffOwner);
        if (USER.equals(signoffOwner.getTypeObject().getName())) {
          pendingSignoffsUser.add(new SnecmaUser(session, signoffOwner));
        }
      }
    }

    return pendingSignoffsUser;
  }

  public boolean isRunning() {
    return getState() == TaskState.STARTED;
  }

  @Override
  public String toString() {
    return getType() + " " + getName();
  }


  /**
   * Represents the state of a Task.
   */
  public enum TaskState {
    UNASSIGNED, // Unassigned task state. Code 1.
    PENDING,    // Pending task state. Code 2.
    STARTED,    // Started task state. Code 4.
    COMPLETED,  // Completed task state. Code 8.
    SKIPPED,    // Skipped task state. Code 16.
    ABORTED,    // Aborted task state. Code 32.
    FAILED,     // Failed task state. Code 64.
    SUSPENDED;  // Suspended task state. Code 128.

    private static final int TC_CODE_FOR_SUSPENDED = 128;
    private static final int TC_CODE_FOR_FAILED = 64;
    private static final int TC_CODE_FOR_ABORTED = 32;
    private static final int TC_CODE_FOR_SKIPPED = 16;
    private static final int TC_CODE_FOR_COMPLETED = 8;
    private static final int TC_CODE_FOR_STARTED = 4;
    private static final int TC_CODE_FOR_PENDING = 2;
    private static final int TC_CODE_FOR_UNASSIGNED = 1;

    /**
     * Return the matching State enum instance form the TC int value, based on the TC convention.
     */
    public static TaskState findState(final int intValue) {
      switch (intValue) {
        case TC_CODE_FOR_UNASSIGNED:
          return TaskState.UNASSIGNED;
        case TC_CODE_FOR_PENDING:
          return TaskState.PENDING;
        case TC_CODE_FOR_STARTED:
          return TaskState.STARTED;
        case TC_CODE_FOR_COMPLETED:
          return TaskState.COMPLETED;
        case TC_CODE_FOR_SKIPPED:
          return TaskState.SKIPPED;
        case TC_CODE_FOR_ABORTED:
          return TaskState.ABORTED;
        case TC_CODE_FOR_FAILED:
          return TaskState.FAILED;
        case TC_CODE_FOR_SUSPENDED:
          return TaskState.SUSPENDED;
        default:
          log.error("Task State unknown: {}", intValue);
          return null;
      }
    }

    /**
     * Returns true if this {@link TaskState} and the provided Task code are a match.
     */
    public boolean matches(final int intValue) {
      switch (intValue) {
        case TC_CODE_FOR_UNASSIGNED:
          return this == TaskState.UNASSIGNED;
        case TC_CODE_FOR_PENDING:
          return this == TaskState.PENDING;
        case TC_CODE_FOR_STARTED:
          return this == TaskState.STARTED;
        case TC_CODE_FOR_COMPLETED:
          return this == TaskState.COMPLETED;
        case TC_CODE_FOR_SKIPPED:
          return this == TaskState.SKIPPED;
        case TC_CODE_FOR_ABORTED:
          return this == TaskState.ABORTED;
        case TC_CODE_FOR_FAILED:
          return this == TaskState.FAILED;
        case TC_CODE_FOR_SUSPENDED:
          return this == TaskState.SUSPENDED;
        default:
          log.error("Task Code unknown: {}", intValue);
          return false;
      }
    }
  }
}
