package click.kamil.springstatemachineexporter.analysis.service;

import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

class EventCarrierPolicyTest {

    @Test
    void carrierNamesIncludeMessagingAndEventConventions() {
        assertThat(EventCarrierPolicy.isCarrierParamName("message")).isTrue();
        assertThat(EventCarrierPolicy.isCarrierParamName("payload")).isTrue();
        assertThat(EventCarrierPolicy.isCarrierParamName("commandKey")).isTrue();
        assertThat(EventCarrierPolicy.isCarrierParamName("orderEvent")).isTrue();
        assertThat(EventCarrierPolicy.isCarrierParamName("orderId")).isFalse();
        assertThat(EventCarrierPolicy.isCarrierParamName("cmd")).isFalse();
    }

    @Test
    void dynamicEventTokensMatchBareUnresolvedTriggers() {
        assertThat(EventCarrierPolicy.isDynamicEventToken("event")).isTrue();
        assertThat(EventCarrierPolicy.isDynamicEventToken("message")).isTrue();
        assertThat(EventCarrierPolicy.isDynamicEventToken("payload")).isTrue();
        assertThat(EventCarrierPolicy.isDynamicEventToken("OrderEvent.PAY")).isFalse();
        assertThat(EventCarrierPolicy.isDynamicEventToken("cmd")).isFalse();
    }

    @Test
    void machineDiscriminatorsAreRoutingButNotCarriers() {
        assertThat(EventCarrierPolicy.isMachineDiscriminatorName("machineType")).isTrue();
        assertThat(EventCarrierPolicy.isMachineDiscriminatorName("domain")).isTrue();
        assertThat(EventCarrierPolicy.isCarrierParamName("machineType")).isFalse();
        assertThat(EventCarrierPolicy.isRoutingDiscriminantName("machineType")).isTrue();
        assertThat(EventCarrierPolicy.isRoutingDiscriminantName("message")).isTrue();
    }

    @Test
    void stateAccessorNamesArePositiveEvidence() {
        assertThat(EventCarrierPolicy.isStateAccessorName("state")).isTrue();
        assertThat(EventCarrierPolicy.isStateAccessorName("currentState")).isTrue();
        assertThat(EventCarrierPolicy.isStateAccessorName("message")).isFalse();
    }

    @Test
    void stateAccessorMethodNamesIncludeGetStateAndGetId() {
        assertThat(EventCarrierPolicy.isStateAccessorMethodName("getState")).isTrue();
        assertThat(EventCarrierPolicy.isStateAccessorMethodName("getId")).isTrue();
        assertThat(EventCarrierPolicy.isStateAccessorMethodName("currentState")).isTrue();
        assertThat(EventCarrierPolicy.isStateAccessorMethodName("message")).isFalse();
    }

    @Test
    void payloadAnnotationMakesMessagingCarrierRegardlessOfName() {
        EntryPoint entryPoint = EntryPoint.builder()
                .type(EntryPoint.Type.JMS)
                .name("JMS: q")
                .className("com.example.L")
                .methodName("onMessage")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("cmd")
                        .type("String")
                        .annotations(List.of("Payload"))
                        .build()))
                .build();

        assertThat(EventCarrierPolicy.isMessagingPayloadParameter(
                entryPoint, entryPoint.getParameters().get(0))).isTrue();
        assertThat(EventCarrierPolicy.isCarrierParameter(
                entryPoint, entryPoint.getParameters().get(0))).isTrue();
    }

    @Test
    void soleUnannotatedStringMessagingBodyIsCarrierEvenWhenNamedCmd() {
        EntryPoint entryPoint = EntryPoint.builder()
                .type(EntryPoint.Type.JMS)
                .name("JMS: q")
                .className("com.example.L")
                .methodName("onMessage")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("cmd")
                        .type("String")
                        .annotations(List.of())
                        .build()))
                .build();

        assertThat(EventCarrierPolicy.isSoleUnannotatedStringMessagingBody(
                entryPoint, entryPoint.getParameters().get(0))).isTrue();
        assertThat(EventCarrierPolicy.isMessagingPayloadParameter(
                entryPoint, entryPoint.getParameters().get(0))).isTrue();
    }

    @Test
    void twoUnannotatedStringBodiesAreNotAutoCarriersBySoleBodyRule() {
        EntryPoint entryPoint = EntryPoint.builder()
                .type(EntryPoint.Type.JMS)
                .name("JMS: q")
                .className("com.example.L")
                .methodName("onMessage")
                .parameters(List.of(
                        EntryPoint.Parameter.builder()
                                .name("cmd")
                                .type("String")
                                .annotations(List.of())
                                .build(),
                        EntryPoint.Parameter.builder()
                                .name("extra")
                                .type("String")
                                .annotations(List.of())
                                .build()))
                .build();

        assertThat(EventCarrierPolicy.isSoleUnannotatedStringMessagingBody(
                entryPoint, entryPoint.getParameters().get(0))).isFalse();
        assertThat(EventCarrierPolicy.isMessagingPayloadParameter(
                entryPoint, entryPoint.getParameters().get(0))).isFalse();
        assertThat(EventCarrierPolicy.isMessagingPayloadParameter(
                entryPoint, entryPoint.getParameters().get(1))).isFalse();
    }

    @Test
    void unannotatedMessagingStringUsesCarrierNameConvention() {
        EntryPoint entryPoint = EntryPoint.builder()
                .type(EntryPoint.Type.JMS)
                .name("JMS: q")
                .className("com.example.L")
                .methodName("onMessage")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("message")
                        .type("String")
                        .annotations(List.of())
                        .build()))
                .build();

        assertThat(EventCarrierPolicy.isMessagingPayloadParameter(
                entryPoint, entryPoint.getParameters().get(0))).isTrue();
    }
}
