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 org.mockito.internal.util.collections.*;

import java.util.Set;
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());
    }

    @Test
    void testCreateAggregateModelDoesNotDuplicateRootLevelAggregateMembersForPolymorphicAggregates() {
        int expectedNumberOfAggregateEventHandlerInvocations = 2;
        int expectedNumberOfMemberEventHandlerInvocations = 1;

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

        //noinspection unchecked
        Set<Class<? extends RootAggregate>> subtypes = Sets.newSet(LeafAggregate.class, OtherLeafAggregate.class);
        AggregateModel<RootAggregate> testSubject =
                AnnotatedAggregateMetaModelFactory.inspectAggregate(RootAggregate.class, subtypes);

        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();
        }
    }

    private static class OtherLeafAggregate 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 {

    }
}

@@ -1055,24 +1055,33 @@
 * %3Cp%3E%0A     *
+         
  +----------
@@ -1085,16 +1085,32 @@
 -------+
+%0A     *         
  %7CRoot A
@@ -1118,16 +1118,32 @@
 gregate%7C
+%0A     *         
  %7C   +--
@@ -1147,24 +1147,40 @@
 +------+   %7C
+%0A     *         
  %7C   %7CMember
@@ -1184,16 +1184,32 @@
 ber%7C   %7C
+%0A     *         
  %7C   +--
@@ -1217,16 +1217,32 @@
 ---+   %7C
+%0A     *         
  +------
@@ -1250,16 +1250,39 @@
 -------+
+%0A     *                
  v%0A     
@@ -1282,16 +1282,25 @@
 v%0A     *
+         
  +------
@@ -1308,16 +1308,32 @@
 -------+
+%0A     *         
  %7CNode A
@@ -1333,32 +1333,48 @@
 %7CNode Aggregate%7C
+%0A     *         
  +------+-------
@@ -1378,46 +1378,167 @@
 ---+
- v +------+-------+ %7CLeaf Aggregate%7C +
+%0A     *          v              v%0A     * +--------+-----+ +------+-------------+%0A     * %7CLeaf Aggregate%7C %7COther Leaf Aggregate%7C%0A     * +--------------+ +------
 ----

Correct javadoc