package click.kamil.springstatemachineexporter.analysis.enricher.matching;

import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import click.kamil.springstatemachineexporter.model.Event;
import java.util.List;

public class StrictFqnMatchingEngine implements EventMatchingEngine {

    @Override
    public boolean matches(Event stateMachineEvent, TriggerPoint triggerPoint) {
        if (stateMachineEvent == null || triggerPoint == null || triggerPoint.getEvent() == null) {
            return false;
        }

        if (triggerPoint.isExternal()) {
            return true;
        }

        String smEventRaw = stateMachineEvent.fullIdentifier() != null ? stateMachineEvent.fullIdentifier() : stateMachineEvent.rawName();
        String rawTriggerEvent = triggerPoint.getEvent();

        if (rawTriggerEvent != null && rawTriggerEvent.startsWith("<SYMBOLIC: ") && rawTriggerEvent.endsWith(".*>")) {
            String targetType = rawTriggerEvent.substring(11, rawTriggerEvent.length() - 3);
            if (smEventRaw.startsWith(targetType + ".")) {
                return true;
            }
            if (smEventRaw.contains(".")) {
                String smType = smEventRaw.substring(0, smEventRaw.lastIndexOf('.'));
                if (smType.endsWith("." + targetType) || smType.equals(targetType)) {
                    return true;
                }
            }
            return false;
        }

        List<String> polyEvents = triggerPoint.getPolymorphicEvents() != null ? triggerPoint.getPolymorphicEvents() : java.util.Collections.emptyList();
        
        for (String pe : polyEvents) {
            if (matchEventNames(pe, smEventRaw, triggerPoint.getEventTypeFqn())) {
                return true;
            }
            if (pe.startsWith("<SYMBOLIC: ") && pe.endsWith(".*>")) {
                String targetType = pe.substring(11, pe.length() - 3);
                if (smEventRaw.startsWith(targetType + ".")) {
                    return true;
                }
                if (smEventRaw.contains(".")) {
                    String smType = smEventRaw.substring(0, smEventRaw.lastIndexOf('.'));
                    if (smType.endsWith("." + targetType) || smType.equals(targetType)) {
                        return true;
                    }
                }
            }
        }

        if (rawTriggerEvent != null && rawTriggerEvent.contains(".valueOf(")) {
            String enumClass = rawTriggerEvent.substring(0, rawTriggerEvent.indexOf(".valueOf("));
            if (enumClass.contains(" ")) {
                enumClass = enumClass.substring(enumClass.lastIndexOf(" ") + 1);
            }
            String smClass = smEventRaw.contains(".") ? smEventRaw.substring(0, smEventRaw.lastIndexOf('.')) : smEventRaw;
            if (enumClass.equals(smClass) || enumClass.endsWith("." + smClass) || smClass.endsWith("." + enumClass)) {
                return true;
            }
            return false;
        }

        if (polyEvents.isEmpty() && isDynamicVariable(rawTriggerEvent)) {
            if (triggerPoint.getEventTypeFqn() != null) {
                if (smEventRaw.startsWith(triggerPoint.getEventTypeFqn() + ".")) {
                    return true;
                }
                if (isStringTypeOrPrimitive(triggerPoint.getEventTypeFqn()) && !smEventRaw.contains(".")) {
                    return true;
                }
                return false;
            }
            return true;
        }

        return matchEventNames(rawTriggerEvent, smEventRaw, triggerPoint.getEventTypeFqn());
    }

    private boolean matchEventNames(String triggerEvent, String smEvent, String eventTypeFqn) {
        if (triggerEvent == null || smEvent == null) return false;
        if (triggerEvent.equals(smEvent)) return true;

        String tConst = constantName(triggerEvent);
        String smConst = constantName(smEvent);

        if (!tConst.equals(smConst)) return false;

        // Prevent matching string/primitive triggers to enum transitions
        if (isStringTypeOrPrimitive(eventTypeFqn) && smEvent.contains(".")) {
            return false;
        }

        // Prevent matching enum triggers to string transitions
        if (!smEvent.contains(".") && triggerEvent.contains(".")) {
            return false;
        }

        if (smEvent.contains(".")) {
            String smEnumType = smEvent.substring(0, smEvent.lastIndexOf('.'));

            if (eventTypeFqn != null) {
                return enumTypesMatch(eventTypeFqn, smEnumType);
            }

            if (triggerEvent.contains(".")) {
                String triggerEnumType = triggerEvent.substring(0, triggerEvent.lastIndexOf('.'));
                return enumTypesMatch(triggerEnumType, smEnumType);
            }

            // Bare constant name (e.g. after Enum.name()) with no type context must not match every enum
            return false;
        }

        // Both sides are unqualified identifiers (string/primitive events)
        return !triggerEvent.contains(".");
    }

    private String constantName(String event) {
        return event.contains(".") ? event.substring(event.lastIndexOf('.') + 1) : event;
    }

    private boolean enumTypesMatch(String type1, String type2) {
        if (type1 == null || type2 == null) return false;
        if (type1.equals(type2)) return true;
        if (type1.endsWith("." + type2) || type2.endsWith("." + type1)) return true;

        String simple1 = simpleName(type1);
        String simple2 = simpleName(type2);
        if (!simple1.equals(simple2)) return false;

        // Same simple name only matches when at least one side is package-less (import-style reference).
        // Prevents com.foo.OrderEvents from matching com.bar.OrderEvents.
        return !type1.contains(".") || !type2.contains(".");
    }

    private String simpleName(String name) {
        return name.contains(".") ? name.substring(name.lastIndexOf('.') + 1) : name;
    }

    private boolean isStringTypeOrPrimitive(String typeFqn) {
        if (typeFqn == null) return false;
        return typeFqn.equals("String") || typeFqn.equals("java.lang.String") ||
               typeFqn.equals("int") || typeFqn.equals("long") || typeFqn.equals("char");
    }

    private boolean isDynamicVariable(String eventStr) {
        if (eventStr == null || eventStr.isEmpty()) return false;
        
        if (eventStr.contains(" ? ") && eventStr.contains(" : ")) {
            return true;
        }
        
        if (eventStr.endsWith("()")) {
            return true;
        }
        
        if (eventStr.contains(".")) {
            String firstPart = eventStr.substring(0, eventStr.indexOf('.'));
            if (!firstPart.isEmpty() && Character.isLowerCase(firstPart.charAt(0))) {
                return true; 
            }
            return false;
        }

        if (Character.isLowerCase(eventStr.charAt(0))) {
            return true; 
        }

        return false;
    }
}
