// 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.jsonpath.psi;

import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class JsonPathPsiUtils {
  private static final String ourEscapesTable = "\"\"\\\\//b\bf\fn\nr\rt\t";

  static final Key<List<Pair<TextRange, String>>> STRING_FRAGMENTS = new Key<>("JSONPATH string fragments");

  private JsonPathPsiUtils() {
  }

  public static @NotNull List<Pair<TextRange, String>> getTextFragments(@NotNull JsonPathStringLiteral literal) {
    List<Pair<TextRange, String>> cached = literal.getUserData(STRING_FRAGMENTS);
    if (cached != null) return cached;

    List<Pair<TextRange, String>> result = new ArrayList<>();
    String text = literal.getText();
    int length = text.length();
    int pos = 1, unescapedSequenceStart = 1;
    while (pos < length) {
      if (text.charAt(pos) == '\\') {
        if (unescapedSequenceStart != pos) {
          result.add(Pair.create(new TextRange(unescapedSequenceStart, pos), text.substring(unescapedSequenceStart, pos)));
        }
        if (pos == length - 1) {
          result.add(Pair.create(new TextRange(pos, pos + 1), "\\"));
          break;
        }
        char next = text.charAt(pos + 1);
        switch (next) {
          case '"', '\\', '/', 'b', 'f', 'n', 'r', 't' -> {
            final int idx = ourEscapesTable.indexOf(next);
            result.add(Pair.create(new TextRange(pos, pos + 2), ourEscapesTable.substring(idx + 1, idx + 2)));
            pos += 2;
          }
          case 'u' -> {
            int i = pos + 2;
            for (; i < pos + 6; i++) {
              if (i == length || !StringUtil.isHexDigit(text.charAt(i))) {
                break;
              }
            }
            result.add(Pair.create(new TextRange(pos, i), text.substring(pos, i)));
            pos = i;
          }
          default -> {
            result.add(Pair.create(new TextRange(pos, pos + 2), text.substring(pos, pos + 2)));
            pos += 2;
          }
        }
        unescapedSequenceStart = pos;
      }
      else {
        pos++;
      }
    }
    int contentEnd = text.charAt(0) == text.charAt(length - 1) ? length - 1 : length;
    if (unescapedSequenceStart < contentEnd) {
      result.add(Pair.create(new TextRange(unescapedSequenceStart, contentEnd), text.substring(unescapedSequenceStart, contentEnd)));
    }
    result = Collections.unmodifiableList(result);
    literal.putUserData(STRING_FRAGMENTS, result);
    return result;
  }
}
