import {
  StreamLanguage,
  HighlightStyle,
  syntaxHighlighting,
} from "@codemirror/language";
import { tags } from "@lezer/highlight";

/**
 * CodeMirror 6 syntax highlighting for the µFrame .uf DSL.
 *
 * Keywords and values can be updated dynamically via setDslKeywords()
 * which is called when the frontend fetches /api/dsl-meta.
 */

// Mutable sets — updated from /api/dsl-meta
let KEYWORDS = new Set([
  // Layout
  "page", "box", "row", "col", "spacer", "pad",
  // Content
  "heading", "text", "label", "divider", "link", "list", "item",
  "bigtitle", "image", "dither", "invert", "caption",
  // Data
  "gauge", "sparkline", "status", "table", "columns",
  // Navigation
  "hnav", "vnav", "separator",
  // Form
  "form", "field", "radio", "checkbox", "button", "password",
  // Style
  "align", "color", "bg", "bold", "italic", "underline", "theme",
  // Dynamic
  "source", "let", "if", "elif", "else", "for", "cache", "state", "on_submit",
  "set", "append", "prepend",
  // Components
  "component", "use",
]);

let WEIGHT_VALS = new Set([
  // Border weights
  "light", "heavy", "double", "rounded",
  // List styles
  "bullet", "dash", "number", "arrow",
  // Alignment
  "left", "center", "right",
  // Status states
  "online", "offline", "degraded", "unknown", "alert",
  // Source types
  "shell", "file", "json", "python", "rns", "param",
  // Themes
  "default", "nouveau", "gothic", "bamboo", "circuit", "brutalist",
  // Nav styles
  "bar", "tabs", "pills", "breadcrumb", "underline",
  "boxed", "tree", "sidebar", "minimal",
  // Fonts
  "block", "thin", "pixel",
  // Image modes
  "braille", "ascii", "halfblock",
  // Dither
  "floyd", "threshold", "none",
  // Divider styles
  "dot",
  // Misc
  "active", "compact",
]);

/** Update keywords and values from /api/dsl-meta response. */
export function setDslKeywords(keywords: string[], values: string[]) {
  if (keywords.length > 0) KEYWORDS = new Set(keywords);
  if (values.length > 0) WEIGHT_VALS = new Set(values);
}

const uframeLanguage = StreamLanguage.define({
  token(stream) {
    // Comments
    if (stream.sol() && stream.match(/\s*#/)) {
      stream.skipToEnd();
      return "lineComment";
    }

    // Skip whitespace
    if (stream.eatSpace()) return null;

    // Quoted strings
    if (stream.match(/"/)) {
      while (!stream.eol()) {
        if (stream.next() === '"') break;
      }
      return "string";
    }

    // @modifier{} syntax
    if (stream.match(/@\w+/)) return "keyword";

    // $variable references
    if (stream.match(/\$[\w.]+/)) return "variableName";

    // Numbers (integers, floats, hex colors)
    if (stream.match(/\b\d+(\.\d+)?\b/)) return "number";

    // warn=N, crit=N parameters
    if (stream.match(/\b(warn|crit)=/)) return "attributeName";

    // Pipe separator for inline lists
    if (stream.match(/\|/)) return "punctuation";

    // Keywords and values
    if (stream.match(/\b\w+\b/)) {
      const word = stream.current();
      if (KEYWORDS.has(word)) return "keyword";
      if (WEIGHT_VALS.has(word)) return "atom";
      return null;
    }

    stream.next();
    return null;
  },
  startState: () => ({}),
  copyState: (s) => ({ ...s }),
  blankLine: () => {},
  languageData: {},
});

const uframeStyle = HighlightStyle.define([
  { tag: tags.keyword, color: "#c792ea", fontWeight: "bold" },
  { tag: tags.string, color: "#c3e88d" },
  { tag: tags.lineComment, color: "#546e7a", fontStyle: "italic" },
  { tag: tags.variableName, color: "#f78c6c" },
  { tag: tags.number, color: "#f78c6c" },
  { tag: tags.atom, color: "#89ddff" },
  { tag: tags.attributeName, color: "#ffcb6b" },
  { tag: tags.punctuation, color: "#89ddff" },
]);

export function uframeHighlight() {
  return [uframeLanguage, syntaxHighlighting(uframeStyle)];
}
