package org.axonframework.modelling.command.inspection;

import org.axonframework.eventhandling.EventHandler;
import org.axonframework.eventhandling.EventMessage;
import org.axonframework.eventhandling.GenericEventMessage;
import org.axonframework.modelling.command.AggregateMember;
import org.junit.jupiter.api.*;

import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Test class validating an annotated aggregate model with {@link AggregateMember}s on the root level of a polymorphic
 * aggregate behaves as desired when a meta model is created with the {@link AnnotatedAggregateMetaModelFactory}.
 *
 * @author Steven van Beelen
 */
class AnnotatedRootMemberAggregateMetaModelFactoryTest {

    private static final AtomicInteger aggregateEventCounter = new AtomicInteger(0);
    private static final AtomicInteger memberEventCounter = new AtomicInteger(0);

    /**
     * The hierarchy of the Aggregate, is as follows:
     * <p>
     * +--------------+
     * |Root Aggregate|
     * |   +------+   |
     * |   |Member|   |
     * |   +------+   |
     * +--------------+
     *        v
     * +------+-------+
     * |Node Aggregate|
     * +------+-------+
     *        v
     * +------+-------+
     * |Leaf Aggregate|
     * +--------------+
     * <p>
     * On all levels an AggregateEvent handler is present. Only the Member has the MemberEvent handler. In such a set up
     * we would assume the AggregateEvent handler to be invoked once in the root (which encompasses the root, node and
     * leaf aggregate) and once in the member. Furthermore we would anticipate the MemberEvent handler to be invoked
     * once too, since there only is a single occurrence of the member in the entire set up.
     */
    @Test
    void testCreateAggregateModelDoesNotDuplicateRootLevelAggregateMembers() {
        int expectedNumberOfAggregateEventHandlerInvocations = 2;
        int expectedNumberOfMemberEventHandlerInvocations = 1;

        EventMessage<AggregateEvent> testAggregateEvent = GenericEventMessage.asEventMessage(new AggregateEvent());
        EventMessage<MemberEvent> testMemberEvent = GenericEventMessage.asEventMessage(new MemberEvent());
        LeafAggregate testModel = new LeafAggregate();

        AggregateModel<LeafAggregate> testSubject =
                AnnotatedAggregateMetaModelFactory.inspectAggregate(LeafAggregate.class);

        testSubject.publish(testAggregateEvent, testModel);
        assertEquals(expectedNumberOfAggregateEventHandlerInvocations, aggregateEventCounter.get());
        testSubject.publish(testMemberEvent, testModel);
        assertEquals(expectedNumberOfMemberEventHandlerInvocations, memberEventCounter.get());
    }

    @SuppressWarnings("unused")
    private abstract static class RootAggregate {

        @AggregateMember
        private final Member member = new Member();

        @EventHandler
        public void on(AggregateEvent event) {
            aggregateEventCounter.incrementAndGet();
        }
    }

    private abstract static class NodeAggregate extends RootAggregate {

        @EventHandler
        public void on(AggregateEvent event) {
            aggregateEventCounter.incrementAndGet();
        }
    }

    private static class LeafAggregate extends NodeAggregate {

        @EventHandler
        public void on(AggregateEvent event) {
            aggregateEventCounter.incrementAndGet();
        }
    }

    @SuppressWarnings("unused")
    private static class Member {

        @EventHandler
        public void on(AggregateEvent event) {
            aggregateEventCounter.incrementAndGet();
        }

        @EventHandler
        public void on(MemberEvent event) {
            memberEventCounter.incrementAndGet();
        }
    }

    private static class AggregateEvent {

    }

    private static class MemberEvent {

    }
}

@@ -308,16 +308,86 @@
 .api.*;%0A
+import org.mockito.internal.util.collections.*;%0A%0Aimport java.util.Set;
 %0Aimport 
@@ -1076,23 +1076,16 @@
 -------+
-%0A     *
  %7CRoot A
@@ -1093,23 +1093,16 @@
 gregate%7C
-%0A     *
  %7C   +--
@@ -1106,31 +1106,24 @@
 +------+   %7C
-%0A     *
  %7C   %7CMember
@@ -1127,23 +1127,16 @@
 ber%7C   %7C
-%0A     *
  %7C   +--
@@ -1144,23 +1144,16 @@
 ---+   %7C
-%0A     *
  +------
@@ -1157,38 +1157,24 @@
 -----------+
-%0A     *       
  v%0A     * +-
@@ -1187,23 +1187,16 @@
 -------+
-%0A     *
  %7CNode A
@@ -1196,39 +1196,32 @@
 %7CNode Aggregate%7C
-%0A     *
  +------+-------
@@ -1225,31 +1225,10 @@
 ---+
-%0A     *        v%0A     *
+ v
  +--
@@ -1240,23 +1240,16 @@
 -------+
-%0A     *
  %7CLeaf A
@@ -1257,23 +1257,16 @@
 gregate%7C
-%0A     *
  +------
@@ -2697,32 +2697,1175 @@
 .get());%0A    %7D%0A%0A
+    @Test%0A    void testCreateAggregateModelDoesNotDuplicateRootLevelAggregateMembersForPolymorphicAggregates() %7B%0A        int expectedNumberOfAggregateEventHandlerInvocations = 2;%0A        int expectedNumberOfMemberEventHandlerInvocations = 1;%0A%0A        EventMessage%3CAggregateEvent%3E testAggregateEvent = GenericEventMessage.asEventMessage(new AggregateEvent());%0A        EventMessage%3CMemberEvent%3E testMemberEvent = GenericEventMessage.asEventMessage(new MemberEvent());%0A        LeafAggregate testModel = new LeafAggregate();%0A%0A        //noinspection unchecked%0A        Set%3CClass%3C? extends RootAggregate%3E%3E subtypes = Sets.newSet(LeafAggregate.class, OtherLeafAggregate.class);%0A        AggregateModel%3CRootAggregate%3E testSubject =%0A                AnnotatedAggregateMetaModelFactory.inspectAggregate(RootAggregate.class, subtypes);%0A%0A        testSubject.publish(testAggregateEvent, testModel);%0A        assertEquals(expectedNumberOfAggregateEventHandlerInvocations, aggregateEventCounter.get());%0A        testSubject.publish(testMemberEvent, testModel);%0A        assertEquals(expectedNumberOfMemberEventHandlerInvocations, memberEventCounter.get());%0A    %7D%0A%0A
     @SuppressWar
@@ -4555,32 +4555,240 @@
        %7D%0A    %7D%0A%0A
+    private static class OtherLeafAggregate extends NodeAggregate %7B%0A%0A        @EventHandler%0A        public void on(AggregateEvent event) %7B%0A            aggregateEventCounter.incrementAndGet();%0A        %7D%0A    %7D%0A%0A
     @SuppressWar

Introduce polymorphic test case