/**
 * highlightNativ — lightweight, dependency-free .nativ syntax highlighter.
 * Mirrors the token palette in tokens/syntax.css (.k .f .s .t .m .n .c).
 * Returns an HTML string of <span> tokens for one source string.
 */
const KEYWORD_WORDS = [
  "app", "screen", "component", "model", "state", "remember", "between",
  "sessions", "store", "navigation", "tabs", "start", "name", "each", "in",
  "if", "then", "else", "on", "tap", "go", "to", "back", "bind",
];
const ELEMENT_WORDS = [
  "text", "button", "textfield", "toggle", "row", "column", "spacer", "image",
  "list", "stack", "icon", "color", "spacing", "padding", "align",
];
const MODIFIER_WORDS = [
  "big", "bold", "small", "gray", "grey", "green", "red", "blue", "black",
  "white", "italic", "center", "leading", "trailing",
];
const KEYWORDS = new Set(KEYWORD_WORDS);
const ELEMENTS = new Set(ELEMENT_WORDS);
const MODIFIERS = new Set(MODIFIER_WORDS);
const COMPLETIONS = [
  { label: "screen", insert: "screen Home:\n  ", kind: "keyword", detail: "new app screen" },
  { label: "state", insert: "state count = 0", kind: "keyword", detail: "screen-local value" },
  { label: "text", insert: 'text "Hello, Nativ"', kind: "element", detail: "render text" },
  { label: "button", insert: 'button "Tap me":\n  count += 1', kind: "element", detail: "tap action" },
  { label: "textfield", insert: 'textfield "Name"', kind: "element", detail: "editable input" },
  { label: "toggle", insert: 'toggle "Enabled"', kind: "element", detail: "boolean switch" },
  { label: "row", insert: "row:\n  ", kind: "layout", detail: "horizontal layout" },
  { label: "column", insert: "column:\n  ", kind: "layout", detail: "vertical layout" },
  { label: "spacer", insert: "spacer", kind: "layout", detail: "flex space" },
  { label: "on tap", insert: "on tap:\n  ", kind: "event", detail: "gesture handler" },
  { label: "go to", insert: "go to Detail", kind: "event", detail: "navigate" },
  ...MODIFIER_WORDS.map((m) => ({ label: m, insert: m, kind: "modifier", detail: "style modifier" })),
];

function escapeHtml(s) {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

function highlightNativ(src) {
  const lines = src.split("\n");
  return lines
    .map((line) => {
      // full-line comment
      const commentIdx = line.indexOf("//");
      let code = line;
      let comment = "";
      if (commentIdx >= 0) {
        code = line.slice(0, commentIdx);
        comment = line.slice(commentIdx);
      }
      // tokenize the code part: strings, numbers, words, punctuation
      const tokenRe = /("[^"]*")|(\b\d+\b)|([A-Za-z_][A-Za-z0-9_]*)|([^\sA-Za-z0-9_"]+)|(\s+)/g;
      let out = "";
      let m;
      while ((m = tokenRe.exec(code)) !== null) {
        const [whole, str, num, word, punct, ws] = m;
        if (str) out += `<span class="s">${escapeHtml(str)}</span>`;
        else if (num) out += `<span class="n">${num}</span>`;
        else if (ws) out += escapeHtml(ws);
        else if (punct) out += escapeHtml(punct);
        else if (word) {
          if (KEYWORDS.has(word)) out += `<span class="k">${word}</span>`;
          else if (ELEMENTS.has(word)) out += `<span class="f">${word}</span>`;
          else if (MODIFIERS.has(word)) out += `<span class="m">${word}</span>`;
          else if (/^[A-Z]/.test(word)) out += `<span class="t">${word}</span>`;
          else out += escapeHtml(word);
        }
      }
      if (comment) out += `<span class="c">${escapeHtml(comment)}</span>`;
      return out || "\u200b";
    })
    .join("\n");
}

function completionContext(src, cursor) {
  const left = src.slice(0, cursor);
  const right = src.slice(cursor);
  const line = left.split("\n").pop() || "";
  const word = /[A-Za-z_][A-Za-z0-9_]*$/.exec(line)?.[0] || "";
  const suffix = /^[A-Za-z0-9_]*/.exec(right)?.[0] || "";
  return {
    prefix: word,
    start: cursor - word.length,
    end: cursor + suffix.length,
    lineIndex: left.split("\n").length - 1,
    col: line.length,
    lineBlank: /^\s*$/.test(line),
  };
}

/**
 * SourceEditor — a real <textarea> over a syntax-highlighted <pre> overlay,
 * the same approach the hosted /try playground uses. Line numbers in a gutter.
 */
function SourceEditor({ value, onChange }) {
  const taRef = React.useRef(null);
  const preRef = React.useRef(null);
  const gutRef = React.useRef(null);
  const [cursor, setCursor] = React.useState(0);
  const [active, setActive] = React.useState(0);
  const [focused, setFocused] = React.useState(false);
  const [hiddenAt, setHiddenAt] = React.useState(-1);

  const sync = () => {
    const ta = taRef.current;
    if (preRef.current) {
      preRef.current.scrollTop = ta.scrollTop;
      preRef.current.scrollLeft = ta.scrollLeft;
    }
    if (gutRef.current) gutRef.current.scrollTop = ta.scrollTop;
  };

  const lineCount = value.split("\n").length;
  const gutter = Array.from({ length: lineCount }, (_, i) => i + 1).join("\n");
  const ctx = completionContext(value, cursor);
  const prefix = ctx.prefix.toLowerCase();
  const suggestions = focused && hiddenAt !== cursor && (prefix || ctx.lineBlank)
    ? COMPLETIONS.filter((c) => !prefix || c.label.startsWith(prefix)).slice(0, 8)
    : [];
  const item = suggestions[active] || suggestions[0];
  const top = Math.max(10, Math.min(18 + ctx.lineIndex * 22.1 + 26 - (taRef.current?.scrollTop || 0), 340));
  const left = Math.max(8, Math.min(4 + ctx.col * 7.8 - (taRef.current?.scrollLeft || 0), 360));

  React.useEffect(() => setActive(0), [ctx.prefix, cursor]);

  const rememberCursor = () => {
    const ta = taRef.current;
    if (ta) setCursor(ta.selectionStart || 0);
  };

  const applyCompletion = (picked = item) => {
    if (!picked) return;
    const next = value.slice(0, ctx.start) + picked.insert + value.slice(ctx.end);
    const nextCursor = ctx.start + picked.insert.length;
    onChange(next);
    setCursor(nextCursor);
    setHiddenAt(-1);
    requestAnimationFrame(() => {
      const ta = taRef.current;
      if (!ta) return;
      ta.focus();
      ta.setSelectionRange(nextCursor, nextCursor);
      sync();
    });
  };

  const insertText = (text) => {
    const ta = taRef.current;
    const start = ta?.selectionStart ?? cursor;
    const end = ta?.selectionEnd ?? cursor;
    const next = value.slice(0, start) + text + value.slice(end);
    const nextCursor = start + text.length;
    onChange(next);
    setCursor(nextCursor);
    requestAnimationFrame(() => taRef.current?.setSelectionRange(nextCursor, nextCursor));
  };

  const onKeyDown = (e) => {
    if (suggestions.length) {
      if (e.key === "ArrowDown") { e.preventDefault(); setActive((n) => (n + 1) % suggestions.length); return; }
      if (e.key === "ArrowUp") { e.preventDefault(); setActive((n) => (n - 1 + suggestions.length) % suggestions.length); return; }
      if (e.key === "Enter" || e.key === "Tab") { e.preventDefault(); applyCompletion(); return; }
      if (e.key === "Escape") { e.preventDefault(); setHiddenAt(cursor); return; }
    }
    if (e.key === "Tab") { e.preventDefault(); insertText("  "); }
  };

  const shared = {
    margin: 0,
    fontFamily: "var(--mono)",
    fontSize: 13,
    lineHeight: "1.7",
    padding: "18px 18px 18px 4px",
    tabSize: 2,
    whiteSpace: "pre",
    wordWrap: "off",
  };

  return (
    <div
      style={{
        display: "flex",
        flex: 1,
        minHeight: 0,
        background: "var(--panel-deep)",
        position: "relative",
        overflow: "hidden",
      }}
    >
      {/* gutter */}
      <pre
        ref={gutRef}
        aria-hidden="true"
        style={{
          ...shared,
          padding: "18px 12px 18px 18px",
          margin: 0,
          color: "var(--faint)",
          textAlign: "right",
          userSelect: "none",
          overflow: "hidden",
          background: "rgba(0,0,0,.18)",
          borderRight: "1px solid var(--line-soft)",
          minWidth: 46,
        }}
      >
        {gutter}
      </pre>

      <div style={{ position: "relative", flex: 1, minWidth: 0 }}>
        <pre
          className="no-scrollbar"
          ref={preRef}
          aria-hidden="true"
          style={{
            ...shared,
            position: "absolute",
            inset: 0,
            color: "var(--text)",
            overflow: "auto",
            pointerEvents: "none",
          }}
          dangerouslySetInnerHTML={{ __html: highlightNativ(value) + "\n" }}
        />
        <textarea
          className="no-scrollbar"
          ref={taRef}
          value={value}
          spellCheck="false"
          onChange={(e) => onChange(e.target.value)}
          onInput={(e) => { setCursor(e.currentTarget.selectionStart || 0); setHiddenAt(-1); }}
          onClick={rememberCursor}
          onKeyUp={rememberCursor}
          onKeyDown={onKeyDown}
          onFocus={() => { setFocused(true); rememberCursor(); }}
          onBlur={() => setFocused(false)}
          onScroll={sync}
          style={{
            ...shared,
            position: "absolute",
            inset: 0,
            width: "100%",
            height: "100%",
            border: "none",
            outline: "none",
            resize: "none",
            background: "transparent",
            color: "transparent",
            caretColor: "var(--swift)",
            overflow: "auto",
          }}
        />
        {suggestions.length > 0 && (
          <div
            style={{
              position: "absolute",
              top,
              left,
              zIndex: 8,
              width: 244,
              padding: 6,
              borderRadius: 12,
              border: "1px solid var(--line)",
              background: "rgba(13,15,20,.97)",
              boxShadow: "0 18px 48px rgba(0,0,0,.38)",
              fontFamily: "var(--mono)",
            }}
          >
            {suggestions.map((s, i) => {
              const on = i === active;
              const color = s.kind === "element" ? "var(--sx-f)" : s.kind === "modifier" ? "var(--sx-m)" : "var(--sx-k)";
              return (
                <button
                  key={s.label}
                  type="button"
                  onMouseEnter={() => setActive(i)}
                  onMouseDown={(e) => { e.preventDefault(); applyCompletion(s); }}
                  style={{
                    width: "100%",
                    display: "flex",
                    alignItems: "center",
                    gap: 10,
                    border: "none",
                    borderRadius: 8,
                    padding: "8px 9px",
                    background: on ? "rgba(255,255,255,.07)" : "transparent",
                    color: "var(--text)",
                    cursor: "pointer",
                    font: "inherit",
                    textAlign: "left",
                  }}
                >
                  <span style={{ color, minWidth: 78 }}>{s.label}</span>
                  <span style={{ color: "var(--faint)", fontSize: 11 }}>{s.detail}</span>
                </button>
              );
            })}
            <div style={{ padding: "5px 9px 3px", color: "var(--faint)", fontSize: 10.5 }}>
              Tab or Enter accepts · Esc closes
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { highlightNativ, SourceEditor });
