package click.kamil.springstatemachineexporter.analysis.service;

import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import lombok.RequiredArgsConstructor;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RequiredArgsConstructor
public class SpringComponentDetector {
    private final CodebaseContext context;

    public List<EntryPoint> detect(CompilationUnit cu) {
        List<EntryPoint> entryPoints = new ArrayList<>();
        cu.accept(new ASTVisitor() {
            @Override
            public boolean visit(MethodDeclaration node) {
                if (!(node.getParent() instanceof TypeDeclaration)) {
                    return super.visit(node);
                }
                TypeDeclaration parentTd = (TypeDeclaration) node.getParent();

                for (Object modifier : node.modifiers()) {
                    if (modifier instanceof Annotation annotation) {
                        String typeName = annotation.getTypeName().getFullyQualifiedName();
                        if (typeName.endsWith("Scheduled")) {
                            Map<String, String> meta = new HashMap<>();
                            String cron = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractAnnotationMember(annotation, "cron");
                            if (!cron.isEmpty())
                                meta.put("cron", cron);
                            String fixedRate = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractAnnotationMember(annotation, "fixedRate");
                            if (!fixedRate.isEmpty())
                                meta.put("fixedRate", fixedRate);
                            String fixedDelay = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractAnnotationMember(annotation, "fixedDelay");
                            if (!fixedDelay.isEmpty())
                                meta.put("fixedDelay", fixedDelay);

                            entryPoints.add(EntryPoint.builder()
                                    .type(EntryPoint.Type.CUSTOM)
                                    .name("@Scheduled: " + node.getName().getIdentifier())
                                    .className(context.getFqn(parentTd))
                                    .methodName(node.getName().getIdentifier())
                                    .sourceFile(context.getRelativePath(context.getFqn(parentTd)))
                                    .metadata(meta)
                                    .build());
                        } else if (typeName.endsWith("EventListener")) {
                            Map<String, String> meta = new HashMap<>();
                            String classes = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractAnnotationMember(annotation, "classes");
                            if (!classes.isEmpty())
                                meta.put("classes", classes);
                            String condition = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractAnnotationMember(annotation, "condition");
                            if (!condition.isEmpty())
                                meta.put("condition", condition);

                            entryPoints.add(EntryPoint.builder()
                                    .type(EntryPoint.Type.CUSTOM)
                                    .name("@EventListener: " + node.getName().getIdentifier())
                                    .className(context.getFqn(parentTd))
                                    .methodName(node.getName().getIdentifier())
                                    .sourceFile(context.getRelativePath(context.getFqn(parentTd)))
                                    .metadata(meta)
                                    .build());
                        } else if (typeName.endsWith("Around") || typeName.endsWith("Before") || typeName.endsWith("After") || typeName.endsWith("AfterReturning") || typeName.endsWith("AfterThrowing")) {
                            Map<String, String> meta = new HashMap<>();
                            String value = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractAnnotationMember(annotation, "value");
                            if (!value.isEmpty())
                                meta.put("pointcut", value);
                            else {
                                String pointcut = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractAnnotationMember(annotation, "pointcut");
                                if (!pointcut.isEmpty())
                                    meta.put("pointcut", pointcut);
                            }

                            entryPoints.add(EntryPoint.builder()
                                    .type(EntryPoint.Type.CUSTOM)
                                    .name("@" + annotation.getTypeName().getFullyQualifiedName() + ": " + node.getName().getIdentifier())
                                    .className(context.getFqn(parentTd))
                                    .methodName(node.getName().getIdentifier())
                                    .sourceFile(context.getRelativePath(context.getFqn(parentTd)))
                                    .metadata(meta)
                                    .build());
                        }
                    }
                }
                return super.visit(node);
            }
        });
        return entryPoints;
    }
}
