/* global React */
// Extremely small TS/JS syntax highlighter used for code blocks.
// Not a real tokenizer; good enough for the short code samples we show.

const KEYWORDS = new Set([
  "import","from","const","let","var","await","async","function","return",
  "if","else","for","of","in","while","new","true","false","null","undefined",
  "export","default","as","type","interface"
]);

const BUILTINS = new Set(["console","Promise","Math","Date","synapse"]);

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

function highlight(line) {
  // Handle line comments first
  const cmt = line.indexOf("//");
  let code = line, trailing = "";
  if (cmt >= 0) {
    code = line.slice(0, cmt);
    trailing = `<span class="tok-cmt">${escapeHtml(line.slice(cmt))}</span>`;
  }

  // Strings
  code = escapeHtml(code).replace(/(['"`])((?:\\.|(?!\1).)*)\1/g, (m) => `<span class="tok-str">${m}</span>`);

  // Numbers
  code = code.replace(/\b(\d+(?:\.\d+)?)\b/g, `<span class="tok-num">$1</span>`);

  // Keywords
  code = code.replace(/\b([a-zA-Z_$][\w$]*)\b/g, (m, w) => {
    if (KEYWORDS.has(w)) return `<span class="tok-kw">${w}</span>`;
    if (BUILTINS.has(w)) return `<span class="tok-const">${w}</span>`;
    return m;
  });

  // Function calls:  word(
  code = code.replace(/([a-zA-Z_$][\w$]*)(\s*)\(/g, (m, w, ws) => {
    // Don't re-wrap if already a span
    if (/tok-/.test(w)) return m;
    return `<span class="tok-fn">${w}</span>${ws}(`;
  });

  // Object properties after dot
  code = code.replace(/\.([a-zA-Z_$][\w$]*)/g, (m, w) => `.<span class="tok-prop">${w}</span>`);

  // Punctuation
  code = code.replace(/([{}();,:])/g, `<span class="tok-punc">$1</span>`);

  return code + trailing;
}

function CodeBlock({ lang = "typescript", file, code, tools = ["copy"] }) {
  const [copied, setCopied] = React.useState(false);
  const lines = code.replace(/\n+$/, "").split("\n");
  const onCopy = () => {
    navigator.clipboard && navigator.clipboard.writeText(code);
    setCopied(true);
    setTimeout(() => setCopied(false), 1400);
  };
  return (
    <div className="code">
      <div className="code-head">
        <span className="lang"><span className="sq"></span>{file || lang}</span>
        <span className="tools">
          {tools.includes("copy") && (
            <button onClick={onCopy}>{copied ? "copied" : "copy"}</button>
          )}
        </span>
      </div>
      <pre><code dangerouslySetInnerHTML={{
        __html: lines.map(l => highlight(l)).join("\n")
      }} /></pre>
    </div>
  );
}

Object.assign(window, { CodeBlock, highlight });
