private List<SubscriptionBaseEvent> toEvents(
      final DefaultSubscriptionBase defaultSubscriptionBase,
      final DateTime now,
      final DateTime ctd,
      final TimedMigration[] migrationEvents,
      final CallContext context) {

    if (ctd == null) {
      throw new SubscriptionBaseError(
          String.format("Could not create migration billing event ctd = %s", ctd));
    }

    final List<SubscriptionBaseEvent> events =
        new ArrayList<SubscriptionBaseEvent>(migrationEvents.length);

    ApiEventMigrateBilling apiEventMigrateBilling = null;

    // The first event date after the MIGRATE_ENTITLEMENT event
    DateTime nextEventDate = null;

    boolean isCancelledSubscriptionPriorOrAtCTD = false;

    for (final TimedMigration cur : migrationEvents) {

      final ApiEventBuilder builder =
          new ApiEventBuilder()
              .setSubscriptionId(defaultSubscriptionBase.getId())
              .setEventPlan((cur.getPlan() != null) ? cur.getPlan().getName() : null)
              .setEventPlanPhase((cur.getPhase() != null) ? cur.getPhase().getName() : null)
              .setEventPriceList(cur.getPriceList())
              .setActiveVersion(defaultSubscriptionBase.getActiveVersion())
              .setEffectiveDate(cur.getEventTime())
              .setProcessedDate(now)
              .setRequestedDate(now)
              .setFromDisk(true);

      if (cur.getEventType() == EventType.PHASE) {
        nextEventDate =
            nextEventDate != null && nextEventDate.compareTo(cur.getEventTime()) < 0
                ? nextEventDate
                : cur.getEventTime();
        final PhaseEvent nextPhaseEvent =
            PhaseEventData.createNextPhaseEvent(
                defaultSubscriptionBase.getId(),
                defaultSubscriptionBase.getActiveVersion(),
                cur.getPhase().getName(),
                now,
                cur.getEventTime());
        events.add(nextPhaseEvent);

      } else if (cur.getEventType() == EventType.API_USER) {

        switch (cur.getApiEventType()) {
          case MIGRATE_ENTITLEMENT:
            ApiEventMigrateSubscription creationEvent = new ApiEventMigrateSubscription(builder);
            events.add(creationEvent);
            break;

          case CHANGE:
            nextEventDate =
                nextEventDate != null && nextEventDate.compareTo(cur.getEventTime()) < 0
                    ? nextEventDate
                    : cur.getEventTime();
            events.add(new ApiEventChange(builder));
            break;
          case CANCEL:
            isCancelledSubscriptionPriorOrAtCTD = !cur.getEventTime().isAfter(ctd);
            nextEventDate =
                nextEventDate != null && nextEventDate.compareTo(cur.getEventTime()) < 0
                    ? nextEventDate
                    : cur.getEventTime();
            events.add(new ApiEventCancel(builder));
            break;
          default:
            throw new SubscriptionBaseError(
                String.format("Unexpected type of api migration event %s", cur.getApiEventType()));
        }
      } else {
        throw new SubscriptionBaseError(
            String.format("Unexpected type of migration event %s", cur.getEventType()));
      }

      // create the MIGRATE_BILLING based on the current state of the last event.
      if (!cur.getEventTime().isAfter(ctd)) {
        builder.setEffectiveDate(ctd);
        builder.setUuid(UUID.randomUUID());
        apiEventMigrateBilling = new ApiEventMigrateBilling(builder);
      }
    }
    // Always ADD MIGRATE BILLING which is constructed from latest state seen in the stream prior to
    // CTD
    if (apiEventMigrateBilling != null && !isCancelledSubscriptionPriorOrAtCTD) {
      events.add(apiEventMigrateBilling);
    }

    Collections.sort(
        events,
        new Comparator<SubscriptionBaseEvent>() {
          int compForApiType(
              final SubscriptionBaseEvent o1,
              final SubscriptionBaseEvent o2,
              final ApiEventType type) {
            ApiEventType apiO1 = null;
            if (o1.getType() == EventType.API_USER) {
              apiO1 = ((ApiEvent) o1).getEventType();
            }
            ApiEventType apiO2 = null;
            if (o2.getType() == EventType.API_USER) {
              apiO2 = ((ApiEvent) o2).getEventType();
            }
            if (apiO1 != null && apiO1.equals(type)) {
              return -1;
            } else if (apiO2 != null && apiO2.equals(type)) {
              return 1;
            } else {
              return 0;
            }
          }

          @Override
          public int compare(final SubscriptionBaseEvent o1, final SubscriptionBaseEvent o2) {

            int comp = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
            if (comp == 0) {
              comp = compForApiType(o1, o2, ApiEventType.MIGRATE_ENTITLEMENT);
            }
            if (comp == 0) {
              comp = compForApiType(o1, o2, ApiEventType.MIGRATE_BILLING);
            }
            return comp;
          }
        });

    return events;
  }
  @Override
  public SubscriptionBase createSubscription(
      final UUID bundleId,
      final PlanPhaseSpecifier spec,
      final DateTime requestedDateWithMs,
      final InternalCallContext context)
      throws SubscriptionBaseApiException {
    try {
      final String realPriceList =
          (spec.getPriceListName() == null)
              ? PriceListSet.DEFAULT_PRICELIST_NAME
              : spec.getPriceListName();
      final DateTime now = clock.getUTCNow();
      final DateTime requestedDate =
          (requestedDateWithMs != null) ? DefaultClock.truncateMs(requestedDateWithMs) : now;
      if (requestedDate.isAfter(now)) {
        throw new SubscriptionBaseApiException(
            ErrorCode.SUB_INVALID_REQUESTED_DATE, now.toString(), requestedDate.toString());
      }
      final DateTime effectiveDate = requestedDate;

      final Catalog catalog = catalogService.getFullCatalog();
      final Plan plan =
          catalog.findPlan(
              spec.getProductName(), spec.getBillingPeriod(), realPriceList, requestedDate);

      final PlanPhase phase = plan.getAllPhases()[0];
      if (phase == null) {
        throw new SubscriptionBaseError(
            String.format(
                "No initial PlanPhase for Product %s, term %s and set %s does not exist in the catalog",
                spec.getProductName(), spec.getBillingPeriod().toString(), realPriceList));
      }

      final SubscriptionBaseBundle bundle = dao.getSubscriptionBundleFromId(bundleId, context);
      if (bundle == null) {
        throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BUNDLE, bundleId);
      }

      DateTime bundleStartDate = null;
      final DefaultSubscriptionBase baseSubscription =
          (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
      switch (plan.getProduct().getCategory()) {
        case BASE:
          if (baseSubscription != null) {
            if (baseSubscription.getState() == EntitlementState.ACTIVE) {
              throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
            }
          }
          bundleStartDate = requestedDate;
          break;
        case ADD_ON:
          if (baseSubscription == null) {
            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_NO_BP, bundleId);
          }
          if (effectiveDate.isBefore(baseSubscription.getStartDate())) {
            throw new SubscriptionBaseApiException(
                ErrorCode.SUB_INVALID_REQUESTED_DATE,
                effectiveDate.toString(),
                baseSubscription.getStartDate().toString());
          }
          addonUtils.checkAddonCreationRights(baseSubscription, plan);
          bundleStartDate = baseSubscription.getStartDate();
          break;
        case STANDALONE:
          if (baseSubscription != null) {
            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_BP_EXISTS, bundleId);
          }
          // Not really but we don't care, there is no alignment for STANDALONE subscriptions
          bundleStartDate = requestedDate;
          break;
        default:
          throw new SubscriptionBaseError(
              String.format(
                  "Can't create subscription of type %s",
                  plan.getProduct().getCategory().toString()));
      }

      final UUID tenantId =
          nonEntityDao.retrieveIdFromObject(context.getTenantRecordId(), ObjectType.TENANT);
      return apiService.createPlan(
          new SubscriptionBuilder()
              .setId(UUID.randomUUID())
              .setBundleId(bundleId)
              .setCategory(plan.getProduct().getCategory())
              .setBundleStartDate(bundleStartDate)
              .setAlignStartDate(effectiveDate),
          plan,
          spec.getPhaseType(),
          realPriceList,
          requestedDate,
          effectiveDate,
          now,
          context.toCallContext(tenantId));
    } catch (CatalogApiException e) {
      throw new SubscriptionBaseApiException(e);
    }
  }