package de.espend.idea.laravel.routing.utils;

import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression;
import com.jetbrains.php.lang.psi.elements.ArrayHashElement;
import com.jetbrains.php.lang.psi.elements.MethodReference;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.utils.PhpElementsUtil;
import org.jetbrains.annotations.NotNull;

import java.util.*;

/**
 * @author Daniel Espendiller <[email protected]>
 */
public class RouteGroupUtil {
    /**
     * Analyzes Route::group elements and returns string values for specified property.
     * Route::group(['namespace' => 'Foo'], function() {
     *      Route::group(['namespace' => 'Bar'], function() {
     *          Route::get(...
     *      });
     * });
     *
     * getRouteGroupPropertiesCollection(Route::get element, "namespace") will return list with 'Foo', 'Bar'
     */
    @NotNull
    public static List<String> getRouteGroupPropertiesCollection(PsiElement element, String propertyName)
    {
        List<String> values = new ArrayList<>();

        RouteGroupCondition routeGroupCondition = new RouteGroupCondition();

        PsiElement routeGroup = PsiTreeUtil.findFirstParent(element, true, routeGroupCondition);

        while (routeGroup != null) {
            ArrayCreationExpression arrayCreation = PsiTreeUtil.getChildOfType(((MethodReference)routeGroup).getParameterList(), ArrayCreationExpression.class);

            if (arrayCreation != null) {
                for (ArrayHashElement hashElement : arrayCreation.getHashElements()) {
                    if (hashElement.getKey() instanceof StringLiteralExpression) {
                        if (propertyName.equals(((StringLiteralExpression) hashElement.getKey()).getContents())) {
                            if (hashElement.getValue() instanceof StringLiteralExpression) {
                                values.add(((StringLiteralExpression) hashElement.getValue()).getContents());
                            }
                            break;
                        }
                    }
                }
            }

            routeGroup = PsiTreeUtil.findFirstParent(routeGroup, true, routeGroupCondition);
        }

        Collections.reverse(values);
        return values;
    }

    private static class RouteGroupCondition implements Condition<PsiElement> {
        private static Set<String> availableClasses = new HashSet<>(Arrays.asList(
                "\\Illuminate\\Routing\\Router",
                "\\Illuminate\\Support\\Facades\\Route",
                "\\Route"));

        @Override
        public boolean value(PsiElement psiElement) {

            if(!(psiElement instanceof MethodReference)) {
                return false;
            }

            MethodReference methodReference = (MethodReference) psiElement;

            if (!"group".equals(methodReference.getName())) {
                return false;
            }

            if(methodReference.getClassReference() == null || methodReference.getClassReference().getName() == null) {
                return false;
            }

            return availableClasses.contains(PhpElementsUtil.getFullClassName(methodReference, methodReference.getClassReference().getName()));
        }
    }
}