/*
 * Copyright 2018, TeamDev Ltd. 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.commandbus;

import com.google.protobuf.Message;
import io.spine.Identifier;
import io.spine.base.Error;
import io.spine.core.Command;
import io.spine.core.CommandClass;
import io.spine.core.CommandEnvelope;
import io.spine.core.CommandId;
import io.spine.core.CommandValidationError;
import io.spine.core.MessageInvalid;
import io.spine.type.TypeName;

import static java.lang.String.format;

/**
 * The exception for reporting duplicate commands.
 *
 * <p> A command is considered a duplicate when it’s ID matches the ID of a command already
 * dispatched.
 *
 * @author Mykhailo Drachuk
 */
public class DuplicateCommandException extends CommandException implements MessageInvalid {

    private static final long serialVersionUID = 0L;

    private DuplicateCommandException(String messageText, Command command, Error error) {
        super(messageText, command, error);
    }

    /**
     * Creates an exception for a duplicate command.
     */
    public static DuplicateCommandException of(Command command) {
        final CommandEnvelope envelope = CommandEnvelope.of(command);
        final Message commandMessage = envelope.getMessage();
        final String errMsg = errorMessage(envelope.getId(), commandMessage);
        final Error error = error(commandMessage, errMsg);
        return new DuplicateCommandException(errMsg, command, error);
    }

    /**
     * Creates a duplicate command error.
     */
    public static Error error(Message commandMessage, String errorText) {
        final Error.Builder error =
                Error.newBuilder()
                     .setType(DuplicateCommandException.class.getCanonicalName())
                     .setCode(CommandValidationError.DUPLICATE.getNumber())
                     .setMessage(errorText)
                     .putAllAttributes(commandTypeAttribute(commandMessage));
        return error.build();
    }

    /**
     * Generates a formatted duplicate command error message. 
     */
    private static String errorMessage(CommandId id, Message commandMessage) {
        return format(
                "The command (class: `%s`, type: `%s`, id: `%s`) was already handled by " +
                "a Command Bus.",
                CommandClass.of(commandMessage)
                            .value()
                            .getName(),
                TypeName.of(commandMessage),
                Identifier.toString(id));
    }

}

@@ -1201,83 +1201,16 @@
 mand
-Class;%0Aimport io.spine.core.CommandEnvelope;%0Aimport io.spine.core.CommandId
+Envelope
 ;%0Aim
@@ -1372,54 +1372,60 @@
 %0A * 
-The exception for reporting duplicate commands
+Reports a command which was delivered more than once
 .%0A *
@@ -1479,9 +1479,8 @@
 n it
-%E2%80%99
 s ID
@@ -2176,32 +2176,8 @@
 lope
-.getId(), commandMessage
 );%0A 
@@ -2900,17 +2900,16 @@
 message.
- 
 %0A     */
@@ -2959,36 +2959,24 @@
 mand
-Id id, Message commandMessag
+Envelope envelop
 e) %7B
@@ -3107,16 +3107,24 @@
         
+        
 %22a Comma
@@ -3153,44 +3153,35 @@
     
-CommandClass.of(commandMessage)%0A    
+envelope.getMessageClass()%0A
     
@@ -3209,20 +3209,16 @@
 value()%0A
-    
         
@@ -3273,30 +3273,37 @@
 Name.of(
-command
+envelope.get
 Message
+()
 ),%0A     
@@ -3333,18 +3333,32 @@
 oString(
-id
+envelope.getId()
 ));%0A    

Refactor DuplicateCommandException.