//! Thread-safe grammar store for caching compiled grammars.
//!
//! The `GrammarStore` holds compiled grammars that can be shared across threads.
//! Each grammar is compiled once and cached for reuse.
//!
//! # Generated Code
//!
//! This file is automatically generated from the grammar registry
//! (`langs/group-*/*/def/arborium.kdl`). Do not edit manually.

use std::collections::HashMap;
use std::sync::{Arc, RwLock};

#[allow(unused_imports)]
use arborium_highlight::tree_sitter::{CompiledGrammar, GrammarConfig};

/// Thread-safe cache of compiled grammars.
///
/// Grammars are compiled on first access and cached. The store can be shared
/// across threads via `Arc<GrammarStore>`.
///
/// # Example
///
/// ```rust,ignore
/// use std::sync::Arc;
///
/// // Create store (automatically populated with available grammars)
/// let store = Arc::new(GrammarStore::new());
///
/// // Share across threads
/// let store2 = store.clone();
/// std::thread::spawn(move || {
///     let grammar = store2.get("rust").unwrap();
///     // Use grammar...
/// });
/// ```
pub struct GrammarStore {
    grammars: RwLock<HashMap<String, Arc<CompiledGrammar>>>,
}

impl Default for GrammarStore {
    fn default() -> Self {
        Self::new()
    }
}

impl GrammarStore {
    /// Create a new empty grammar store.
    pub fn new() -> Self {
        Self {
            grammars: RwLock::new(HashMap::new()),
        }
    }

    /// Get a grammar by language name, compiling and caching it if needed.
    ///
    /// Returns `None` if the language is not supported.
    pub fn get(&self, language: &str) -> Option<Arc<CompiledGrammar>> {
        let normalized = Self::normalize_language(language);

        // Fast path: check if already cached
        {
            let grammars = self.grammars.read().unwrap();
            if let Some(grammar) = grammars.get(normalized) {
                return Some(grammar.clone());
            }
        }

        // Slow path: compile and cache
        let grammar = Self::compile_grammar(normalized)?;
        let grammar = Arc::new(grammar);

        {
            let mut grammars = self.grammars.write().unwrap();
            // Double-check in case another thread compiled it
            if let Some(existing) = grammars.get(normalized) {
                return Some(existing.clone());
            }
            grammars.insert(normalized.to_string(), grammar.clone());
        }

        Some(grammar)
    }

    /// Normalize a language name to its canonical form.
    fn normalize_language(language: &str) -> &'static str {
        match language {
            // Aliases (generated from arborium.kdl)
            "adoc" => "asciidoc",

            "assembly" => "asm",

            "bat" => "batch",

            "bazel" => "starlark",

            "bzl" => "starlark",

            "c++" => "cpp",

            "cfg" => "ini",

            "cjs" => "javascript",

            "cl" => "commonlisp",

            "clj" => "clojure",

            "cmd" => "batch",

            "conf" => "ini",

            "cs" => "c-sharp",

            "csharp" => "c-sharp",

            "cts" => "typescript",

            "cxx" => "cpp",

            "dlang" => "d",

            "docker" => "dockerfile",

            "el" => "elisp",

            "emacs-lisp" => "elisp",

            "erl" => "erlang",

            "ex" => "elixir",

            "exs" => "elixir",

            "f#" => "fsharp",

            "frag" => "glsl",

            "fs" => "fsharp",

            "golang" => "go",

            "gql" => "graphql",

            "h" => "c",

            "hpp" => "cpp",

            "hs" => "haskell",

            "htm" => "html",

            "idr" => "idris",

            "j2" => "jinja2",

            "jinja" => "jinja2",

            "jl" => "julia",

            "js" => "javascript",

            "jsonc" => "json",

            "jsx" => "javascript",

            "kt" => "kotlin",

            "kts" => "kotlin",

            "lisp" => "commonlisp",

            "m" => "matlab",

            "md" => "markdown",

            "mdx" => "markdown",

            "mjs" => "javascript",

            "ml" => "ocaml",

            "mm" => "objc",

            "mts" => "typescript",

            "mysql" => "sql",

            "nasm" => "x86asm",

            "objective-c" => "objc",

            "patch" => "diff",

            "pbtxt" => "textproto",

            "pl" => "perl",

            "pm" => "perl",

            "postgres" => "sql",

            "postgresql" => "sql",

            "pro" => "prolog",

            "ps" => "postscript",

            "ps1" => "powershell",

            "pwsh" => "powershell",

            "py" => "python",

            "py3" => "python",

            "python3" => "python",

            "rb" => "ruby",

            "res" => "rescript",

            "rkt" => "scheme",

            "rlang" => "r",

            "rq" => "sparql",

            "rs" => "rust",

            "sass" => "scss",

            "scm" => "query",

            "sh" => "bash",

            "shell" => "bash",

            "sqlite" => "sql",

            "ss" => "scheme",

            "sv" => "verilog",

            "svg" => "xml",

            "systemverilog" => "verilog",

            "terraform" => "hcl",

            "textpb" => "textproto",

            "tf" => "hcl",

            "tla" => "tlaplus",

            "ts" => "typescript",

            "typ" => "typst",

            "ua" => "uiua",

            "v" => "verilog",

            "vbnet" => "vb",

            "vert" => "glsl",

            "vhd" => "vhdl",

            "viml" => "vim",

            "vimscript" => "vim",

            "visualbasic" => "vb",

            "x86" => "x86asm",

            "xsl" => "xml",

            "xslt" => "xml",

            "yml" => "yaml",

            // Known canonical IDs pass through as-is
            // (leak the string to get &'static str - this is fine since language names are finite)
            _ => Box::leak(language.to_string().into_boxed_str()),
        }
    }

    /// Compile a grammar for a language.
    #[allow(unused_variables)]
    fn compile_grammar(language: &str) -> Option<CompiledGrammar> {
        macro_rules! try_lang {
            ($feature:literal, $module:ident, $primary:literal) => {
                #[cfg(feature = $feature)]
                if language == $primary {
                    let config = GrammarConfig {
                        language: crate::$module::language().into(),
                        highlights_query: &crate::$module::HIGHLIGHTS_QUERY,
                        injections_query: crate::$module::INJECTIONS_QUERY,
                        locals_query: crate::$module::LOCALS_QUERY,
                    };
                    return CompiledGrammar::new(config).ok();
                }
            };
        }

        // All languages (generated from arborium.kdl)

        try_lang!("lang-ada", lang_ada, "ada");

        try_lang!("lang-agda", lang_agda, "agda");

        try_lang!("lang-asciidoc", lang_asciidoc, "asciidoc");

        try_lang!("lang-asm", lang_asm, "asm");

        try_lang!("lang-awk", lang_awk, "awk");

        try_lang!("lang-bash", lang_bash, "bash");

        try_lang!("lang-batch", lang_batch, "batch");

        try_lang!("lang-c", lang_c, "c");

        try_lang!("lang-c-sharp", lang_c_sharp, "c-sharp");

        try_lang!("lang-caddy", lang_caddy, "caddy");

        try_lang!("lang-capnp", lang_capnp, "capnp");

        try_lang!("lang-clojure", lang_clojure, "clojure");

        try_lang!("lang-cmake", lang_cmake, "cmake");

        try_lang!("lang-commonlisp", lang_commonlisp, "commonlisp");

        try_lang!("lang-cpp", lang_cpp, "cpp");

        try_lang!("lang-css", lang_css, "css");

        try_lang!("lang-d", lang_d, "d");

        try_lang!("lang-dart", lang_dart, "dart");

        try_lang!("lang-devicetree", lang_devicetree, "devicetree");

        try_lang!("lang-diff", lang_diff, "diff");

        try_lang!("lang-dockerfile", lang_dockerfile, "dockerfile");

        try_lang!("lang-dot", lang_dot, "dot");

        try_lang!("lang-elisp", lang_elisp, "elisp");

        try_lang!("lang-elixir", lang_elixir, "elixir");

        try_lang!("lang-elm", lang_elm, "elm");

        try_lang!("lang-erlang", lang_erlang, "erlang");

        try_lang!("lang-fish", lang_fish, "fish");

        try_lang!("lang-fsharp", lang_fsharp, "fsharp");

        try_lang!("lang-gleam", lang_gleam, "gleam");

        try_lang!("lang-glsl", lang_glsl, "glsl");

        try_lang!("lang-go", lang_go, "go");

        try_lang!("lang-graphql", lang_graphql, "graphql");

        try_lang!("lang-haskell", lang_haskell, "haskell");

        try_lang!("lang-hcl", lang_hcl, "hcl");

        try_lang!("lang-hlsl", lang_hlsl, "hlsl");

        try_lang!("lang-html", lang_html, "html");

        try_lang!("lang-idris", lang_idris, "idris");

        try_lang!("lang-ini", lang_ini, "ini");

        try_lang!("lang-java", lang_java, "java");

        try_lang!("lang-javascript", lang_javascript, "javascript");

        try_lang!("lang-jinja2", lang_jinja2, "jinja2");

        try_lang!("lang-jq", lang_jq, "jq");

        try_lang!("lang-json", lang_json, "json");

        try_lang!("lang-julia", lang_julia, "julia");

        try_lang!("lang-kdl", lang_kdl, "kdl");

        try_lang!("lang-kotlin", lang_kotlin, "kotlin");

        try_lang!("lang-lean", lang_lean, "lean");

        try_lang!("lang-lua", lang_lua, "lua");

        try_lang!("lang-markdown", lang_markdown, "markdown");

        try_lang!("lang-matlab", lang_matlab, "matlab");

        try_lang!("lang-meson", lang_meson, "meson");

        try_lang!("lang-nginx", lang_nginx, "nginx");

        try_lang!("lang-ninja", lang_ninja, "ninja");

        try_lang!("lang-nix", lang_nix, "nix");

        try_lang!("lang-objc", lang_objc, "objc");

        try_lang!("lang-ocaml", lang_ocaml, "ocaml");

        try_lang!("lang-perl", lang_perl, "perl");

        try_lang!("lang-php", lang_php, "php");

        try_lang!("lang-postscript", lang_postscript, "postscript");

        try_lang!("lang-powershell", lang_powershell, "powershell");

        try_lang!("lang-prolog", lang_prolog, "prolog");

        try_lang!("lang-python", lang_python, "python");

        try_lang!("lang-query", lang_query, "query");

        try_lang!("lang-r", lang_r, "r");

        try_lang!("lang-rescript", lang_rescript, "rescript");

        try_lang!("lang-ron", lang_ron, "ron");

        try_lang!("lang-ruby", lang_ruby, "ruby");

        try_lang!("lang-rust", lang_rust, "rust");

        try_lang!("lang-scala", lang_scala, "scala");

        try_lang!("lang-scheme", lang_scheme, "scheme");

        try_lang!("lang-scss", lang_scss, "scss");

        try_lang!("lang-sparql", lang_sparql, "sparql");

        try_lang!("lang-sql", lang_sql, "sql");

        try_lang!("lang-ssh-config", lang_ssh_config, "ssh-config");

        try_lang!("lang-starlark", lang_starlark, "starlark");

        try_lang!("lang-svelte", lang_svelte, "svelte");

        try_lang!("lang-swift", lang_swift, "swift");

        try_lang!("lang-textproto", lang_textproto, "textproto");

        try_lang!("lang-thrift", lang_thrift, "thrift");

        try_lang!("lang-tlaplus", lang_tlaplus, "tlaplus");

        try_lang!("lang-toml", lang_toml, "toml");

        try_lang!("lang-tsx", lang_tsx, "tsx");

        try_lang!("lang-typescript", lang_typescript, "typescript");

        try_lang!("lang-typst", lang_typst, "typst");

        try_lang!("lang-uiua", lang_uiua, "uiua");

        try_lang!("lang-vb", lang_vb, "vb");

        try_lang!("lang-verilog", lang_verilog, "verilog");

        try_lang!("lang-vhdl", lang_vhdl, "vhdl");

        try_lang!("lang-vim", lang_vim, "vim");

        try_lang!("lang-vue", lang_vue, "vue");

        try_lang!("lang-x86asm", lang_x86asm, "x86asm");

        try_lang!("lang-xml", lang_xml, "xml");

        try_lang!("lang-yaml", lang_yaml, "yaml");

        try_lang!("lang-yuri", lang_yuri, "yuri");

        try_lang!("lang-zig", lang_zig, "zig");

        try_lang!("lang-zsh", lang_zsh, "zsh");

        None
    }
}
