package org.requirementsascode;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

import org.junit.Before;
import org.junit.Test;
import org.requirementsascode.systemreaction.IgnoresIt;

public class NonStandardEventHandlingTest extends AbstractTestCase {
	private String stepName;
	private Optional<? extends Object> optionalCondition;
	private Optional<? extends Object> optionalEvent;
	private Object systemReaction;

	@Before
	public void setup() {
		setupWithRecordingModelRunner();
	}

	@Test
	public void recordsAutonomousSystemReactionStep() {
		stepName = "";

		ReactionAsRunnable reactionAsRunnable = new ReactionAsRunnable();
		Model model = modelBuilder
			.useCase(USE_CASE)
				.basicFlow()
					.step(SYSTEM_DISPLAYS_TEXT).system(reactionAsRunnable)
			.build();
	
		modelRunner.handleWith(recordStepDetails());
		modelRunner.run(model);

		assertRecordedStepNames(SYSTEM_DISPLAYS_TEXT);
		assertEquals(SYSTEM_DISPLAYS_TEXT, stepName);
		assertFalse(optionalEvent.isPresent());
		assertFalse(optionalCondition.isPresent());
		assertEquals(reactionAsRunnable, systemReaction);
	}

	private class ReactionAsRunnable implements Runnable {
		@Override
		public void run() {
		}
	}

	private Consumer<StepToBeRun> recordStepDetails() {
		return stepToBeRun -> {
			stepName = stepToBeRun.getStepName();
			optionalCondition = stepToBeRun.getCondition();
			optionalEvent = stepToBeRun.getMessage();
			systemReaction = stepToBeRun.getSystemReaction();
			stepToBeRun.run();
		};
	}

	@Test
	public void recordsStepWithEvent() {
		ReactionAsConsumer reactionAsConsumer = new ReactionAsConsumer();
		Model model = modelBuilder.useCase(USE_CASE)
			.condition(new AlwaysTrue()).on(EntersText.class).system(reactionAsConsumer)
		.build();
	
		modelRunner.handleWith(recordStepDetails());
		modelRunner.run(model);
		modelRunner.reactTo(entersText());

		Object condition = optionalCondition.get();
		assertTrue(condition instanceof AlwaysTrue);

		Object event = optionalEvent.get();
		assertTrue(event instanceof EntersText);

		assertEquals(reactionAsConsumer, systemReaction);
	}

	private class AlwaysTrue implements Condition {
		@Override
		public boolean evaluate() {
			return true;
		}
	}

	private class ReactionAsConsumer implements Consumer<EntersText> {
		@Override
		public void accept(EntersText t) {
		}
	}

	@Test
	public void recordsEventWithUnhandledEventHandler() {
		Model model = modelBuilder
			.useCase(USE_CASE)
				.on(EntersText.class).system(displaysEnteredText())
		.build();

		modelRunner.handleUnhandledWith(this::eventRecordingEventHandler).run(model).reactTo(entersNumber());

		Object event = optionalEvent.get();
		assertTrue(event instanceof EntersNumber);
	}
	
	private void eventRecordingEventHandler(Object event) {
		this.optionalEvent = Optional.of(event);
	}

	@Test
	public void publishesEventToList() {
		List<Object> publishedEvents = new ArrayList<>();
		
		Model model = modelBuilder
			.useCase(USE_CASE)
				.basicFlow()
					.step("S1").on(EntersText.class).systemPublish(super.publishesEnteredTextAsEvent())
					.step("S2").on(String.class).system(new IgnoresIt<>())
				.build();
	
		modelRunner.publishWith(event -> publishedEvents.add(event));
		modelRunner.run(model).reactTo(entersText(), "Some String");

		assertEquals(1, publishedEvents.size());
		assertEquals(EntersText.class, publishedEvents.get(0).getClass());
		assertEquals("S2", modelRunner.getLatestStep().get().getName());
	}
}

@@ -3040,19 +3040,13 @@
 vent
-ToList
 () %7B%0A
+
 %09%09Li
@@ -3496,24 +3496,24 @@
 ts.size());%0A
-
 %09%09assertEqua
@@ -3639,10 +3639,793 @@
 ());%0A%09%7D%0A
+%09%0A%09@Test%0A%09public void publishesEventToActor() %7B%0A%09%09List%3CObject%3E actualRecipients = new ArrayList%3C%3E();%0A%09%09Actor recipient = new Actor(%22Recipient%22);%0A%09%09%0A%09%09Model model = modelBuilder%0A%09%09%09.useCase(USE_CASE)%0A%09%09%09%09.basicFlow()%0A%09%09%09%09%09.step(%22S1%22).on(EntersText.class).systemPublish(super.publishesEnteredTextAsEvent()).to(recipient)%0A%09%09%09%09%09.step(%22S2%22).on(String.class).system(new IgnoresIt%3C%3E())%0A%09%09%09%09.build();%0A%0A%09%09modelRunner.publishWith(event -%3E %7B%0A%09%09%09Step step = modelRunner.getLatestStep().get();%0A%09%09%09step.getPublishTo().ifPresent(to -%3E actualRecipients.add(to));%0A%09%09%7D);%0A%09%09modelRunner.run(model).reactTo(entersText(), %22Some String%22);%0A%0A%09%09assertEquals(1, actualRecipients.size());%0A%09%09assertEquals(recipient, actualRecipients.get(0));%0A%09%09assertEquals(%22S2%22, modelRunner.getLatestStep().get().getName());%0A%09%7D%0A
 %7D%0A

Add test for publishing to specific recipient