package com.taobao.weex.utils;

import com.intellij.json.psi.JsonFile;
import com.intellij.json.psi.JsonObject;
import com.intellij.json.psi.JsonProperty;
import com.intellij.json.psi.JsonStringLiteral;
import com.intellij.lang.javascript.psi.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.file.PsiDirectoryFactory;
import com.taobao.weex.custom.Settings;
import com.taobao.weex.lint.Attribute;
import com.taobao.weex.lint.WeexTag;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by moxun on 16/10/24.
 */
public class ExtraModulesUtil {
    public static List<WeexTag> getTagsFromNodeModules() {
        List<WeexTag> list = new ArrayList<WeexTag>();
        Project project = ProjectUtil.guessCurrentProject(null);
        Logger.debug(project.toString());
        VirtualFile vf = project.getBaseDir();
        PsiDirectory[] localModules = new PsiDirectory[0];
        if (vf != null && vf.isDirectory()) {
            Logger.debug("Project root dir: " + vf.toString());
            PsiDirectory dir = PsiDirectoryFactory.getInstance(project).createDirectory(vf);
            localModules = getNodeModules(dir);
            for (PsiDirectory directory : localModules) {
                List<PsiFile> comps = getComponents(directory, getMain(directory));
                String homePage = getHomePage(directory);
                for (PsiFile comp : comps) {
                    WeexTag tag = parseToTag(comp);
                    tag.document = homePage;
                    list.add(tag);
                }
            }
        } else {
            Logger.debug("Project base dir is null");
        }

        for (PsiDirectory dir : getGlobalModules(localModules)) {
            List<PsiFile> comps = getComponents(dir, getMain(dir));
            String homePage = getHomePage(dir);
            Logger.debug(comps.toString());
            for (PsiFile comp : comps) {
                WeexTag tag = parseToTag(comp);
                tag.document = homePage;
                list.add(tag);
            }
        }

        return list;
    }

    private static String getHomePage(PsiDirectory directory) {
        PsiFile pkg = directory.findFile("package.json");
        if (pkg != null && pkg instanceof JsonFile) {
            if (((JsonFile) pkg).getTopLevelValue() instanceof JsonObject) {
                JsonObject object = (JsonObject) ((JsonFile) pkg).getTopLevelValue();
                if (object != null) {
                    JsonProperty homePage = object.findProperty("homepage");
                    if (homePage != null && homePage.getValue() != null && homePage.getValue() instanceof JsonStringLiteral) {
                        JsonStringLiteral propValue = (JsonStringLiteral) homePage.getValue();
                        return propValue.getValue();
                    }
                }
            }
        }
        return null;
    }

    private static List<PsiDirectory> getGlobalModules(PsiDirectory[] localModules) {
        if (localModules == null) {
            localModules = new PsiDirectory[0];
        }
        List<PsiDirectory> result = new ArrayList<PsiDirectory>();
        List<String> globals = Settings.getGlobalModules();
        List<String> locals = new ArrayList<String>();
        for (PsiDirectory dir : localModules) {
            if (dir != null) {
                locals.add(dir.getVirtualFile().getCanonicalPath());
            }
        }
        for (String global : globals) {
            if (!locals.contains(global)) {
                VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByPath(global);
                if (vf != null && vf.isDirectory()) {
                    PsiDirectory dir = PsiDirectoryFactory.getInstance(ProjectUtil.guessCurrentProject(null)).createDirectory(vf);
                    if (dir != null) {
                        result.add(dir);
                    }
                }
            } else {
                Logger.info("Module " + global + " already exists locally, skip it.");
            }
        }
        Logger.debug(result.toString());
        return result;
    }

    private static WeexTag parseToTag(PsiFile comp) {
        WeexTag weexTag = new WeexTag();
        weexTag.tag = comp.getContainingFile().getName().replace(".we", "");
        weexTag.attrs = new CopyOnWriteArrayList<Attribute>();
        weexTag.declare = comp;
        Map<String, String> vars = WeexFileUtil.getAllVarNames(comp);
        for (Map.Entry<String, String> entry : vars.entrySet()) {
            Attribute attribute = new Attribute();
            attribute.name = convertAttrName(entry.getKey());
            attribute.valueType = getType(entry.getValue());
            weexTag.attrs.add(attribute);
        }
        return weexTag;
    }

    private static String convertAttrName(String name) {
        char[] chars = name.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char c: chars) {
            if (Character.isUpperCase(c)) {
                if (sb.length() == 0) {
                    sb.append(Character.toLowerCase(c));
                } else {
                    sb.append('-').append(Character.toLowerCase(c));
                }
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    private static String getType(String realType) {
        realType = realType.toLowerCase();
        if ("number".equals(realType) || "boolean".equals(realType) || "array".equals(realType)) {
            return realType.toLowerCase();
        }
        return "var";
    }

    private static PsiDirectory[] getNodeModules(PsiDirectory root) {
        PsiDirectory node_modules = root.findSubdirectory("node_modules");
        if (node_modules != null) {
            return node_modules.getSubdirectories();
        }
        return new PsiDirectory[0];
    }

    private static PsiFile getMain(PsiDirectory moduleRoot) {
        PsiFile pkg = moduleRoot.findFile("package.json");
        if (pkg != null && pkg instanceof JsonFile) {
            if (((JsonFile) pkg).getTopLevelValue() instanceof JsonObject) {
                JsonObject object = (JsonObject) ((JsonFile) pkg).getTopLevelValue();
                if (object != null) {
                    JsonProperty property = object.findProperty("main");
                    if (property != null && property.getValue() != null && property.getValue() instanceof JsonStringLiteral) {
                        JsonStringLiteral propValue = (JsonStringLiteral) property.getValue();
                        String value = propValue.getValue();
                        PsiFile psiFile = moduleRoot.findFile(value.replace("./", ""));
                        return psiFile;
                    }
                }
            }
        }
        return null;
    }

    private static List<PsiFile> getComponents(PsiDirectory root, PsiFile file) {
        List<PsiFile> results = new ArrayList<PsiFile>();
        if (file != null && file instanceof JSFile) {
            for (PsiElement element : file.getChildren()) {
                if (element instanceof JSExpressionStatement) {
                    JSExpression expression = ((JSExpressionStatement) element).getExpression();
                    if (expression instanceof JSCallExpression
                            && ((JSCallExpression) expression).getArguments().length == 1
                            && ((JSCallExpression) expression).getArguments()[0] instanceof JSLiteralExpression) {
                        JSLiteralExpression expression1 = (JSLiteralExpression) ((JSCallExpression) expression).getArguments()[0];
                        Object val = expression1.getValue();
                        if (val != null) {
                            String[] temp = val.toString().replace("./", "").split("/");
                            if (temp != null && temp.length > 0) {
                                String fileName = temp[temp.length - 1];
                                if (fileName.toLowerCase().endsWith(".we")) {
                                    PsiDirectory start = root;
                                    for (int i = 0; i < temp.length - 1; i++) {
                                        PsiDirectory dir = start.findSubdirectory(temp[i]);
                                        if (dir != null) {
                                            start = dir;
                                        }
                                    }
                                    PsiFile weexScript = start.findFile(temp[temp.length - 1]);
                                    if (weexScript != null) {
                                        results.add(weexScript);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return results;
    }

    public static boolean isNodeModule(PsiDirectory dir) {
        PsiFile pkg = dir.findFile("package.json");
        return pkg != null && pkg instanceof JsonFile;
    }

    public static String getModuleName(PsiDirectory dir) {
        PsiFile pkg = dir.findFile("package.json");
        String name = dir.getName();
        if (pkg != null && pkg instanceof JsonFile) {
            if (((JsonFile) pkg).getTopLevelValue() instanceof JsonObject) {
                JsonObject object = (JsonObject) ((JsonFile) pkg).getTopLevelValue();
                if (object != null) {
                    JsonProperty property = object.findProperty("name");
                    JsonProperty property1 = object.findProperty("version");
                    if (property != null && property.getValue() != null && property.getValue() instanceof JsonStringLiteral) {
                        JsonStringLiteral propValue = (JsonStringLiteral) property.getValue();
                        name = propValue.getValue();
                        if (property1 != null && property1.getValue() != null && property1.getValue() instanceof JsonStringLiteral) {
                            JsonStringLiteral propValue1 = (JsonStringLiteral) property1.getValue();
                            name = name + ":" + propValue1.getValue();
                        }
                    }
                }
            }
        }
        return name;
    }

    public static void main(String[] args) {
        System.out.println(convertAttrName("Foo"));
        System.out.println(convertAttrName("HowAreYou"));
        System.out.println(convertAttrName("areYouOK"));
    }
}