// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.folding.impl;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.xml.util.HtmlUtil;
import com.intellij.xml.util.JspFileTypeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.StringTokenizer;

public class XmlElementSignatureProvider extends AbstractElementSignatureProvider {
  private static final Logger LOG = Logger.getInstance(XmlElementSignatureProvider.class);

  @Override
  public String getSignature(@NotNull PsiElement element) {
    if (element instanceof XmlTag tag) {
      PsiElement parent = tag.getParent();

      StringBuilder buffer = new StringBuilder();
      buffer.append("tag").append(ELEMENT_TOKENS_SEPARATOR);
      String name = tag.getName();
      buffer.append(name.isEmpty() ? "<unnamed>" : escape(name));

      buffer.append(ELEMENT_TOKENS_SEPARATOR);
      int childIndex = getChildIndex(tag, parent, name, XmlTag.class);
      if (childIndex < 0) return null;
      buffer.append(childIndex);

      if (!(parent instanceof PsiFile)) {
        String parentSignature = getSignature(parent);
        if (parentSignature == null) {
          return null;
        }
        buffer.append(";");
        buffer.append(parentSignature);
      }

      return buffer.toString();
    }
    return null;
  }

  @Override
  protected PsiElement restoreBySignatureTokens(@NotNull PsiFile file,
                                                @NotNull PsiElement parent,
                                                @NotNull String type,
                                                @NotNull StringTokenizer tokenizer,
                                                @Nullable StringBuilder processingInfoStorage)
  {
    if (type.equals("tag")) {
      String name = tokenizer.nextToken();

      if (parent instanceof XmlFile) {
        parent = ((XmlFile)parent).getDocument();
        if (parent == null) {
          return null;
        }
      }

      try {
        int index = Integer.parseInt(tokenizer.nextToken());
        String unescapedName = unescape(name);
        PsiElement result = restoreElementInternal(parent, unescapedName, index, XmlTag.class);

        if (result == null && JspFileTypeUtil.isJsp(file)) {
          //TODO: FoldingBuilder API, psi roots, etc?
          if (parent instanceof XmlDocument xmlDocument) {
            // html tag, not found in jsp tree
            result = restoreElementInternal(HtmlUtil.getRealXmlDocument(xmlDocument), unescapedName, index, XmlTag.class);
          }
          else if (name.equals("<unnamed>")) {
            // scriplet/declaration missed because null name
            result = restoreElementInternal(parent, "", index, XmlTag.class);
          }
        }

        return result;
      }
      catch (NumberFormatException e) {
        LOG.error(e);
        return null;
      }
    }
    return null;
  }
}
