private void removeOldIssueLinks(Issue issue, net.rcarz.jiraclient.Issue jiraIssue)
      throws JiraException, NotFoundException {
    Set<String> ids = new HashSet<>();
    for (URL url : Iterables.concat(issue.getBlocks(), issue.getDependsOn()))
      ids.add(getIssueKey(url));

    for (IssueLink link : jiraIssue.getIssueLinks()) {
      net.rcarz.jiraclient.Issue linkedIssue = link.getInwardIssue();
      if (linkedIssue == null) linkedIssue = link.getOutwardIssue();

      if (!ids.contains(linkedIssue.getKey())) link.delete();
    }
  }
  /**
   * Known limitations: - Jira api does not allow an issue type to be update (WTF?) - Jira api does
   * not allow project to be changed
   */
  @Override
  public boolean updateIssue(Issue issue) throws NotFoundException, AphroditeException {
    checkHost(issue.getURL());

    try {
      net.rcarz.jiraclient.Issue jiraIssue = getIssue(issue);
      net.rcarz.jiraclient.Issue.FluentUpdate update =
          WRAPPER.issueToFluentUpdate(issue, jiraIssue.update());
      update.execute();
      if (!hasSameIssueStatus(issue, jiraIssue)) {
        String transition = getJiraTransition(issue, jiraIssue);
        jiraIssue.transition().execute(transition);
      }
      addNewIssueLinks(issue, jiraIssue);
      removeOldIssueLinks(issue, jiraIssue);
      return true;
    } catch (JiraException e) {
      throw new AphroditeException(getUpdateErrorMessage(issue, e), e);
    }
  }
  private void addNewIssueLinks(Issue issue, net.rcarz.jiraclient.Issue jiraIssue)
      throws JiraException, NotFoundException {
    for (URL url : issue.getBlocks()) {
      String trackerId = getIssueKey(url);
      net.rcarz.jiraclient.Issue issueToBlock = getIssue(trackerId);
      if (!issueLinkExists(url, issueToBlock.getIssueLinks()))
        issueToBlock.link(jiraIssue.getKey(), "Dependency");
    }

    for (URL url : issue.getDependsOn()) {
      if (!issueLinkExists(url, jiraIssue.getIssueLinks())) {
        String trackerId = getIssueKey(url);
        jiraIssue.link(trackerId, "Dependency");
      }
    }
  }
  public JiraMetricIssue(final Issue issue) {
    Preconditions.checkNotNull(issue, "Issue cannot be null...");
    this.issueKey = issue.getKey();
    this.summary = issue.getSummary();
    this.description = issue.getDescription();
    this.issueId = Long.valueOf(issue.getId());
    if (issue.getStatus() != null) {
      this.status = issue.getStatus().getName();
    }
    if (issue.getProject() != null) {
      this.projectKey = issue.getProject().getKey();
    }
    if (issue.getReporter() != null) {
      this.reporterName = issue.getReporter().getName();
    }
    if (issue.getAssignee() != null) {
      this.assigneeName = issue.getAssignee().getName();
    }
    if (issue.getResolution() != null) {
      this.resolution = issue.getResolution().getName();
    }
    this.creationDate = issue.getCreatedDate() != null ? issue.getCreatedDate().getTime() : 0;
    this.updateDate = issue.getUpdatedDate() != null ? issue.getUpdatedDate().getTime() : 0;
    this.dueDate = issue.getDueDate() != null ? issue.getDueDate().getTime() : 0;
    if (issue.getPriority() != null) {
      this.priority =
          issue.getPriority().getId() != null ? Long.valueOf(issue.getPriority().getId()) : 0L;
    }
    if (issue.getTimeTracking() != null) {
      this.originalEstimateMinutes = issue.getTimeTracking().getOriginalEstimateSeconds() / 60;
      this.remainingEstimateMinutes = issue.getTimeTracking().getRemainingEstimateSeconds() / 60;
      this.timeSpentMinutes = issue.getTimeTracking().getTimeSpentSeconds() / 60;
    }
    if (issue.getIssueType() != null) {
      IssueType jiraIssueType = issue.getIssueType();
      this.type = new JarvisIssueType(Long.valueOf(jiraIssueType.getId()), jiraIssueType.getName());
      this.typeId = Long.valueOf(jiraIssueType.getId());
      this.typeName = jiraIssueType.getName();
    }
    if (issue.getChangeLog() != null) {
      final List<JiraIssueChangelog> changelogList = Lists.newArrayList();
      final ChangeLog changeLog = issue.getChangeLog();

      for (final ChangeLogEntry changeLogEntry : changeLog.getEntries()) {
        for (final ChangeLogItem changeLogItem : changeLogEntry.getItems()) {
          ChangeCompositeKey changeCompositeKey =
              new ChangeCompositeKey(
                  Long.valueOf(changeLogEntry.getId()), changeLogEntry.getCreated().getTime());
          String issueKey = issue.getKey();
          Long issueId = Long.valueOf(issue.getId());
          String author =
              changeLogEntry.getAuthor() != null
                  ? changeLogEntry.getAuthor().getDisplayName()
                  : "Unknown";

          JiraIssueChangelog jiraIssueChangelog =
              new JiraIssueChangelog(
                  changeCompositeKey,
                  issueKey,
                  issueId,
                  author,
                  changeLogItem.getFieldType(),
                  changeLogItem.getField(),
                  changeLogItem.getFromString(),
                  changeLogItem.getToString(),
                  changeLogItem.getFrom(),
                  changeLogItem.getTo());
          changelogList.add(jiraIssueChangelog);
        }
      }
      this.changelogList = changelogList;
    }

    try {
      for (final WorkLog workLog : issue.getWorkLogs()) {
        IssueWorkLog issueWorkLog = new IssueWorkLog(workLog);
        this.issueWorkLogs.add(issueWorkLog);
      }

      for (final RemoteLink remoteLink : issue.getRemoteLinks()) {
        IssueRemoteLink issueRemoteLink =
            new IssueRemoteLink(
                remoteLink.getTitle(),
                remoteLink.getRemoteUrl(),
                String.format("%s-%s", remoteLink.getId(), this.issueId),
                remoteLink.getUrl());
        remoteLinks.add(issueRemoteLink);
      }

      for (final Component component : issue.getComponents()) {
        this.components.add(component.getName());
      }

      this.labels.addAll(issue.getLabels());
    } catch (Exception e) {
      // ignore
      e.printStackTrace();
    }

    if (issue.getIssueLinks() != null) {
      List<JarvisLink> jarvisIssueLinks = Lists.newArrayList();
      for (final IssueLink issueLink : issue.getIssueLinks()) {
        LinkDirection linkDirection = new LinkDirection();
        Issue linkedIssue = issueLink.getInwardIssue();
        JarvisLink.Direction direction = JarvisLink.Direction.INWARD;

        if (linkedIssue == null) {
          linkedIssue = issueLink.getOutwardIssue();
          direction = JarvisLink.Direction.OUTWARD;
        }

        linkDirection.setIssueId(Long.valueOf(linkedIssue.getId()));
        linkDirection.setIssueKey(linkedIssue.getKey());

        IssueType linkedIssueType = linkedIssue.getIssueType();

        net.rcarz.jiraclient.LinkType jiraLinkType = issueLink.getType();

        if (linkedIssueType != null && linkedIssueType != null) {
          LinkType linkType =
              new LinkType(
                  Long.valueOf(jiraLinkType.getId()),
                  jiraLinkType.getName(),
                  jiraLinkType.getInward(),
                  jiraLinkType.getOutward());

          JarvisIssueType jarvisIssueType =
              new JarvisIssueType(Long.valueOf(linkedIssueType.getId()), linkedIssueType.getName());

          JarvisLink jarvisLink =
              new JarvisLink(
                  Long.valueOf(issueLink.getId()),
                  linkType,
                  linkDirection,
                  jarvisIssueType,
                  direction);

          jarvisIssueLinks.add(jarvisLink);
        }
      }
      this.issueLinks = jarvisIssueLinks;
    }
  }