/**
   * Merges invalid chunks found in the target translation with a valid sibling chunk in order to
   * preserve translation data. Merged chunks are marked as not finished to force translators to
   * review the changes.
   *
   * @param library
   * @param sourceTranslation
   * @param targetTranslation
   * @param chapter
   * @return
   */
  private static boolean mergeInvalidChunksInChapter(
      final Library library,
      final SourceTranslation sourceTranslation,
      final TargetTranslation targetTranslation,
      final Chapter chapter) {
    boolean success = true;
    Logger.i(
        TargetTranslationMigrator.class.getName(),
        "Searching chapter " + chapter.getId() + " for invalid chunks ");
    // TRICKY: the translation format doesn't matter for migrating
    FrameTranslation[] frameTranslations =
        targetTranslation.getFrameTranslations(chapter.getId(), TranslationFormat.DEFAULT);
    String invalidChunks = "";
    Frame lastValidFrame = null;
    for (FrameTranslation frameTranslation : frameTranslations) {
      Frame frame = library.getFrame(sourceTranslation, chapter.getId(), frameTranslation.getId());
      if (frame != null) {
        lastValidFrame = frame;
        // merge invalid frames into the existing frame
        if (!invalidChunks.isEmpty()) {
          targetTranslation.applyFrameTranslation(
              frameTranslation, invalidChunks + frameTranslation.body);
          invalidChunks = "";
          targetTranslation.reopenFrame(frame);
        }
      } else if (!frameTranslation.body.trim().isEmpty()) {
        if (lastValidFrame == null) {
          // collect invalid frame
          invalidChunks += frameTranslation.body + CHUNK_MERGE_MARKER;
        } else { // if last frame is not null, then append invalid chunk to it
          FrameTranslation lastFrameTranslation =
              targetTranslation.getFrameTranslation(lastValidFrame);
          targetTranslation.applyFrameTranslation(
              lastFrameTranslation,
              lastFrameTranslation.body + CHUNK_MERGE_MARKER + frameTranslation.body);
          targetTranslation.reopenFrame(lastValidFrame);
        }
        targetTranslation.applyFrameTranslation(frameTranslation, ""); // clear out old data
      }
    }
    // clean up remaining invalid chunks
    if (!invalidChunks.isEmpty()) {
      if (lastValidFrame == null) {
        // push remaining invalid chunks onto the first available frame
        String[] frameslugs = library.getFrameSlugs(sourceTranslation, chapter.getId());
        if (frameslugs.length > 0) {
          lastValidFrame = library.getFrame(sourceTranslation, chapter.getId(), frameslugs[0]);
        } else {
          Logger.w(
              TargetTranslationMigrator.class.getName(),
              "No frames were found for chapter " + chapter.getId());
        }
      }

      if (lastValidFrame != null) {
        FrameTranslation frameTranslation = targetTranslation.getFrameTranslation(lastValidFrame);
        targetTranslation.applyFrameTranslation(
            frameTranslation, invalidChunks + CHUNK_MERGE_MARKER + frameTranslation.body);
        targetTranslation.reopenFrame(lastValidFrame);
      }
    }

    return success;
  }