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

import click.kamil.springstatemachineexporter.analysis.model.CallChain;

import java.util.Set;

/**
 * Legacy package-prefix and domain-name matching for single-machine / null-context fallback routing.
 */
final class PackageNameRoutingHeuristics {

    private static final Set<String> GENERIC_CLASS_PREFIXES = Set.of(
            "state", "statemachine", "config", "configuration", "adapter",
            "enterprise", "app", "global", "core", "project", "main",
            "base", "common", "shared");

    private static final Set<String> GENERIC_PACKAGE_SEGMENTS = Set.of(
            "config", "configuration", "service", "services", "impl", "api", "web",
            "controller", "controllers", "messaging", "listener", "listeners",
            "repository", "repositories", "db", "model", "models", "domain",
            "constants", "utils", "helper", "helpers", "event", "events",
            "state", "states", "transition", "transitions", "shared", "common",
            "global", "core", "base", "main", "app", "enterprise");

    private PackageNameRoutingHeuristics() {
    }

    /**
     * @return true when any class in the call chain carries a vertical domain prefix or package segment
     */
    static boolean hasVerticalDomainOwnership(CallChain chain) {
        if (chain == null) {
            return false;
        }
        if (chain.getTriggerPoint() != null && chain.getTriggerPoint().getClassName() != null) {
            if (classHasVerticalDomainOwnership(chain.getTriggerPoint().getClassName())) {
                return true;
            }
        }
        if (chain.getMethodChain() != null) {
            for (String method : chain.getMethodChain()) {
                if (classHasVerticalDomainOwnership(getClassNameOnly(method))) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @return true/false if package heuristics decide, null if inconclusive
     */
    static Boolean matches(CallChain chain, String currentMachineName, String explicitBeanTarget) {
        if (chain == null) {
            return null;
        }

        boolean hasExplicitBeanTarget = explicitBeanTarget != null && !explicitBeanTarget.isEmpty();
        String targetVar = hasExplicitBeanTarget ? explicitBeanTarget : chain.getContextMachineId();
        if (targetVar == null && chain.getTriggerPoint() != null) {
            targetVar = chain.getTriggerPoint().getStateMachineId();
        }

        String simplifiedMachineName = currentMachineName != null
                ? currentMachineName.substring(currentMachineName.lastIndexOf('.') + 1).toLowerCase()
                : "";

        if (targetVar != null && !targetVar.isEmpty()) {
            targetVar = targetVar.toLowerCase();
            if (targetVar.endsWith("statemachine")) {
                String prefix = targetVar.substring(0, targetVar.length() - "statemachine".length());
                if (!prefix.isEmpty()) {
                    if (simplifiedMachineName.contains(prefix)) {
                        return true;
                    } else {
                        return false;
                    }
                }
            } else if (simplifiedMachineName.contains(targetVar)) {
                return true;
            }
        }

        if (chain.getMethodChain() != null && !chain.getMethodChain().isEmpty() && currentMachineName != null) {
            String smPackage = getPackageName(currentMachineName);
            String smSimple = getSimpleClassName(currentMachineName);
            String smPrefix = getFirstCamelCaseWord(smSimple);

            boolean hasPositiveMatch = false;
            boolean hasStrongMismatch = false;

            if (chain.getTriggerPoint() != null && chain.getTriggerPoint().getClassName() != null) {
                String chainClass = chain.getTriggerPoint().getClassName();
                String chainPackage = getPackageName(chainClass);
                String chainSimple = getSimpleClassName(chainClass);
                String chainPrefix = getFirstCamelCaseWord(chainSimple);

                if (hasMatchingDomainPrefix(smPrefix, chainPrefix)) {
                    hasPositiveMatch = true;
                }

                if (isDomainMatch(smPackage, chainPackage, smPrefix, chainPrefix)) {
                    hasPositiveMatch = true;
                }
            }

            for (String method : chain.getMethodChain()) {
                String chainClass = getClassNameOnly(method);
                String chainPackage = getPackageName(chainClass);
                String chainSimple = getSimpleClassName(chainClass);
                String chainPrefix = getFirstCamelCaseWord(chainSimple);

                if (hasMatchingDomainPrefix(smPrefix, chainPrefix)) {
                    hasPositiveMatch = true;
                }

                if (isDomainMatch(smPackage, chainPackage, smPrefix, chainPrefix)) {
                    hasPositiveMatch = true;
                }

                if (isDomainMismatch(smPackage, chainPackage, smPrefix, chainPrefix, smSimple, chainSimple)) {
                    hasStrongMismatch = true;
                }
            }

            if (hasPositiveMatch) {
                return true;
            }
            if (hasStrongMismatch) {
                return false;
            }
        }
        return null;
    }

    private static boolean isDomainMatch(String smPackage, String chainPackage, String smPrefix, String chainPrefix) {
        if (smPackage == null || chainPackage == null || smPackage.isEmpty() || chainPackage.isEmpty()) {
            return false;
        }

        if (smPackage.equals(chainPackage)) {
            return true;
        }

        if (!chainPackage.startsWith(smPackage + ".") && !smPackage.startsWith(chainPackage + ".")) {
            return false;
        }

        String[] p1 = smPackage.split("\\.");
        String[] p2 = chainPackage.split("\\.");
        int matchIdx = -1;
        for (int i = 0; i < Math.min(p1.length, p2.length); i++) {
            if (p1[i].equals(p2[i])) {
                matchIdx = i;
            } else {
                break;
            }
        }

        if (smPrefix != null && matchIdx >= 0) {
            String lowerPrefix = smPrefix.toLowerCase();
            for (int i = 0; i <= matchIdx; i++) {
                if (p1[i].toLowerCase().contains(lowerPrefix) || lowerPrefix.contains(p1[i].toLowerCase())) {
                    return true;
                }
            }
        }

        return false;
    }

    private static boolean isDomainMismatch(
            String smPackage,
            String chainPackage,
            String smPrefix,
            String chainPrefix,
            String smSimple,
            String chainSimple) {
        if (smPackage == null || chainPackage == null || smPackage.isEmpty() || chainPackage.isEmpty()) {
            return false;
        }
        if (smPrefix == null || chainPrefix == null) {
            return false;
        }

        String smLower = smPrefix.toLowerCase();
        String chainLower = chainPrefix.toLowerCase();

        if (isGenericClassPrefix(smLower) || isGenericClassPrefix(chainLower)) {
            return false;
        }

        if (!smLower.equals(chainLower)) {
            if (chainPackage.toLowerCase().contains(smLower) || smPackage.toLowerCase().contains(chainLower)) {
                return false;
            }

            String[] p1 = smPackage.split("\\.");
            String[] p2 = chainPackage.split("\\.");
            int matchIdx = -1;
            for (int i = 0; i < Math.min(p1.length, p2.length); i++) {
                if (p1[i].equals(p2[i])) {
                    matchIdx = i;
                } else {
                    break;
                }
            }
            if (matchIdx >= 1) {
                boolean onlyGenericDivergence = true;
                for (int i = matchIdx + 1; i < p1.length; i++) {
                    if (!isGenericPackageSegment(p1[i])) {
                        onlyGenericDivergence = false;
                        break;
                    }
                }
                if (onlyGenericDivergence) {
                    for (int i = matchIdx + 1; i < p2.length; i++) {
                        if (!isGenericPackageSegment(p2[i])) {
                            onlyGenericDivergence = false;
                            break;
                        }
                    }
                }

                if (!onlyGenericDivergence) {
                    return true;
                }
            }
        }

        return false;
    }

    private static String getPackageName(String fqn) {
        if (fqn == null || !fqn.contains(".")) {
            return "";
        }
        return fqn.substring(0, fqn.lastIndexOf('.'));
    }

    private static String getSimpleClassName(String fqn) {
        if (fqn == null) {
            return "";
        }
        if (!fqn.contains(".")) {
            return fqn;
        }
        return fqn.substring(fqn.lastIndexOf('.') + 1);
    }

    private static String getClassNameOnly(String methodFqn) {
        if (methodFqn == null) {
            return "";
        }
        String clean = methodFqn;
        if (clean.contains("(")) {
            clean = clean.substring(0, clean.indexOf('('));
        }
        if (clean.contains(".")) {
            clean = clean.substring(0, clean.lastIndexOf('.'));
        }
        return clean;
    }

    private static String getFirstCamelCaseWord(String simpleName) {
        if (simpleName == null || simpleName.isEmpty()) {
            return null;
        }
        String[] words = simpleName.split("(?<!^)(?=[A-Z])");
        if (words.length > 0) {
            return words[0];
        }
        return null;
    }

    private static boolean classHasVerticalDomainOwnership(String classFqn) {
        if (classFqn == null || classFqn.isEmpty()) {
            return false;
        }
        String prefix = getFirstCamelCaseWord(getSimpleClassName(classFqn));
        if (prefix != null && !isGenericClassPrefix(prefix)) {
            return true;
        }
        return hasVerticalPackageSegment(getPackageName(classFqn));
    }

    private static boolean hasVerticalPackageSegment(String packageName) {
        if (packageName == null || packageName.isEmpty()) {
            return false;
        }
        String[] segments = packageName.split("\\.");
        for (int i = 2; i < segments.length; i++) {
            if (!isGenericPackageSegment(segments[i])) {
                return true;
            }
        }
        return false;
    }

    private static boolean hasMatchingDomainPrefix(String smPrefix, String chainPrefix) {
        return smPrefix != null && chainPrefix != null && smPrefix.equals(chainPrefix);
    }

    private static boolean isGenericClassPrefix(String prefix) {
        return prefix != null && GENERIC_CLASS_PREFIXES.contains(prefix.toLowerCase());
    }

    private static boolean isGenericPackageSegment(String segment) {
        return segment != null && GENERIC_PACKAGE_SEGMENTS.contains(segment.toLowerCase());
    }
}
