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.*;

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

@RequiredArgsConstructor
public class MessagingDetector {
    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) {
                processMethod(node, entryPoints);
                return super.visit(node);
            }
        });
        return entryPoints;
    }

    private void processMethod(MethodDeclaration method, List<EntryPoint> entryPoints) {
        for (Object modifier : method.modifiers()) {
            if (modifier instanceof Annotation annotation) {
                String typeName = annotation.getTypeName().getFullyQualifiedName();
                
                EntryPoint.Type type = null;
                String destination = "";
                
                if (typeName.endsWith("JmsListener")) {
                    type = EntryPoint.Type.JMS;
                    destination = resolveValue(annotation, "destination");
                } else if (typeName.endsWith("RabbitListener")) {
                    type = EntryPoint.Type.RABBIT;
                    destination = resolveValue(annotation, "queues");
                    if ("unknown".equals(destination)) {
                        destination = resolveValue(annotation, "queue");
                    }
                    if ("unknown".equals(destination)) {
                        destination = resolveValue(annotation, "bindings");
                    }
                } else if (typeName.endsWith("SqsListener")) {
                    type = EntryPoint.Type.SQS;
                    destination = resolveValue(annotation, "value");
                    if ("unknown".equals(destination)) {
                        destination = resolveValue(annotation, "queueNames");
                    }
                } else if (typeName.endsWith("SnsListener")) {
                    type = EntryPoint.Type.SNS;
                    destination = resolveValue(annotation, "value");
                    if ("unknown".equals(destination)) {
                        destination = resolveValue(annotation, "topicNames");
                    }
                } else if (typeName.endsWith("KafkaListener")) {
                    type = EntryPoint.Type.KAFKA;
                    destination = resolveValue(annotation, "topics");
                    if ("unknown".equals(destination)) {
                        destination = resolveValue(annotation, "topicPattern");
                    }
                }

                if (type != null) {
                    Map<String, String> metadata = new HashMap<>();
                    metadata.put("destination", destination);
                    
                    String protocolName = type.name();
                    if (typeName.endsWith("SqsListener")) protocolName = "SQS";
                    if (typeName.endsWith("SnsListener")) protocolName = "SNS";
                    metadata.put("protocol", protocolName);
                    
                    entryPoints.add(EntryPoint.builder()
                            .type(type)
                            .name(protocolName + ": " + destination)
                            .className(context.getFqn((TypeDeclaration) method.getParent()))
                            .methodName(method.getName().getIdentifier())
                            .sourceFile(context.getRelativePath(context.getFqn((TypeDeclaration) method.getParent())))
                            .metadata(metadata)
                            .parameters(extractParameters(method))
                            .build());
                }
            }
        }
    }

    private String resolveValue(Annotation annotation, String memberName) {
        Expression value = null;
        if (annotation instanceof SingleMemberAnnotation sma) {
            value = sma.getValue();
        } else if (annotation instanceof NormalAnnotation na) {
            for (Object pairObj : na.values()) {
                MemberValuePair pair = (MemberValuePair) pairObj;
                if (memberName.equals(pair.getName().getIdentifier())) {
                    value = pair.getValue();
                    break;
                }
            }
        }

        if (value != null) {
            if (value instanceof ArrayInitializer ai) {
                List<String> values = new ArrayList<>();
                for (Object o : ai.expressions()) {
                    values.add(context.resolveExpression((Expression) o));
                }
                return String.join(", ", values);
            }
            return context.resolveExpression(value);
        }
        return "unknown";
    }

    private List<EntryPoint.Parameter> extractParameters(MethodDeclaration method) {
        List<EntryPoint.Parameter> parameters = new ArrayList<>();
        for (Object paramObj : method.parameters()) {
            if (paramObj instanceof SingleVariableDeclaration param) {
                List<String> annotations = new ArrayList<>();
                for (Object mod : param.modifiers()) {
                    if (mod instanceof Annotation ann) {
                        String name = ann.getTypeName().getFullyQualifiedName();
                        int lastDot = name.lastIndexOf('.');
                        if (lastDot >= 0) {
                            name = name.substring(lastDot + 1);
                        }
                        annotations.add(name);
                    }
                }
                parameters.add(EntryPoint.Parameter.builder()
                        .name(param.getName().getIdentifier())
                        .type(param.getType().toString())
                        .annotations(annotations)
                        .build());
            }
        }
        return parameters;
    }
}
