// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.tasks.generic;

import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.annotations.Tag;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Mikhail Golubev
 */
@Tag("XPathResponseHandler")
public final class XPathResponseHandler extends SelectorBasedResponseHandler {
  private final Map<String, XPath> myCompiledCache = new HashMap<>();

  /**
   * Serialization constructor
   */
  @SuppressWarnings("UnusedDeclaration")
  public XPathResponseHandler() {
    // empty
  }

  public XPathResponseHandler(GenericRepository repository) {
    super(repository);
  }

  @Override
  protected @NotNull List<Object> selectTasksList(@NotNull String response, int max) throws Exception {
    Document document = new SAXBuilder(false).build(new StringReader(response));
    Element root = document.getRootElement();
    XPath xPath = lazyCompile(getSelector(TASKS).getPath());
    @SuppressWarnings("unchecked")
    List<Object> rawTaskElements = xPath.selectNodes(root);
    if (!rawTaskElements.isEmpty() && !(rawTaskElements.get(0) instanceof Element)) {
      throw new Exception(String.format("Expression '%s' should match list of XML elements. Got '%s' instead.",
                                        xPath.getXPath(), rawTaskElements.toString()));
    }
    return ContainerUtil.getFirstItems(rawTaskElements, max);
  }

  @Override
  protected @Nullable String selectString(@NotNull Selector selector, @NotNull Object context) throws Exception {
    if (StringUtil.isEmpty(selector.getPath())) {
      return null;
    }
    XPath xPath = lazyCompile(selector.getPath());
    String s = xPath.valueOf(context); //NON-NLS
    if (s == null) {
      throw new Exception(String.format("XPath expression '%s' doesn't match", xPath.getXPath()));
    }
    return s;
  }

  private @NotNull XPath lazyCompile(@NotNull String path) throws Exception {
    XPath xPath = myCompiledCache.get(path);
    if (xPath == null) {
      try {
        xPath = XPath.newInstance(path);
        myCompiledCache.put(path, xPath);
      }
      catch (JDOMException e) {
        throw new Exception(String.format("Malformed XPath expression '%s'", path));
      }
    }
    return xPath;
  }

  @Override
  public @NotNull ResponseType getResponseType() {
    return ResponseType.XML;
  }
}
