package click.kamil.springstatemachineexporter.analysis.enricher;

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

import java.util.List;

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

class CallChainLinkPolicyTest {

    @Test
    void shouldFailClosedOnEnumSetAmbiguousWiden() {
        TriggerPoint trigger = TriggerPoint.builder()
                .ambiguous(true)
                .event("ENUM_SET:com.example.OrderEvent.PAY,com.example.OrderEvent.SHIP")
                .polymorphicEvents(List.of("com.example.OrderEvent.PAY", "com.example.OrderEvent.SHIP"))
                .build();

        assertThat(CallChainLinkPolicy.shouldFailClosedOnAmbiguousCallGraphWiden(trigger)).isTrue();
        assertThat(CallChainLinkPolicy.resolveLinkResolution(trigger, List.of(), false))
                .isEqualTo(LinkResolution.AMBIGUOUS_WIDEN);
    }

    @Test
    void shouldMarkExternalTriggersUnresolved() {
        TriggerPoint trigger = TriggerPoint.builder()
                .external(true)
                .event("eventString")
                .build();

        assertThat(CallChainLinkPolicy.resolveLinkResolution(trigger, List.of(), false))
                .isEqualTo(LinkResolution.UNRESOLVED_EXTERNAL);
    }

    @Test
    void shouldFailClosedOnValueOfAmbiguousWiden() {
        TriggerPoint trigger = TriggerPoint.builder()
                .ambiguous(true)
                .event("OrderEvent.valueOf(eventStr)")
                .polymorphicEvents(List.of("a.OrderEvent.PAY", "a.OrderEvent.SHIP"))
                .build();

        assertThat(CallChainLinkPolicy.shouldFailClosedOnAmbiguousCallGraphWiden(trigger)).isTrue();
        assertThat(CallChainLinkPolicy.resolveLinkResolution(trigger, List.of(), false))
                .isEqualTo(LinkResolution.AMBIGUOUS_WIDEN);
    }

    @Test
    void shouldAllowTrustedMachineScopedValueOfWiden() {
        TriggerPoint trigger = TriggerPoint.builder()
                .ambiguous(true)
                .eventTypeFqn("com.example.OrderEvent")
                .event("OrderEvent.valueOf(eventStr)")
                .polymorphicEvents(List.of("com.example.OrderEvent.PAY", "com.example.OrderEvent.SHIP"))
                .build();

        assertThat(CallChainLinkPolicy.isTrustedEnumPolymorphicWiden(trigger)).isTrue();
        assertThat(CallChainLinkPolicy.shouldFailClosedOnAmbiguousCallGraphWiden(trigger)).isFalse();
    }

    @Test
    void shouldAllowTrustedWidenUsingMachineEventTypeWhenTriggerTypeIsNull() {
        TriggerPoint trigger = TriggerPoint.builder()
                .ambiguous(true)
                .event("OrderEvent.valueOf(eventStr)")
                .polymorphicEvents(List.of("com.example.OrderEvent.PAY", "com.example.OrderEvent.SHIP"))
                .build();

        assertThat(CallChainLinkPolicy.isTrustedEnumPolymorphicWiden(
                trigger, "com.example.OrderEvent")).isTrue();
        assertThat(CallChainLinkPolicy.shouldFailClosedOnAmbiguousCallGraphWiden(
                trigger, "com.example.OrderEvent")).isFalse();
    }

    @Test
    void shouldResolveLinkWhenTrustedWidenPassesUsingMachineEventType() {
        TriggerPoint trigger = TriggerPoint.builder()
                .ambiguous(false)
                .event("OrderEvent.valueOf(eventStr)")
                .polymorphicEvents(List.of("com.example.OrderEvent.PAY", "com.example.OrderEvent.SHIP"))
                .build();

        assertThat(CallChainLinkPolicy.resolveLinkResolution(
                trigger,
                List.of(),
                false,
                "com.example.OrderEvent")).isEqualTo(LinkResolution.NO_MATCH);
        assertThat(CallChainLinkPolicy.shouldFailClosedOnAmbiguousCallGraphWiden(
                trigger, "com.example.OrderEvent")).isFalse();
    }

    @Test
    void shouldNotTrustImportStylePolymorphicWidenWithoutPackage() {
        TriggerPoint trigger = TriggerPoint.builder()
                .ambiguous(true)
                .event("OrderEvent.valueOf(eventStr)")
                .polymorphicEvents(List.of("OrderEvent.PAY", "OrderEvent.SHIP"))
                .build();

        assertThat(CallChainLinkPolicy.isTrustedEnumPolymorphicWiden(trigger)).isFalse();
        assertThat(CallChainLinkPolicy.shouldFailClosedOnAmbiguousCallGraphWiden(trigger)).isTrue();
    }
}
