package com.jantvrdik.intellij.latte.annotator;

import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.jantvrdik.intellij.latte.config.LatteConfiguration;
import com.jantvrdik.intellij.latte.intentions.*;
import com.jantvrdik.intellij.latte.psi.*;
import com.jantvrdik.intellij.latte.settings.LatteTagSettings;
import org.jetbrains.annotations.NotNull;

/**
 * Annotator is mostly used to check semantic rules which can not be easily checked during parsing.
 */
public class LatteAnnotator implements Annotator {
	@Override
	public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
		if (element instanceof LatteMacroClassic) {
			checkMacroClassic((LatteMacroClassic) element, holder);

		} else if (element instanceof LatteNetteAttr) {
			checkNetteAttr((LatteNetteAttr) element, holder);

		}/* else if (element instanceof LeafPsiElement && element.getParent().getLastChild() instanceof PsiErrorElement) {
			LeafPsiElement leaf = (LeafPsiElement) element;
			if (leaf.getElementType() == LatteTypes.T_PHP_DOUBLE_QUOTE_LEFT
					|| leaf.getElementType() == LatteTypes.T_PHP_SINGLE_QUOTE_LEFT) {
				holder.createErrorAnnotation(element, "Unclosed string");
			} else if (leaf.getElementType() == LatteTypes.T_MACRO_OPEN_TAG_OPEN || leaf.getElementType() == LatteTypes.T_MACRO_CLOSE_TAG_OPEN) {
				holder.createErrorAnnotation(element.getParent(), "Malformed tag. Missing closing }");
			}
		}*/
	}

	private void checkNetteAttr(@NotNull LatteNetteAttr element, @NotNull AnnotationHolder holder) {
		PsiElement attrName = element.getAttrName();
		String tagName = attrName.getText();
		boolean prefixed = false;

		if (tagName.startsWith("n:inner-")) {
			prefixed = true;
			tagName = tagName.substring(8);
		} else if (tagName.startsWith("n:tag-")) {
			prefixed = true;
			tagName = tagName.substring(6);
		} else {
			tagName = tagName.substring(2);
		}

		Project project = element.getProject();
		LatteTagSettings macro = LatteConfiguration.getInstance(project).getTag(tagName);
		if (macro == null || macro.getType() == LatteTagSettings.Type.UNPAIRED) {
			Annotation annotation = holder.createErrorAnnotation(attrName, "Unknown attribute tag " + attrName.getText());
			annotation.registerFix(new AddCustomPairMacro(tagName));
			if (!prefixed) annotation.registerFix(new AddCustomAttrOnlyMacro(tagName));

		} else if (prefixed && macro.getType() != LatteTagSettings.Type.PAIR && macro.getType() != LatteTagSettings.Type.AUTO_EMPTY) {
			holder.createErrorAnnotation(attrName, "Attribute tag n:" + tagName + " can not be used with prefix.");
		}
	}

	private void checkMacroClassic(@NotNull LatteMacroClassic element, @NotNull AnnotationHolder holder) {
		LatteMacroTag openTag = element.getOpenTag();
		LatteMacroTag closeTag = element.getCloseTag();

		String openTagName = openTag.getMacroName();
		LatteTagSettings macro = LatteConfiguration.getInstance(element.getProject()).getTag(openTagName);
		if (macro == null || macro.getType() == LatteTagSettings.Type.ATTR_ONLY) {
			boolean isOk = false;
			LatteMacroContent content = openTag.getMacroContent();
			if (content != null) {
				LattePhpContent phpContent = content.getFirstPhpContent();
				if (phpContent != null && phpContent.getFirstChild() instanceof LattePhpVariable) {
					isOk = true;
				}
			}

			if (!isOk) {
				if (macro != null) {
					holder.createErrorAnnotation(openTag, "Can not use n:" + openTagName + " attribute as normal tag");
					if (closeTag != null) {
						holder.createErrorAnnotation(closeTag, "Tag n:" + openTagName + " can not be used as pair tag");
					}

				} else {
					Annotation annotation = holder.createErrorAnnotation(openTag, "Unknown tag {" + openTagName + "}");
					annotation.registerFix(new AddCustomPairMacro(openTagName));
					annotation.registerFix(new AddCustomUnpairedMacro(openTagName));
				}
			}
		}

		String closeTagName = closeTag != null ? closeTag.getMacroName() : null;
		if (closeTagName != null && !closeTagName.isEmpty() && !closeTagName.equals(openTagName)) {
			holder.createErrorAnnotation(closeTag, "Unexpected {/" + closeTagName + "}, expected {/" + openTagName + "}");
		}

		if (
				macro != null
				&& closeTag == null
				&& ((element instanceof LattePairMacro && macro.getType() == LatteTagSettings.Type.AUTO_EMPTY) || macro.getType() == LatteTagSettings.Type.PAIR)
		) {
			holder.createErrorAnnotation(openTag, "Unclosed tag " + openTagName);
		}
	}
}