(function () {
  const KEYWORDS = new Set([
    "if", "elif", "else", "for", "while", "to", "step", "in",
    "def", "return", "class", "interface", "enum", "annotation",
    "public", "private", "protected", "static", "abstract", "final", "override",
    "readonly", "writeonly", "property", "get", "set", "extends", "implements",
    "import", "package", "as", "try", "catch", "finally", "throw", "throws",
    "assert", "pass", "const", "this", "super",
    "and", "or", "not", "is", "constructor", "notnull", "ornull", "ref", "out",
    "operator", "break", "continue", "match", "case", "default", "lambda"
  ]);

  const TYPES = new Set([
    "Int", "UInt", "Long", "ULong", "Short", "UShort", "Byte", "SByte",
    "Float", "Double", "Bool", "Boolean", "Char", "String", "Object",
    "Void", "Exception"
  ]);

  function escapeHtml(text) {
    return text
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/\"/g, "&quot;");
  }

  function highlightPlain(text) {
    let out = "";
    const re = /@[A-Za-z_][A-Za-z0-9_]*|\b\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+)?[uUfF]?\b|\b[A-Za-z_][A-Za-z0-9_]*\b/g;
    let last = 0;
    let match;

    while ((match = re.exec(text)) !== null) {
      const start = match.index;
      const end = re.lastIndex;
      if (start > last) {
        out += escapeHtml(text.slice(last, start));
      }

      const token = match[0];
      if (token.startsWith("@")) {
        out += '<span class="tok-ann">' + escapeHtml(token) + "</span>";
      } else if (/^\d/.test(token)) {
        out += '<span class="tok-num">' + escapeHtml(token) + "</span>";
      } else if (KEYWORDS.has(token)) {
        out += '<span class="tok-kw">' + escapeHtml(token) + "</span>";
      } else if (TYPES.has(token) || /^[A-Z][A-Za-z0-9_]*$/.test(token)) {
        out += '<span class="tok-type">' + escapeHtml(token) + "</span>";
      } else if (token === "true" || token === "false" || token === "null") {
        out += '<span class="tok-num">' + escapeHtml(token) + "</span>";
      } else {
        out += escapeHtml(token);
      }
      last = end;
    }

    if (last < text.length) {
      out += escapeHtml(text.slice(last));
    }

    return out;
  }

  function highlightLine(line) {
    let out = "";
    let i = 0;
    while (i < line.length) {
      const ch = line[i];
      if (ch === "#") {
        out += '<span class="tok-com tok-cmt">' + escapeHtml(line.slice(i)) + "</span>";
        return out;
      }
      if (ch === "\"" || ch === "'") {
        const quote = ch;
        let j = i + 1;
        let escaped = false;
        for (; j < line.length; j++) {
          const c = line[j];
          if (escaped) {
            escaped = false;
            continue;
          }
          if (c === "\\") {
            escaped = true;
            continue;
          }
          if (c === quote) {
            j++;
            break;
          }
        }
        const str = line.slice(i, j);
        out += '<span class="tok-str">' + escapeHtml(str) + "</span>";
        i = j;
        continue;
      }

      let j = i + 1;
      for (; j < line.length; j++) {
        const c = line[j];
        if (c === "#" || c === "\"" || c === "'") break;
      }
      const plain = line.slice(i, j);
      out += highlightPlain(plain);
      i = j;
    }
    return out;
  }

  function highlightCodeBlock(code) {
    const lines = code.split(/\n/);
    const outLines = lines.map(highlightLine);
    return outLines.join("\n");
  }

  window.klynHighlightCode = highlightCodeBlock;

  function run() {
    const blocks = document.querySelectorAll("pre code");
    blocks.forEach((block) => {
      const raw = block.textContent || "";
      block.innerHTML = highlightCodeBlock(raw);
    });
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", run);
  } else {
    run();
  }
})();
