/*
 * Copyright 2018, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.server;

import io.spine.server.entity.Repository;
import io.spine.server.event.EventBus;
import io.spine.server.event.EventEnricher;
import io.spine.system.server.CommandLifecycleRepository;
import io.spine.system.server.EntityHistoryRepository;
import io.spine.system.server.NoOpSystemGateway;
import io.spine.system.server.ScheduledCommandRepository;
import io.spine.system.server.SystemEnricher;
import io.spine.system.server.SystemGateway;

import java.util.stream.Stream;

/**
 * An implementation of {@link BoundedContext} used for the System domain.
 *
 * <p>Orchestrates the system entities, such as
 * {@link io.spine.system.server.EntityHistoryAggregate EntityHistory} and
 * {@link io.spine.system.server.CommandLifecycleAggregate CommandLifecycle}.
 *
 * <p>Each {@link DomainBoundedContext} has an associated {@code SystemBoundedContext}.
 * The system entities describe the meta information about the domain entities of the associated
 * {@link DomainBoundedContext}. A system bounded context does NOT have an associated bounded
 * context.
 *
 * <p>The system entities serve the goal of monitoring, auditing, and debugging the domain-specific
 * entities.
 *
 * <p>Users should not access a System bounded context directly. See {@link SystemGateway} for
 * the front-facing API of the System bounded context.
 *
 * @author Dmytro Dashenkov
 * @see SystemGateway
 * @see BoundedContext
 * @see DomainBoundedContext
 */
final class SystemBoundedContext extends BoundedContext {

    private SystemBoundedContext(BoundedContextBuilder builder) {
        super(builder);
    }

    /**
     * Creates a new instance of {@code SystemBoundedContext} from the given
     * {@link BoundedContextBuilder}.
     *
     * @param builder the configuration of the instance to create
     * @return new {@code SystemBoundedContext}
     */
    static SystemBoundedContext newInstance(BoundedContextBuilder builder) {
        CommandLifecycleRepository repository = new CommandLifecycleRepository();
        BoundedContextBuilder preparedBuilder = prepareEnricher(builder, repository);
        SystemBoundedContext result = new SystemBoundedContext(preparedBuilder);
        result.init(repository);
        return result;
    }

    private static BoundedContextBuilder prepareEnricher(BoundedContextBuilder builder,
                                                         CommandLifecycleRepository repository) {
        EventBus.Builder busBuilder = builder.getEventBus()
                                             .orElseGet(EventBus::newBuilder);
        EventEnricher enricher = SystemEnricher.create(repository);
        EventBus.Builder builderWithEnricher = busBuilder.setEnricher(enricher);
        return builder.setEventBus(builderWithEnricher);
    }

    private void init(CommandLifecycleRepository commandLifecycle) {
        EntityHistoryRepository entityHistory = new EntityHistoryRepository();
        ScheduledCommandRepository scheduledCommand = new ScheduledCommandRepository();
        registerAll(entityHistory,
                    commandLifecycle,
                    scheduledCommand);
    }

    private void registerAll(Repository<?, ?>... repositories) {
        Stream.of(repositories)
              .forEach(this::register);
    }

    /**
     * {@inheritDoc}
     *
     * <p>Since a system bounded context does not have an associated system bounded context, returns
     * a {@link NoOpSystemGateway} instance.
     */
    @Override
    public NoOpSystemGateway getSystemGateway() {
        return NoOpSystemGateway.INSTANCE;
    }
}

@@ -1036,50 +1036,8 @@
 r;%0A%0A
-import io.spine.server.entity.Repository;%0A
 impo
@@ -1431,41 +1431,8 @@
 y;%0A%0A
-import java.util.stream.Stream;%0A%0A
 /**%0A
@@ -3798,164 +3798,33 @@
     
-EntityHistoryRepository entityHistory = new EntityHistoryRepository();%0A        ScheduledCommandRepository scheduledCommand = new ScheduledCommandRepository(
+register(commandLifecycle
 );%0A 
@@ -3842,13 +3842,14 @@
 ster
-All(e
+(new E
 ntit
@@ -3860,228 +3860,72 @@
 tory
-,%0A                    commandLifecycle,%0A                    scheduledCommand);%0A    %7D%0A%0A    private void registerAll(Repository%3C?, ?%3E... repositories) %7B%0A        Stream.of(repositories)%0A              .forEach(this::register
+Repository());%0A        register(new ScheduledCommandRepository()
 );%0A 

Simplify system repositories registration.