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

  const LITERALS = new Set(["true", "false", "null"]);

  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 token(className, value) {
    return '<span class="' + className + '">' + escapeHtml(value) + "</span>";
  }

  function isIdentifierStart(ch) {
    return /[A-Za-z_]/.test(ch);
  }

  function isIdentifierPart(ch) {
    return /[A-Za-z0-9_]/.test(ch);
  }

  function highlightCodeBlock(code) {
    let out = "";
    let i = 0;
    while (i < code.length) {
      const ch = code[i];

      if (ch === "#") {
        const start = i;
        while (i < code.length && code[i] !== "\n") i++;
        out += token("tok-com tok-cmt", code.slice(start, i));
        continue;
      }

      if (ch === "/" && code[i + 1] === "*") {
        const start = i;
        i += 2;
        while (i + 1 < code.length && !(code[i] === "*" && code[i + 1] === "/")) i++;
        if (i + 1 < code.length) i += 2;
        out += token("tok-com tok-cmt", code.slice(start, i));
        continue;
      }

      if (ch === "\"" || ch === "'") {
        const quote = ch;
        const start = i;
        const triple = code.slice(i, i + 3) === quote.repeat(3);
        i += triple ? 3 : 1;
        let escaped = false;
        while (i < code.length) {
          if (triple) {
            if (code.slice(i, i + 3) === quote.repeat(3)) {
              i += 3;
              break;
            }
            i++;
          } else {
            const current = code[i++];
            if (escaped) {
              escaped = false;
            } else if (current === "\\") {
              escaped = true;
            } else if (current === quote) {
              break;
            }
          }
        }
        out += token("tok-str", code.slice(start, i));
        continue;
      }

      if (ch === "@" && isIdentifierStart(code[i + 1] || "")) {
        const start = i++;
        while (i < code.length && isIdentifierPart(code[i])) i++;
        out += token("tok-ann", code.slice(start, i));
        continue;
      }

      if (/\d/.test(ch)) {
        const numeric = code.slice(i).match(/^(?:0[xX][0-9A-Fa-f](?:_?[0-9A-Fa-f])*|0[bB][01](?:_?[01])*|0[oO][0-7](?:_?[0-7])*|\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?)(?:[uU](?:[lL])?|[lL]|[fF])?/);
        if (numeric) {
          out += token("tok-num", numeric[0]);
          i += numeric[0].length;
          continue;
        }
      }

      if (isIdentifierStart(ch)) {
        const start = i++;
        while (i < code.length && isIdentifierPart(code[i])) i++;
        const identifier = code.slice(start, i);
        if (LITERALS.has(identifier)) {
          out += token("tok-num", identifier);
        } else if (KEYWORDS.has(identifier)) {
          out += token("tok-kw", identifier);
        } else if (TYPES.has(identifier) || /^[A-Z]/.test(identifier)) {
          out += token("tok-type", identifier);
        } else {
          out += escapeHtml(identifier);
        }
        continue;
      }

      if (":=+-*/<>!%&|^~?.(),[]{}".includes(ch)) {
        out += token("tok-op", ch);
      } else {
        out += escapeHtml(ch);
      }
      i++;
    }
    return out;
  }

  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();
  }
})();
