package click.kamil.springstatemachineexporter.analysis.service;

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.ast.common.CodebaseContext;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jdt.core.dom.CompilationUnit;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public class JdtIntelligenceProvider implements CodebaseIntelligenceProvider {
    private final CodebaseContext context;
    private final Path rootDir;
    private final ConstantResolver constantResolver = new ConstantResolver();
    private final GenericEventDetector eventDetector;
    private final SpringMvcDetector mvcDetector;
    private final MessagingDetector messagingDetector;
    private final CallGraphBuilder callGraphBuilder;
    private final LifecycleDetector lifecycleDetector;
    private final InterceptorDetector interceptorDetector;

    public JdtIntelligenceProvider(CodebaseContext context, Path rootDir) {
        this.context = context;
        this.rootDir = rootDir;
        this.eventDetector = new GenericEventDetector(context, constantResolver, context.getLibraryHints());
        this.mvcDetector = new SpringMvcDetector(context, constantResolver);
        this.messagingDetector = new MessagingDetector(context);
        this.callGraphBuilder = new CallGraphBuilder(context);
        this.lifecycleDetector = new LifecycleDetector(context);
        this.interceptorDetector = new InterceptorDetector(context);
    }

    @Override
    public List<TriggerPoint> findTriggerPoints() {
        List<TriggerPoint> allTriggers = new ArrayList<>();
        var cus = context.getCompilationUnits();
        log.info("JdtIntelligenceProvider scanning {} compilation units for triggers", cus.size());
        for (CompilationUnit cu : cus) {
            allTriggers.addAll(eventDetector.detect(cu));
            allTriggers.addAll(lifecycleDetector.detect(cu));
        }
        log.info("Found {} triggers (including lifecycle) in total", allTriggers.size());
        return allTriggers;
    }

    @Override
    public List<EntryPoint> findEntryPoints() {
        List<EntryPoint> allEntryPoints = new ArrayList<>();
        var cus = context.getCompilationUnits();
        log.info("JdtIntelligenceProvider scanning {} compilation units for entry points", cus.size());
        for (CompilationUnit cu : cus) {
            allEntryPoints.addAll(mvcDetector.detect(cu));
            allEntryPoints.addAll(messagingDetector.detect(cu));
            allEntryPoints.addAll(interceptorDetector.detect(cu));
        }
        log.info("Found {} entry points (including interceptors and listeners) in total", allEntryPoints.size());
        return allEntryPoints;
    }

    @Override
    public List<CallChain> findCallChains(List<EntryPoint> entryPoints, List<TriggerPoint> triggers) {
        log.info("JdtIntelligenceProvider searching for call chains between {} entry points and {} triggers", entryPoints.size(), triggers.size());
        return callGraphBuilder.findChains(entryPoints, triggers);
    }

    @Override
    public Map<String, Map<String, String>> resolveProperties() {
        return context.getProperties();
    }
}
