/*
 * Copyright 2020, 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.storage.memory;

import io.spine.test.storage.ProjectId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import static com.google.common.collect.Lists.newArrayListWithExpectedSize;
import static com.google.common.collect.Sets.newHashSetWithExpectedSize;
import static org.junit.jupiter.api.Assertions.assertEquals;

@DisplayName("MultitenantStorage should")
class MultitenantStorageTest {

    private static final boolean IS_MULTITENANT = false;

    private MultitenantStorage<TenantRecords<ProjectId>> multitenantStorage;

    @BeforeEach
    void setUp() {
        multitenantStorage = new MultitenantStorage<TenantRecords<ProjectId>>(IS_MULTITENANT) {
            @Override
            TenantRecords<ProjectId> createSlice() {
                return new TenantRecords<>();
            }
        };
    }

    @Test
    @DisplayName("return same slice within single tenant and multitenant environment")
    void returnSameSlice()
            throws InterruptedException, ExecutionException {
        int numberOfTasks = 1000;
        Collection<Callable<TenantRecords>> tasks = newArrayListWithExpectedSize(numberOfTasks);

        for (int i = 0; i < numberOfTasks; i++) {
            tasks.add(() -> {
                TenantRecords<ProjectId> storage = multitenantStorage.currentSlice();
                return storage;
            });
        }

        List<Future<TenantRecords>> futures = executeInMultithreadedEnvironment(tasks);
        Set<TenantRecords> tenantRecords = convertFuturesToSetOfCompletedResults(futures);

        int expected = 1;
        assertEquals(expected, tenantRecords.size());
    }

    private static <R> Set<R> convertFuturesToSetOfCompletedResults(List<Future<R>> futures)
            throws ExecutionException, InterruptedException {
        Set<R> tenantRecords = newHashSetWithExpectedSize(futures.size());
        for (Future<R> future : futures) {
            tenantRecords.add(future.get());
        }
        return tenantRecords;
    }

    private static <R> List<Future<R>>
    executeInMultithreadedEnvironment(Collection<Callable<R>> tasks) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime()
                                                                       .availableProcessors() * 2);
        List<Future<R>> futures = executor.invokeAll(tasks);
        return futures;
    }
}

@@ -1047,16 +1047,60 @@
 emory;%0A%0A
+import io.spine.server.entity.EntityRecord;%0A
 import i
@@ -1756,16 +1756,17 @@
 ayName(%22
+%60
 Multiten
@@ -1775,16 +1775,17 @@
 tStorage
+%60
  should%22
@@ -1905,38 +1905,39 @@
 ntStorage%3CTenant
-Record
+Message
 s%3CProjectId%3E%3E mu
@@ -1931,16 +1931,30 @@
 rojectId
+, EntityRecord
 %3E%3E multi
@@ -2032,16 +2032,32 @@
 torage =
+%0A               
  new Mul
@@ -2074,30 +2074,31 @@
 orage%3CTenant
-Record
+Message
 s%3CProjectId%3E
@@ -2096,16 +2096,30 @@
 rojectId
+, EntityRecord
 %3E%3E(IS_MU
@@ -2143,16 +2143,24 @@
         
+        
 @Overrid
@@ -2173,38 +2173,61 @@
         
+ 
-TenantRecords%3CProjectI
+       TenantMessages%3CProjectId, EntityRecor
 d%3E creat
@@ -2249,24 +2249,32 @@
             
+        
 return new T
@@ -2278,22 +2278,23 @@
 w Tenant
-Record
+Message
 s%3C%3E();%0A 
@@ -2296,34 +2296,50 @@
 ();%0A            
+      
-%7D%0A
+  %7D%0A        
         %7D;%0A    %7D
@@ -2590,30 +2590,31 @@
 lable%3CTenant
-Record
+Message
 s%3E%3E tasks = 
@@ -2761,22 +2761,23 @@
   Tenant
-Record
+Message
 s%3CProjec
@@ -2779,16 +2779,30 @@
 rojectId
+, EntityRecord
 %3E storag
@@ -2925,22 +2925,23 @@
 e%3CTenant
-Record
+Message
 s%3E%3E futu
@@ -3006,22 +3006,23 @@
 t%3CTenant
-Record
+Message
 s%3E tenan

Migrate `MultitenantStorageTest`.