package click.kamil.springstatemachineexporter.analysis;

import click.kamil.springstatemachineexporter.analysis.model.CallChain;
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
import click.kamil.springstatemachineexporter.analysis.service.HeuristicCallGraphEngine;
import click.kamil.springstatemachineexporter.analysis.service.GenericEventDetector;
import click.kamil.springstatemachineexporter.analysis.service.SpringMvcDetector;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

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

public class InheritedEventDetectionTest {

    @Test
    void testInheritedMessageBuilderEvent(@TempDir Path tempDir) throws IOException {
        Path srcDir = tempDir.resolve("src/main/java/com/example");
        Files.createDirectories(srcDir);

        // 1. Abstract Base Controller with protected method using MessageBuilder
        Files.writeString(srcDir.resolve("BaseController.java"),
                "package com.example;\n" +
                        "import org.springframework.messaging.support.MessageBuilder;\n" +
                        "import org.springframework.statemachine.StateMachine;\n" +
                        "public abstract class BaseController {\n" +
                        "    protected StateMachine<String, String> stateMachine;\n" +
                        "    \n" +
                        "    protected void send(String event) {\n" +
                        "        stateMachine.sendEvent(MessageBuilder.withPayload(event).build());\n" +
                        "    }\n" +
                        "}");

        // 2. Child Controller calling super.send()
        Files.writeString(srcDir.resolve("ChildController.java"),
                "package com.example;\n" +
                        "import org.springframework.web.bind.annotation.*;\n" +
                        "@RestController\n" +
                        "public class ChildController extends BaseController {\n" +
                        "    \n" +
                        "    @GetMapping(\"/trigger\")\n" +
                        "    public void trigger() {\n" +
                        "        super.send(\"MY_EVENT\");\n" +
                        "    }\n" +
                        "}");

        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        // Detect Entry Points
        SpringMvcDetector mvcDetector = new SpringMvcDetector(context, new ConstantResolver());
        TypeDeclaration childTd = context.getTypeDeclaration("com.example.ChildController");
        CompilationUnit childCu = (CompilationUnit) childTd.getRoot();
        List<EntryPoint> entryPoints = mvcDetector.detect(childCu);
        assertThat(entryPoints).hasSize(1);
        EntryPoint ep = entryPoints.get(0);

        // Detect Trigger Points
        GenericEventDetector eventDetector = new GenericEventDetector(context, new ConstantResolver(), List.of());
        TypeDeclaration baseTd = context.getTypeDeclaration("com.example.BaseController");
        CompilationUnit baseCu = (CompilationUnit) baseTd.getRoot();

        List<TriggerPoint> triggers = eventDetector.detect(baseCu);

        assertThat(triggers).describedAs("Should detect sendEvent in BaseController").hasSize(1);
        TriggerPoint tp = triggers.get(0);
        assertThat(tp.getEvent()).isEqualTo("event");

        // Build Call Chains
        HeuristicCallGraphEngine callGraphBuilder = new HeuristicCallGraphEngine(context);
        List<CallChain> chains = callGraphBuilder.findChains(entryPoints, triggers);

        assertThat(chains).describedAs("Should link ChildController.trigger to BaseController.send").hasSize(1);
        CallChain chain = chains.get(0);
        assertThat(chain.getMethodChain()).containsExactly(
                "com.example.ChildController.trigger",
                "com.example.BaseController.send"
        );
        assertThat(chain.getTriggerPoint().getEvent()).isEqualTo("MY_EVENT");
    }
}
