/*
 * 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.client;

import com.google.protobuf.StringValue;
import com.google.protobuf.Timestamp;
import io.spine.core.ActorContext;
import io.spine.core.Command;
import io.spine.core.CommandContext;
import io.spine.core.TenantId;
import io.spine.core.UserId;
import io.spine.core.given.GivenTenantId;
import io.spine.core.given.GivenUserId;
import io.spine.time.testing.TimeTests;
import io.spine.test.commands.RequiredFieldCommand;
import io.spine.time.Timestamps2;
import io.spine.time.ZoneOffset;
import io.spine.time.ZoneOffsets;
import io.spine.validate.ValidationException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

@DisplayName("Command factory should")
class CommandFactoryTest extends ActorRequestFactoryTest {

    @Test
    @DisplayName("create command context for given parameters")
    void createCommandContext() {
        final TenantId tenantId = GivenTenantId.newUuid();
        final UserId userId = GivenUserId.newUuid();
        final ZoneOffset zoneOffset = ZoneOffsets.ofHours(-3);
        final int targetVersion = 100500;

        final CommandContext commandContext = CommandFactory.createContext(tenantId,
                                                                           userId,
                                                                           zoneOffset,
                                                                           targetVersion);

        final ActorContext actorContext = commandContext.getActorContext();

        assertEquals(tenantId, actorContext.getTenantId());
        assertEquals(userId, actorContext.getActor());
        assertEquals(zoneOffset, actorContext.getZoneOffset());
        assertEquals(targetVersion, commandContext.getTargetVersion());
    }

    @Nested
    @DisplayName("create command")
    class CreateCommand {

        @Test
        @DisplayName("with current time")
        void withTimestamp() {
            // We are creating a range of +/- second between the call to make sure the timestamp
            // would fit into this range. The purpose of this test is to make sure it works with
            // this precision and to add coverage.
            final Timestamp beforeCall = TimeTests.Past.secondsAgo(1);
            final Command command = factory().command()
                                             .create(StringValue.getDefaultInstance());
            final Timestamp afterCall = TimeTests.Future.secondsFromNow(1);

            assertTrue(Timestamps2.isBetween(
                    command.getContext()
                           .getActorContext()
                           .getTimestamp(), beforeCall, afterCall));
        }

        @Test
        @DisplayName("with given entity version")
        void withEntityVersion() {
            final Command command = factory().command()
                                             .create(StringValue.getDefaultInstance(), 2);

            assertEquals(2, command.getContext()
                                   .getTargetVersion());
        }

        @Test
        @DisplayName("with own tenant ID")
        void withOwnTenantId() {
            final TenantId tenantId = TenantId.newBuilder()
                                              .setValue(getClass().getSimpleName())
                                              .build();
            final ActorRequestFactory mtFactory = ActorRequestFactory.newBuilder()
                                                                     .setTenantId(tenantId)
                                                                     .setActor(getActor())
                                                                     .setZoneOffset(getZoneOffset())
                                                                     .build();
            final Command command = mtFactory.command()
                                             .create(StringValue.getDefaultInstance());

            assertEquals(tenantId, command.getContext()
                                          .getActorContext()
                                          .getTenantId());
        }
    }

    @Nested
    @DisplayName("throw ValidationException when creating command")
    class NotAccept {

        @Test
        @DisplayName("from invalid Message")
        void invalidMessage() {
            final RequiredFieldCommand invalidCommand = RequiredFieldCommand.getDefaultInstance();
            assertThrows(ValidationException.class, () -> factory().command()
                                                                   .create(invalidCommand));
        }

        @Test
        @DisplayName("from invalid Message with version")
        void invalidMessageWithVersion() {
            final RequiredFieldCommand invalidCommand = RequiredFieldCommand.getDefaultInstance();
            assertThrows(ValidationException.class, () -> factory().command()
                                                                   .create(invalidCommand, 42));
        }
    }
}

@@ -2097,38 +2097,32 @@
 ext() %7B%0A        
-final 
 TenantId tenantI
@@ -2158,22 +2158,16 @@
         
-final 
 UserId u
@@ -2205,22 +2205,16 @@
         
-final 
 ZoneOffs
@@ -2262,22 +2262,16 @@
         
-final 
 int targ
@@ -2295,30 +2295,24 @@
 0;%0A%0A        
-final 
 CommandConte
@@ -2330,16 +2330,32 @@
 ontext =
+%0A               
  Command
@@ -2389,253 +2389,28 @@
 tId,
-%0A                                                                           userId,%0A                                                                           zoneOffset,%0A                                                                          
+ userId, zoneOffset,
  tar
@@ -2427,30 +2427,24 @@
 );%0A%0A        
-final 
 ActorContext
@@ -3162,38 +3162,32 @@
 ge.%0A            
-final 
 Timestamp before
@@ -3227,38 +3227,32 @@
 1);%0A            
-final 
 Command command 
@@ -3304,38 +3304,32 @@
                 
-      
 .create(StringVa
@@ -3367,22 +3367,16 @@
         
-final 
 Timestam
@@ -3742,38 +3742,32 @@
 ) %7B%0A            
-final 
 Command command 
@@ -3780,38 +3780,32 @@
 ory().command()%0A
-      
                 
@@ -4093,22 +4093,16 @@
         
-final 
 TenantId
@@ -4171,30 +4171,24 @@
             
-      
 .setValue(ge
@@ -4245,38 +4245,32 @@
                 
-   
-   
 .build();%0A      
@@ -4275,22 +4275,16 @@
         
-final 
 ActorReq
@@ -4326,29 +4326,16 @@
 tFactory
-.newBuilder()
 %0A       
@@ -4351,37 +4351,22 @@
     
-                             
+.newBuilder()%0A
     
@@ -4428,229 +4428,82 @@
     
-                                                 .setActor(getActor())%0A                                                                     .setZoneOffset(getZoneOffset())%0A                                                 
+.setActor(getActor())%0A                    .setZoneOffset(getZoneOffset())%0A
     
@@ -4540,22 +4540,16 @@
         
-final 
 Command 
@@ -4570,38 +4570,32 @@
 ctory.command()%0A
-      
                 
@@ -5052,38 +5052,32 @@
 ) %7B%0A            
-final 
 RequiredFieldCom
@@ -5454,14 +5454,8 @@
     
-final 
 Requ

Improve code layout