/**
 * Ace Editor Mode for CheckXML Custom Script Language
 * Combines C# syntax with XmlObject custom functions
 * Version 5.2 - Auto-complete on dot, underline errors, missing semicolon detection
 *
 * @author CheckXML Team
 * @version 5.2
 * @description Hỗ trợ soạn thảo custom code tương thích với Core.XmlObject
 *              - Autocomplete tự động khi nhấn dấu '.'
 *              - Underline đỏ (wavy) cho lỗi: thiếu ';', sai hàm, mã độc
 *              - Underline cam (wavy) cho cảnh báo: lỗi chính tả, field không tồn tại
 *              - Validation toàn diện: cú pháp, hàm, field, XPath
 *              - Phát hiện lỗi chính tả với gợi ý sửa (Levenshtein)
 *              - Kiểm tra field access cho XML1-5
 *              - Phát hiện mã độc hại theo IsMaliciousCode
 *              - Tooltip documentation đẹp mắt với ví dụ
 */

// ============================================
// AUTOCOMPLETE DATA - Full Function Definitions with Documentation
// ============================================

/**
 * @typedef {Object} CompletionEntry
 * @property {string} name
 * @property {string} [meta]
 * @property {number} [score]
 * @property {string} [snippet]
 * @property {string} [doc]
 */

/**
 * @typedef {Object} XmlVariableEntry
 * @property {string} name
 * @property {string} meta
 * @property {number} [score]
 * @property {string} [doc]
 */

/**
 * @typedef {Object} CheckXmlCompletionsData
 * @property {CompletionEntry[]} xmlObjectFunctions
 * @property {CompletionEntry[]} stringFunctions
 * @property {CompletionEntry[]} colorFunctions
 * @property {CompletionEntry[]} linqFunctions
 * @property {XmlVariableEntry[]} xmlVariables
 * @property {CompletionEntry[]} xml1Fields
 */

/** @type {CheckXmlCompletionsData} */
var checkXmlCompletions = {
    // ========== XmlObject Core Functions ==========
    /** @type {CompletionEntry[]} */
    xmlObjectFunctions: [
        // XPath Functions
        { 
            name: "Xpath", 
            meta: "XmlObject", 
            score: 1000, 
            snippet: 'Xpath("${1:xpathExpression}")',
            doc: "Xpath(string xpathExpression) → XmlNode?\n\n" +
                 "Truy vấn XPath trả về node đầu tiên khớp.\n\n" +
                 "Params:\n" +
                 "  • xpathExpression (string): Biểu thức XPath\n\n" +
                 "Returns: XmlNode? - Node đầu tiên hoặc null\n\n" +
                 "Ví dụ:\n" +
                 "  Xpath('//TONG_HOP/MA_LK')\n" +
                 "  Xpath('//CHI_TIET_THUOC[MA_NHOM=4]')"
        },
        { 
            name: "Xpaths", 
            meta: "XmlObject", 
            score: 1000, 
            snippet: 'Xpath("${1:xpathExpression}")',
            doc: "Xpaths(string xpathExpression) → XmlNodeList?\n\n" +
                 "Truy vấn XPath trả về danh sách tất cả nodes khớp.\n" +
                 "Kết quả được lưu vào NodesSet để dùng với ShowTable.\n\n" +
                 "Params:\n" +
                 "  • xpathExpression (string): Biểu thức XPath\n\n" +
                 "Returns: XmlNodeList? - Danh sách nodes\n\n" +
                 "Ví dụ:\n" +
                 "  Xpaths('//CHI_TIET_THUOC')\n" +
                 "  var thuocs = Xpaths('//CHI_TIET_THUOC[SO_LUONG>10]')"
        },
        // Val Functions
        { 
            name: "Value", 
            meta: "Property", 
            score: 960, 
            doc: ".Value → string | decimal | int\n\n" +
                 "Lấy giá trị của Field (StringField/DecimalField/IntField).\n\n" +
                 "Overloads:\n" +
                 "  • StringField.Value → string\n" +
                 "  • DecimalField.Value → decimal\n" +
                 "  • IntField.Value → int\n" +
                 "  • XmlNode.Value → string\n" +
                 "  • object.Value → object\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.MA_LK.Value" 
                  
        }, {
            name: "Val",
            meta: "Property",
            score: 950,
            doc: ".Val → string | decimal | int\n\n" +
                "Lấy giá trị của Field (StringField/DecimalField/IntField).\n\n" +
                "Overloads:\n" +
                "  • StringField.Val → string\n" +
                "  • DecimalField.Val → decimal\n" +
                "  • IntField.Val → int\n" +
                "  • XmlNode.Val → string\n" +
                "  • object.Val → object\n\n" +
                "Ví dụ:\n" +
                "  XML1.MA_LK.Val\n" +
                "  XML2[0].SO_LUONG.Val"
        }, 
        { 
            name: "Text", 
            meta: "Extension", 
            score: 950, 
            doc: ".Text() → string?\n\n" +
                 "Lấy InnerText của XmlNode.\n\n" +
                 "Params: Không\n\n" +
                 "Returns: string? - Nội dung text hoặc null\n\n" +
                 "Ví dụ:\n" +
                 "  Xpath('//MA_LK').Text()\n" +
                 "  node.Text()"
        },
        // Display Functions  
        { 
            name: "ShowTable", 
            meta: "Display", 
            score: 900, 
            snippet: "ShowTable(${1:list}, ${2:'COL1'}, ${3:'COL2'})",
            doc: "ShowTable(dynamic list, params string[] columns) → string\n\n" +
                 "Hiển thị dữ liệu dạng bảng HTML.\n" +
                 "Tự động format ngày (NGAY_*) và tiền (T_*).\n\n" +
                 "Params:\n" +
                 "  • list: List<T> hoặc XmlNodeList\n" +
                 "  • columns: Danh sách tên cột cần hiển thị\n\n" +
                 "Returns: string - HTML table\n\n" +
                 "Columns đặc biệt:\n" +
                 "  • TEN_BAC_SI: Tự lookup tên từ MA_BAC_SI\n" +
                 "  • TEN_NGUOI_THUC_HIEN: Tự lookup từ NGUOI_THUC_HIEN\n\n" +
                 "Ví dụ:\n" +
                 "  ShowTable(XML2, 'TEN_THUOC', 'SO_LUONG', 'DON_GIA')\n" +
                 "  ShowTable(NodesSet, 'MA_DICH_VU', 'TEN_DICH_VU')"
        },
        { 
            name: "Show", 
            meta: "Display", 
            score: 900, 
            snippet: "Show(${1:item}, ${2:'COL1'}, ${3:'COL2'})",
            doc: ".Show(params string[] columns) → string\n\n" +
                 "Hiển thị một item dạng bảng HTML (1 dòng).\n\n" +
                 "Params:\n" +
                 "  • columns: Danh sách tên cột cần hiển thị\n\n" +
                 "Returns: string - HTML table với 1 dòng\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.Show('MA_LK', 'HO_TEN', 'NGAY_VAO')\n" +
                 "  item.Show('TEN_THUOC', 'THANH_TIEN_BV')"
        },
        { 
            name: "showtable", 
            meta: "Display", 
            score: 890, 
            snippet: ".showtable(${1:'COL1'}, ${2:'COL2'})",
            doc: ".showtable(params string[] columns) → string\n\n" +
                 "Extension method hiển thị list dạng bảng HTML.\n\n" +
                 "Params:\n" +
                 "  • columns: Danh sách tên cột\n\n" +
                 "Returns: string - HTML table\n\n" +
                 "Ví dụ:\n" +
                 "  XML2.showtable('TEN_THUOC', 'SO_LUONG')\n" +
                 "  XML3.Where(x => x.MA_NHOM.Val == 4).ToList().showtable('TEN_DICH_VU')"
        },
        // Lookup Functions
        { 
            name: "TLookUp", 
            meta: "Lookup", 
            score: 850, 
            snippet: "TLookUp(${1:list}, ${2:'conditionExp'})",
            doc: ".TLookUp<T>(string conditionExp) → List<T>\n\n" +
                 "Lọc danh sách theo điều kiện (parallel processing).\n\n" +
                 "Params:\n" +
                 "  • conditionExp (string): Biểu thức điều kiện C#\n\n" +
                 "Returns: List<T> - Danh sách thỏa điều kiện\n\n" +
                 "Ví dụ:\n" +
                 "  XML2.TLookUp('MA_NHOM == 4')\n" +
                 "  XML3.TLookUp('SO_LUONG > 10 && DON_GIA > 1000')"
        },
        { 
            name: "VlookUp", 
            meta: "Lookup", 
            score: 850, 
            snippet: "VlookUp(${1:list}, ${2:valueLookUp}, ${3:'fieldLookUp'}, ${4:'fieldReturn'})",
            doc: ".VlookUp<T>(object valueLookUp, string fieldLookUp, string fieldReturn) → T?\n\n" +
                 "Tìm item đầu tiên có field khớp giá trị.\n\n" +
                 "Params:\n" +
                 "  • valueLookUp: Giá trị cần tìm\n" +
                 "  • fieldLookUp: Tên field để so sánh\n" +
                 "  • fieldReturn: Tên field trả về (unused)\n\n" +
                 "Returns: T? - Item đầu tiên khớp hoặc null\n\n" +
                 "Ví dụ:\n" +
                 "  XML2.VlookUp(maThuoc, 'MA_THUOC', 'TEN_THUOC')"
        },
        // Aggregate Functions
        { 
            name: "Sum", 
            meta: "Aggregate", 
            score: 800, 
            snippet: "Sum(${1:list}, ${2:'fieldName'})",
            doc: ".Sum<T>(string field) → decimal\n.Sum(XmlNodeList nodeList, string field) → decimal\n\n" +
                 "Tính tổng giá trị của một field trong list.\n\n" +
                 "Params:\n" +
                 "  • field (string): Tên field cần tính tổng\n\n" +
                 "Returns: decimal - Tổng giá trị\n\n" +
                 "Ví dụ:\n" +
                 "  XML2.Sum('THANH_TIEN_BV')\n" +
                 "  Xpaths('//CHI_TIET_THUOC').Sum('T_BHTT')"
        },
        { 
            name: "SumIf", 
            meta: "Aggregate", 
            score: 800, 
            snippet: "SumIf(${1:list}, ${2:'fieldSum'}, ${3:'conditionExp'})",
            doc: ".SumIf<T>(string field, string conditionExp) → decimal\n\n" +
                 "Tính tổng có điều kiện (dynamic expression).\n" +
                 "Tự động xử lý .Value cho các Field types.\n\n" +
                 "Params:\n" +
                 "  • field (string): Field cần tính tổng\n" +
                 "  • conditionExp (string): Biểu thức điều kiện\n\n" +
                 "Returns: decimal - Tổng các giá trị thỏa điều kiện\n\n" +
                 "Ví dụ:\n" +
                 "  XML2.SumIf('THANH_TIEN_BV', 'MA_NHOM.Value == 4')\n" +
                 "  XML3.SumIf('T_BHTT', 'DON_GIA.Value > 1000')"
        },
        { 
            name: "SumIf2", 
            meta: "Aggregate", 
            score: 790, 
            snippet: "SumIf2(${1:list}, ${2:'fieldSum'}, ${3:'conditionExp'})",
            doc: ".SumIf2<T>(string field, string conditionExp) → decimal\n\n" +
                 "Tính tổng có điều kiện (Exec evaluation).\n\n" +
                 "Params:\n" +
                 "  • field (string): Field cần tính tổng\n" +
                 "  • conditionExp (string): Biểu thức điều kiện\n\n" +
                 "Returns: decimal - Tổng các giá trị thỏa điều kiện\n\n" +
                 "Ví dụ:\n" +
                 "  XML2.SumIf2('THANH_TIEN_BV', 'MA_NHOM == 4')"
        },
        { 
            name: "CountIf", 
            meta: "Aggregate", 
            score: 800, 
            snippet: "CountIf(${1:list}, ${2:'conditionExp'})",
            doc: ".CountIf<T>(string conditionExp) → int\n\n" +
                 "Đếm số phần tử thỏa điều kiện.\n\n" +
                 "Params:\n" +
                 "  • conditionExp (string): Biểu thức điều kiện\n\n" +
                 "Returns: int - Số lượng thỏa điều kiện\n\n" +
                 "Ví dụ:\n" +
                 "  XML2.CountIf('MA_NHOM == 4')\n" +
                 "  XML3.CountIf('SO_LUONG > 10')"
        },
        { 
            name: "Length", 
            meta: "Aggregate", 
            score: 750, 
            doc: ".Length → int\n.Length(XmlNodeList nodeList) → int\n\n" +
                 "Đếm số phần tử trong XmlNodeList hoặc độ dài string.\n\n" +
                 "Returns: int - Số lượng phần tử\n\n" +
                 "Ví dụ:\n" +
                 "  Xpaths('//CHI_TIET_THUOC').Length\n" +
                 "  XML1.MA_LK.Val.Length"
        },
        // Script Execution
        { 
            name: "Execute", 
            meta: "Script", 
            score: 700, 
            snippet: "Execute(${1:'code'})",
            doc: "Execute(string code) → object?\n\n" +
                 "Thực thi đoạn code C# động.\n" +
                 "⚠️ Có giới hạn 10,000 ký tự và kiểm tra mã độc.\n\n" +
                 "Params:\n" +
                 "  • code (string): Đoạn code C# cần thực thi\n\n" +
                 "Returns: object? - Kết quả thực thi\n\n" +
                 "Ví dụ:\n" +
                 "  Execute('1 + 2')\n" +
                 "  Execute('true && false')"
        },
        { 
            name: "Exec", 
            meta: "Script", 
            score: 700, 
            snippet: ".Exec(${1:'conditionExp'})",
            doc: ".Exec<T>(string conditionExp) → bool\n\n" +
                 "Thực thi biểu thức điều kiện trên object.\n" +
                 "Tự động thay thế property names bằng giá trị.\n\n" +
                 "Params:\n" +
                 "  • conditionExp (string): Biểu thức điều kiện\n\n" +
                 "Returns: bool - Kết quả điều kiện\n\n" +
                 "Ví dụ:\n" +
                 "  item.Exec('MA_NHOM == 4')\n" +
                 "  thuoc.Exec('SO_LUONG > 10 && DON_GIA > 1000')"
        },
        // Config & Check
        { 
            name: "Config", 
            meta: "System", 
            score: 750, 
            snippet: "Config(${1:'key'})",
            doc: "Config(string key) → string\n\n" +
                 "Lấy giá trị cấu hình hệ thống.\n\n" +
                 "Params:\n" +
                 "  • key (string): Khóa cấu hình\n\n" +
                 "Returns: string - Giá trị cấu hình\n\n" +
                 "Keys thông dụng:\n" +
                 "  • MA_CSKCB: Mã cơ sở KCB\n" +
                 "  • TEN_CSKCB: Tên cơ sở KCB\n\n" +
                 "Ví dụ:\n" +
                 "  Config('MA_CSKCB')"
        },
        { 
            name: "IsCheck", 
            meta: "System", 
            score: 750, 
            snippet: "IsCheck(${1:'ruleId'})",
            doc: "IsCheck(string ruleId) → bool\n\n" +
                 "Kiểm tra rule đã được enable/check chưa.\n\n" +
                 "Params:\n" +
                 "  • ruleId (string): Mã rule cần kiểm tra\n\n" +
                 "Returns: bool - true nếu rule đang active\n\n" +
                 "Ví dụ:\n" +
                 "  if(IsCheck('RULE001')) { ... }"
        },
        // Medical Check Functions
        { 
            name: "IsPTTT", 
            meta: "Medical", 
            score: 700, 
            doc: ".IsPTTT(XmlDocument hoso) → bool\n\n" +
                 "Kiểm tra hồ sơ có phẫu thuật/thủ thuật không.\n" +
                 "(MA_NHOM = 8 hoặc 18)\n\n" +
                 "Params:\n" +
                 "  • hoso: XmlDocument của hồ sơ\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  HOSO.IsPTTT()"
        },
        { 
            name: "IsPT", 
            meta: "Medical", 
            score: 700, 
            doc: ".IsPT(XmlDocument hoso) → bool\n\n" +
                 "Kiểm tra hồ sơ có phẫu thuật không (MA_NHOM = 8).\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  HOSO.IsPT()"
        },
        { 
            name: "IsTT", 
            meta: "Medical", 
            score: 700, 
            doc: ".IsTT(XmlDocument hoso) → bool\n\n" +
                 "Kiểm tra hồ sơ có thủ thuật không (MA_NHOM = 18).\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  HOSO.IsTT()"
        },
        { 
            name: "IsTruocPT", 
            meta: "Medical", 
            score: 700, 
            doc: ".IsTruocPT(XML130/XML4210 hoso, CHI_TIET_* item) → bool\n\n" +
                 "Kiểm tra item có trước thời điểm phẫu thuật không.\n\n" +
                 "Overloads:\n" +
                 "  • IsTruocPT(XML130, CHI_TIET_DVKT_130)\n" +
                 "  • IsTruocPT(XML130, CHI_TIET_THUOC_130)\n" +
                 "  • IsTruocPT(XML4210, CHI_TIET_DVKT_4210)\n" +
                 "  • IsTruocPT(XML4210, CHI_TIET_THUOC_4210)\n\n" +
                 "Returns: bool - true nếu trước PT\n\n" +
                 "Ví dụ:\n" +
                 "  XML130.IsTruocPT(thuocItem)"
        },
        { 
            name: "IsSauPT", 
            meta: "Medical", 
            score: 700, 
            doc: ".IsSauPT(XML130/XML4210 hoso, CHI_TIET_* item) → bool\n\n" +
                 "Kiểm tra item có sau thời điểm phẫu thuật không.\n\n" +
                 "Returns: bool - true nếu sau PT\n\n" +
                 "Ví dụ:\n" +
                 "  XML130.IsSauPT(dvktItem)"
        },
        // String Functions from Xobj
        { 
            name: "ReplaceFirst", 
            meta: "StringExt", 
            score: 700, 
            snippet: ".ReplaceFirst(${1:'find'}, ${2:'replace'})",
            doc: ".ReplaceFirst(string find, string replace) → string\n\n" +
                 "Thay thế lần xuất hiện đầu tiên của chuỗi.\n\n" +
                 "Params:\n" +
                 "  • find (string): Chuỗi cần tìm\n" +
                 "  • replace (string): Chuỗi thay thế\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'abc;def;abc'.ReplaceFirst('abc', 'xyz') // 'xyz;def;abc'"
        },
        { 
            name: "Casentive", 
            meta: "StringExt", 
            score: 650, 
            doc: ".Casentive() → string\n\n" +
                 "Viết hoa chữ cái đầu, còn lại viết thường.\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'NGUYEN VAN A'.Casentive() // 'Nguyen van a'"
        },
        { 
            name: "StringInStr", 
            meta: "StringExt", 
            score: 700, 
            snippet: ".StringInStr(${1:'searchValue'})",
            doc: ".StringInStr(string val) → bool\n\n" +
                 "Kiểm tra chuỗi có chứa giá trị không (Contains).\n\n" +
                 "Params:\n" +
                 "  • val (string): Chuỗi cần tìm\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.MA_BENH.Val.StringInStr('E11')"
        },
        { 
            name: "StringExist", 
            meta: "StringExt", 
            score: 700, 
            snippet: "StringExist(${1:selfVal}, ${2:'pattern'})",
            doc: "StringExist(string selfVal, string pattern, string val2 = '') → bool\n\n" +
                 "Kiểm tra chuỗi có chứa pattern (hỗ trợ AND/OR logic).\n" +
                 "Dấu phẩy = AND, dấu chấm phẩy = OR.\n\n" +
                 "Params:\n" +
                 "  • selfVal (string): Chuỗi gốc\n" +
                 "  • pattern (string): Pattern cần kiểm tra\n" +
                 "  • val2 (string): Chuỗi thay thế (optional)\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  StringExist(maBenh, 'E10,E11;E12') // (E10 AND E11) OR E12"
        },
        { 
            name: "StringReplace", 
            meta: "StringExt", 
            score: 700, 
            snippet: ".StringReplace(${1:'old'}, ${2:'new'}, ${3:count})",
            doc: ".StringReplace(string oldValue, string newValue, int count) → string\n\n" +
                 "Thay thế chuỗi với giới hạn số lần.\n\n" +
                 "Params:\n" +
                 "  • oldValue (string): Chuỗi cần thay\n" +
                 "  • newValue (string): Chuỗi mới\n" +
                 "  • count (int): Số lần thay tối đa\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'a;b;a;c'.StringReplace('a', 'x', 1) // 'x;b;a;c'"
        },
        { 
            name: "IsNumber", 
            meta: "StringExt", 
            score: 650, 
            doc: ".IsNumber() → bool\n\n" +
                 "Kiểm tra chuỗi có phải toàn số không.\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  '12345'.IsNumber() // true\n" +
                 "  '123.45'.IsNumber() // false"
        },
    ],
    
    // ========== String Extension Functions (từ StringExtentions.cs) ==========
    /** @type {CompletionEntry[]} */
    stringFunctions: [
        // Type Conversion
        { 
            name: "ToDecimal", 
            meta: "Convert", 
            score: 900, 
            doc: ".ToDecimal() → decimal\n\n" +
                 "Chuyển object sang decimal.\n" +
                 "Tự động xử lý dấu . và , làm decimal separator.\n" +
                 "Trả về 0 nếu không parse được.\n\n" +
                 "Returns: decimal\n\n" +
                 "Ví dụ:\n" +
                 "  '123.45'.ToDecimal() // 123.45\n" +
                 "  '1,234.56'.ToDecimal() // 1234.56\n" +
                 "  '1.234,56'.ToDecimal() // 1234.56"
        },
        { 
            name: "ToInt", 
            meta: "Convert", 
            score: 900, 
            doc: ".ToInt() → int\n\n" +
                 "Chuyển object sang int (truncate decimal).\n" +
                 "Trả về 0 nếu không parse được.\n\n" +
                 "Returns: int\n\n" +
                 "Ví dụ:\n" +
                 "  '123'.ToInt() // 123\n" +
                 "  '123.99'.ToInt() // 123"
        },
        { 
            name: "ToDouble", 
            meta: "Convert", 
            score: 900, 
            doc: ".ToDouble() → double\n\n" +
                 "Chuyển string sang double.\n" +
                 "Trả về 0.0 nếu không parse được.\n\n" +
                 "Returns: double\n\n" +
                 "Ví dụ:\n" +
                 "  '123.45'.ToDouble()"
        },
        // Date Functions
        { 
            name: "Date", 
            meta: "DateTime", 
            score: 850, 
            doc: ".Date() → DateTime?\n\n" +
                 "Tự động parse string sang DateTime.\n" +
                 "Hỗ trợ nhiều format: yyyyMMddHHmm, ddMMyyyyHHmm,\n" +
                 "yyyyMMdd, dd/MM/yyyy, yyyy00000000, etc.\n\n" +
                 "Returns: DateTime? - null nếu không parse được\n\n" +
                 "Ví dụ:\n" +
                 "  '202401151200'.Date() // 15/01/2024 12:00\n" +
                 "  '20240115'.Date() // 15/01/2024"
        },
        { 
            name: "ToDateTime", 
            meta: "DateTime", 
            score: 850, 
            snippet: ".ToDateTime(${1:'format'})",
            doc: ".ToDateTime(string format) → DateTime?\n\n" +
                 "Parse string sang DateTime theo format chỉ định.\n\n" +
                 "Params:\n" +
                 "  • format (string): Format pattern\n\n" +
                 "Returns: DateTime? - null nếu không match format\n\n" +
                 "Ví dụ:\n" +
                 "  '20240115'.ToDateTime('yyyyMMdd')\n" +
                 "  '15/01/2024'.ToDateTime('dd/MM/yyyy')"
        },
        { 
            name: "IsDate", 
            meta: "DateTime", 
            score: 800, 
            doc: ".IsDate() → bool\n\n" +
                 "Kiểm tra string có phải ngày hợp lệ không.\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  '202401151200'.IsDate() // true\n" +
                 "  'abc'.IsDate() // false"
        },
        { 
            name: "ShowDate", 
            meta: "DateTime", 
            score: 800, 
            doc: ".ShowDate() → string\n\n" +
                 "Hiển thị ngày định dạng dd/MM-yyyy.\n\n" +
                 "Returns: string - '' nếu không parse được\n\n" +
                 "Ví dụ:\n" +
                 "  '202401151200'.ShowDate() // '15/01/2024'"
        },
        { 
            name: "ShowDateTime", 
            meta: "DateTime", 
            score: 800, 
            doc: ".ShowDateTime() → string\n\n" +
                 "Hiển thị ngày giờ định dạng dd/MM/yyyy HH:mm.\n\n" +
                 "Returns: string - '' nếu không parse được\n\n" +
                 "Ví dụ:\n" +
                 "  '202401151200'.ShowDateTime() // '15/01/2024 12:00'"
        },
        { 
            name: "DateDiff", 
            meta: "DateTime", 
            score: 800, 
            snippet: ".DateDiff(${1:date2}, ${2:'D'})",
            doc: ".DateDiff(object date2, string type = 'D') → int?\n\n" +
                 "Tính khoảng cách giữa 2 ngày.\n\n" +
                 "Params:\n" +
                 "  • date2: Ngày so sánh\n" +
                 "  • type: 'D'=ngày, 'M'=tháng, 'Y'=năm, 'h'=giờ, 'n'=phút\n\n" +
                 "Returns: int? - Khoảng cách (this - date2)\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.NGAY_RA.Val.DateDiff(XML1.NGAY_VAO.Val, 'D')\n" +
                 "  ngay1.DateDiff(ngay2, 'n') // phút"
        },
        { 
            name: "DateBeTween", 
            meta: "DateTime", 
            score: 750, 
            snippet: ".DateBeTween(${1:date1}, ${2:date2}, ${3:'D'})",
            doc: ".DateBeTween(object date1, object date2, string type = 'D') → bool\n\n" +
                 "Kiểm tra ngày có nằm trong khoảng không.\n\n" +
                 "Params:\n" +
                 "  • date1: Ngày bắt đầu\n" +
                 "  • date2: Ngày kết thúc\n" +
                 "  • type: 'D'=ngày, 'h'=giờ\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  ngayYL.DateBeTween(ngayVao, ngayRa)"
        },
        { 
            name: "DateLessThan", 
            meta: "DateTime", 
            score: 750, 
            doc: ".DateLessThan(object date) → bool\n\n" +
                 "So sánh ngày nhỏ hơn (<).\n\n" +
                 "Ví dụ:\n" +
                 "  ngay1.DateLessThan(ngay2)"
        },
        { 
            name: "DateEqualLessThan", 
            meta: "DateTime", 
            score: 750, 
            doc: ".DateEqualLessThan(object date) → bool\n\n" +
                 "So sánh ngày nhỏ hơn hoặc bằng (<=).\n\n" +
                 "Ví dụ:\n" +
                 "  ngay1.DateEqualLessThan(ngay2)"
        },
        { 
            name: "DateGetterThan", 
            meta: "DateTime", 
            score: 750, 
            doc: ".DateGetterThan(object date) → bool\n\n" +
                 "So sánh ngày lớn hơn (>).\n\n" +
                 "Ví dụ:\n" +
                 "  ngay1.DateGetterThan(ngay2)"
        },
        { 
            name: "DateEqualGetterThan", 
            meta: "DateTime", 
            score: 750, 
            doc: ".DateEqualGetterThan(object date) → bool\n\n" +
                 "So sánh ngày lớn hơn hoặc bằng (>=).\n\n" +
                 "Ví dụ:\n" +
                 "  ngay1.DateEqualGetterThan(ngay2)"
        },
        { 
            name: "DateEqual", 
            meta: "DateTime", 
            score: 750, 
            doc: ".DateEqual(object date) → bool\n\n" +
                 "So sánh ngày bằng nhau (==).\n\n" +
                 "Ví dụ:\n" +
                 "  ngay1.DateEqual(ngay2)"
        },
        // Date Parts
        { 
            name: "Year", 
            meta: "DatePart", 
            score: 700, 
            doc: ".Year() → int\n\n" +
                 "Lấy năm từ ngày.\n\n" +
                 "Returns: int - Năm hoặc 0 nếu lỗi\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.NGAY_VAO.Val.Year() // 2024"
        },
        { 
            name: "Mon", 
            meta: "DatePart", 
            score: 700, 
            doc: ".Mon() → int\n\n" +
                 "Lấy tháng từ ngày (1-12).\n\n" +
                 "Returns: int\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.NGAY_VAO.Val.Mon() // 1"
        },
        { 
            name: "Quater", 
            meta: "DatePart", 
            score: 700, 
            doc: ".Quater() → int\n\n" +
                 "Lấy quý từ ngày (1-4).\n\n" +
                 "Returns: int\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.NGAY_VAO.Val.Quater() // 1 (Q1)"
        },
        { 
            name: "Day", 
            meta: "DatePart", 
            score: 700, 
            doc: ".Day() → int\n\n" +
                 "Lấy ngày trong tháng (1-31).\n\n" +
                 "Returns: int\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.NGAY_VAO.Val.Day() // 15"
        },
        { 
            name: "Hour", 
            meta: "DatePart", 
            score: 700, 
            doc: ".Hour() → int\n\n" +
                 "Lấy giờ từ ngày (0-23).\n\n" +
                 "Returns: int\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.NGAY_VAO.Val.Hour() // 12"
        },
        { 
            name: "Min", 
            meta: "DatePart", 
            score: 700, 
            doc: ".Min() → int\n\n" +
                 "Lấy phút từ ngày (0-59).\n\n" +
                 "Returns: int\n\n" +
                 "Ví dụ:\n" +
                 "  XML1.NGAY_VAO.Val.Min() // 30"
        },
        // String Manipulation
        { 
            name: "StringLeft", 
            meta: "String", 
            score: 750, 
            snippet: ".StringLeft(${1:len})",
            doc: ".StringLeft(int len) → string\n\n" +
                 "Lấy n ký tự bên trái của chuỗi.\n\n" +
                 "Params:\n" +
                 "  • len (int): Số ký tự cần lấy\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  maThe.StringLeft(2) // 'DN' (từ 'DN1123456789012')"
        },
        { 
            name: "StringRight", 
            meta: "String", 
            score: 750, 
            snippet: ".StringRight(${1:len})",
            doc: ".StringRight(int len) → string\n\n" +
                 "Lấy n ký tự bên phải của chuỗi.\n\n" +
                 "Params:\n" +
                 "  • len (int): Số ký tự cần lấy\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  maThe.StringRight(5) // '89012'"
        },
        { 
            name: "StringTruncate", 
            meta: "String", 
            score: 750, 
            snippet: ".StringTruncate(${1:len})",
            doc: ".StringTruncate(int len) → string\n\n" +
                 "Cắt chuỗi và thêm '...' nếu dài hơn giới hạn.\n\n" +
                 "Params:\n" +
                 "  • len (int): Độ dài tối đa\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  tenThuoc.StringTruncate(50)"
        },
        { 
            name: "LastString", 
            meta: "String", 
            score: 700, 
            snippet: ".LastString(${1:';'})",
            doc: ".LastString(string delimiter = ';') → string?\n\n" +
                 "Lấy phần tử cuối cùng sau khi split.\n\n" +
                 "Params:\n" +
                 "  • delimiter (string): Ký tự phân cách (default ';')\n\n" +
                 "Returns: string?\n\n" +
                 "Ví dụ:\n" +
                 "  'A;B;C'.LastString() // 'C'\n" +
                 "  'A|B|C'.LastString('|') // 'C'"
        },
        { 
            name: "ShortName", 
            meta: "String", 
            score: 650, 
            snippet: ".ShortName(${1:4})",
            doc: ".ShortName(int maxWords = 4) → string\n\n" +
                 "Rút gọn tên (viết tắt phần giữa).^M\n\n" +
                 "Params:\n" +
                 "  • maxWords (int): Số từ tối đa hiển thị\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'NGUYEN VAN MINH ANH'.ShortName(3) // 'NGUYEN VMA ANH'"
        },
        // ICD10 & Medical
        { 
            name: "CleanCode", 
            meta: "Medical", 
            score: 700, 
            doc: ".CleanCode() → string\n\n" +
                 "Loại bỏ ký tự đặc biệt khỏi mã ICD (* . †).\n" +
                 "Chuyển sang uppercase.\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'E11.5*'.CleanCode() // 'E115'"
        },
        { 
            name: "IsRange", 
            meta: "Medical", 
            score: 700, 
            snippet: ".IsRange(${1:'range'})",
            doc: ".IsRange(string range) → bool\n\n" +
                 "Kiểm tra mã ICD có trong khoảng không.\n" +
                 "Hỗ trợ: range đơn, nhiều range (;), khoảng (-).\n\n" +
                 "Params:\n" +
                 "  • range (string): Khoảng ICD cần kiểm tra\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  'E11'.IsRange('E10-E14') // true\n" +
                 "  'E11'.IsRange('E10;E11;E12') // true\n" +
                 "  'E115'.IsRange('E10-E14') // true"
        },
        { 
            name: "GetPureMaBS", 
            meta: "Medical", 
            score: 650, 
            doc: ".GetPureMaBS() → string\n\n" +
                 "Lấy mã bác sĩ thuần (loại bỏ phần mở rộng).\n" +
                 "Cắt trước dấu ( hoặc .\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  '12345(PK)'.GetPureMaBS() // '12345'\n" +
                 "  '12345.01'.GetPureMaBS() // '12345'"
        },
        // Comparison
        { 
            name: "In", 
            meta: "Compare", 
            score: 700, 
            snippet: ".In(${1:val1}, ${2:val2})",
            doc: ".In(params object[] values) → bool\n\n" +
                 "Kiểm tra giá trị có trong danh sách không.\n" +
                 "Hỗ trợ wildcard (*) cho string.\n\n" +
                 "Params:\n" +
                 "  • values: Danh sách giá trị cần kiểm tra\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  maNhom.In(1, 2, 3, 4)\n" +
                 "  maLoaiKCB.In(1, 7, 9)\n" +
                 "  'ABC*'.In('ABCDEF', 'XYZ') // true (wildcard)"
        },
        { 
            name: "IsMoreThanSaiSo", 
            meta: "Compare", 
            score: 650, 
            snippet: ".IsMoreThanSaiSo(${1:num2}, ${2:saiso})",
            doc: ".IsMoreThanSaiSo(object num, decimal saiso = 0) → bool\n\n" +
                 "So sánh 2 số với sai số cho phép.\n\n" +
                 "Params:\n" +
                 "  • num: Số cần so sánh\n" +
                 "  • saiso: Sai số cho phép (default 0)\n\n" +
                 "Returns: bool - true nếu |this - num| > saiso\n\n" +
                 "Ví dụ:\n" +
                 "  tien1.IsMoreThanSaiSo(tien2, 1) // true nếu chênh > 1"
        },
        // Encoding
        { 
            name: "Base64Encode", 
            meta: "Encoding", 
            score: 600, 
            doc: ".Base64Encode() → string\n\n" +
                 "Mã hóa chuỗi sang Base64 (UTF-8).\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'Hello'.Base64Encode() // 'SGVsbG8='"
        },
        { 
            name: "Base64Decode", 
            meta: "Encoding", 
            score: 600, 
            doc: ".Base64Decode() → string\n\n" +
                 "Giải mã chuỗi từ Base64 sang UTF-8.\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'SGVsbG8='.Base64Decode() // 'Hello'"
        },
        { 
            name: "HtmlToPlainText", 
            meta: "Encoding", 
            score: 600, 
            doc: ".HtmlToPlainText() → string\n\n" +
                 "Chuyển HTML sang text thuần.\n" +
                 "Loại bỏ tags, giữ nội dung.\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  '<b>Hello</b>'.HtmlToPlainText() // 'Hello'"
        },
        { 
            name: "EncodeXML", 
            meta: "Encoding", 
            score: 600, 
            doc: ".EncodeXML() → string\n\n" +
                 "Encode các ký tự đặc biệt XML (&, <, >, ', \").\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  '<test>'.EncodeXML() // '&lt;test&gt;'"
        },
        { 
            name: "FixSpecialChar", 
            meta: "Encoding", 
            score: 600, 
            doc: ".FixSpecialChar() → string\n\n" +
                 "Loại bỏ ký tự $ và thay | bằng ;\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'A$B|C'.FixSpecialChar() // 'AB;C'"
        },
        { 
            name: "PureXML", 
            meta: "Encoding", 
            score: 600, 
            doc: ".PureXML() → string\n\n" +
                 "Làm sạch XML (loại bỏ BOM, XML declaration thừa).\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  xmlContent.PureXML()"
        },
        // Join & List
        { 
            name: "JoinList", 
            meta: "List", 
            score: 600, 
			snippet: ".JoinList(${1:';'})",
            doc: ".JoinList<T>(string delimiter = ';') → string\n\n" +
                 "Nối các phần tử thành chuỗi.\n\n" +
                 "Params:\n" +
                 "  • delimiter (string): Ký tự phân cách\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  list.JoinList(', ')"
        },
        { 
            name: "JoinCols", 
            meta: "List", 
            score: 600, 
			snippet: ".JoinCols(${1:'ColumnName'}, ${2:'Value'})",
            doc: ".JoinCols<T>(string columnName, string nestedProperty = 'Value') → string\n\n" +
                 "Nối giá trị của một cột từ list.\n\n" +
                 "Params:\n" +
                 "  • columnName (string): Tên cột\n" +
                 "  • nestedProperty (string): Tên property lồng (default 'Value')\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  XML2.JoinCols('TEN_THUOC', 'Value')"
        },
        // Validation
        { 
            name: "FilterUnEntity", 
            meta: "Validation", 
            score: 550, 
            doc: ".FilterUnEntity() → string\n\n" +
                 "Lọc ra các ký tự đặc biệt XML không hợp lệ.\n\n" +
                 "Returns: string - Chỉ chứa < > & \" '\n\n" +
                 "Ví dụ:\n" +
                 "  content.FilterUnEntity()"
        },
        { 
            name: "IsUnEntity", 
            meta: "Validation", 
            score: 550, 
            doc: ".IsUnEntity() → bool\n\n" +
                 "Kiểm tra có ký tự XML không hợp lệ không.\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  content.IsUnEntity() // true nếu có < > & \" '"
        },
        // Format & Display
        { 
            name: "Formater", 
            meta: "Format", 
            score: 600, 
            doc: ".Formater() → string\n\n" +
                 "Format số với dấu phân cách hàng nghìn,\n" +
                 "hoặc format DateTime thành dd/MM/yyyy HH:mm.\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  1234567.89.Formater() // '1,234,567.89'\n" +
                 "  DateTime.Now.Formater() // '15/01/2024 12:00'"
        },
        { 
            name: "StringCompare", 
            meta: "Compare", 
            score: 600, 
            snippet: ".StringCompare(${1:'standard'}, out result)",
            doc: ".StringCompare(string standard, out string result) → bool\n" +
                 ".StringCompare(string standard, bool caseSensitive, out string result) → bool\n\n" +
                 "So sánh chuỗi và highlight khác biệt.\n\n" +
                 "Params:\n" +
                 "  • standard (string): Chuỗi chuẩn để so sánh\n" +
                 "  • caseSensitive (bool): Phân biệt hoa/thường\n" +
                 "  • result (out string): Chuỗi với highlight\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  maCSKCB.StringCompare(Config('MA_CSKCB'), out result)"
        },
        { 
            name: "NumberCompare", 
            meta: "Compare", 
            score: 600, 
            snippet: ".NumberCompare(${1:standard}, out result)",
            doc: ".NumberCompare(decimal standard, out string result, string format = 'N0', decimal saiso = 0, int mode = 0) → bool\n\n" +
                 "So sánh số và highlight nếu khác.\n\n" +
                 "Params:\n" +
                 "  • standard: Số chuẩn\n" +
                 "  • result: Kết quả format (đỏ nếu khác)\n" +
                 "  • format: Format string ('N0', 'N2'...)\n" +
                 "  • saiso: Sai số cho phép\n" +
                 "  • mode: 0=bằng, 1=nhỏ hơn bằng\n\n" +
                 "Returns: bool\n\n" +
                 "Ví dụ:\n" +
                 "  tienThuoc.NumberCompare(tongThuoc, out result)"
        },
        { 
            name: "ConvertToUnsign", 
            meta: "String", 
            score: 550, 
            doc: ".ConvertToUnsign() → string\n\n" +
                 "Chuyển chuỗi tiếng Việt có dấu sang không dấu.\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'Nguyễn Văn A'.ConvertToUnsign() // 'Nguyen Van A'"
        },
        { 
            name: "RemoveTitle", 
            meta: "String", 
            score: 550, 
            doc: ".RemoveTitle() → string\n\n" +
                 "Loại bỏ dấu tiếng Việt (legacy method).\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  'Xin chào'.RemoveTitle() // 'Xin chao'"
        },
        { 
            name: "StringStripWS", 
            meta: "String", 
            score: 550, 
            snippet: ".StringStripWS(${1:StripFlags.STRIP_ALL})",
            doc: ".StringStripWS(StripFlags flag) → string\n\n" +
                 "Loại bỏ khoảng trắng theo flag.\n\n" +
                 "StripFlags:\n" +
                 "  • STRIP_HEAD (1): Bỏ đầu\n" +
                 "  • STRIP_TAIL (2): Bỏ cuối\n" +
                 "  • STRIP_BOTH (3): Bỏ cả 2\n" +
                 "  • STRIP_DOUBLE (4): Bỏ khoảng trắng kép\n" +
                 "  • STRIP_ALL (7): Tất cả trên\n" +
                 "  • STRIP_ALL_WHITESPACE (8): Bỏ tất cả khoảng trắng\n\n" +
                 "Ví dụ:\n" +
                 "  '  A  B  '.StringStripWS(StripFlags.STRIP_ALL)"
        },
        { 
            name: "StringStripCR", 
            meta: "String", 
            score: 550, 
            doc: ".StringStripCR() → string\n\n" +
                 "Thay thế xuống dòng (\\r\\n, \\n, \\r) bằng khoảng trắng.\n\n" +
                 "Returns: string\n\n" +
                 "Ví dụ:\n" +
                 "  multiLineText.StringStripCR()"
        },
    ],
    
    // Color/Display Functions - Complete styling functions
    /** @type {CompletionEntry[]} */
    colorFunctions: [
        { name: "Red", meta: "Color", score: 950, doc: ".Red() → string\n\nTô màu đỏ cho text (HTML)\n<b style='color:red;'>text</b>\n\nVí dụ:\n  'Lỗi'.Red()\n  soTien.Red()" },
        { name: "Blue", meta: "Color", score: 950, doc: ".Blue() → string\n\nTô màu xanh dương cho text\n<b style='color:blue;'>text</b>\n\nVí dụ:\n  'Info'.Blue()\n  maBN.Blue()" },
        { name: "Green", meta: "Color", score: 950, doc: ".Green() → string\n\nTô màu xanh lá cho text\n<b style='color:green;'>text</b>\n\nVí dụ:\n  'Thành công'.Green()" },
        { name: "Purple", meta: "Color", score: 950, doc: ".Purple() → string\n\nTô màu tím cho text\n<b style='color:purple;'>text</b>\n\nVí dụ:\n  'Warning'.Purple()" },
        { name: "Orange", meta: "Color", score: 950, doc: ".Orange() → string\n\nTô màu cam cho text\n<b style='color:orange;'>text</b>\n\nVí dụ:\n  'Cảnh báo'.Orange()" },
        { name: "Grey", meta: "Color", score: 950, doc: ".Grey() → string\n\nTô màu xám cho text\n<b style='color:grey;'>text</b>\n\nVí dụ:\n  'Disabled'.Grey()" },
        { name: "Color", meta: "Color", score: 900, snippet: ".Color(${1:colorIndex})", doc: ".Color(int colorIndex) → string\n.Color(string colorName) → string\n\nTô màu tùy chọn\n\nParams:\n  • colorIndex: 0-21 (bảng màu có sẵn)\n  • colorName: Tên màu CSS\n\nBảng màu: Red, Magenta, Purple, Violet, Blue, Teal, Green, Chartreuse, Yellow, Amber, Orange, Vermilion...\n\nVí dụ:\n  text.Color(5) // Teal\n  text.Color('crimson')" },
        { name: "Code", meta: "Style", score: 900, doc: ".Code() → string\n\nFormat inline code style\n\nVí dụ:\n  maThuoc.Code()" },
        { name: "StrongCode", meta: "Style", score: 850, doc: ".StrongCode() → string\n\nFormat code bold\n<code><strong>text</strong></code>\n\nVí dụ:\n  tenDV.StrongCode()" },
        { name: "Mark", meta: "Style", score: 850, doc: ".Mark() → string\n\nHighlight text với màu nền vàng\n<span style='background-color: Orange;'>text</span>\n\nVí dụ:\n  tenThuoc.Mark()" },
        { name: "Kbd", meta: "Style", score: 850, doc: ".Kbd() → string\n\nFormat kiểu keyboard key\nMàu xanh lá trên nền đen\n\nVí dụ:\n  'Ctrl+S'.Kbd()" },
        { name: "Kbdlf", meta: "Style", score: 800, doc: ".Kbdlf() → string\n\nKbd với xuống dòng phía trước\n<br><kbd>text</kbd>\n\nVí dụ:\n  'Enter'.Kbdlf()" },
        { name: "Formater", meta: "Format", score: 850, doc: ".Formater() → string\n\nFormat số hoặc ngày\n• Số: Thêm dấu phân cách nghìn (#,##0.##)\n• DateTime: dd/MM/yyyy HH:mm\n\nVí dụ:\n  1234567.89.Formater() // '1,234,567.89'\n  DateTime.Now.Formater() // '15/01/2024 12:00'" },
        { name: "HightLight", meta: "Style", score: 800, snippet: ".HightLight(${1:'searchText'})", doc: ".HightLight(string searchText) → string\n\nHighlight text được tìm thấy bằng màu đỏ\n\nParams:\n  • searchText: Chuỗi cần highlight\n\nVí dụ:\n  tenThuoc.HightLight('para')" },
    ],
    
    // LINQ Functions
    /** @type {CompletionEntry[]} */
    linqFunctions: [
        { name: "Where", meta: "LINQ", score: 900, doc: ".Where(predicate) : IEnumerable<T>\nLọc các phần tử thỏa điều kiện\nVí dụ: XML2.Where(x => x.MA_NHOM.Val == '4')" },
        { name: "Select", meta: "LINQ", score: 900, doc: ".Select(selector) : IEnumerable<TResult>\nChọn/biến đổi các phần tử\nVí dụ: XML2.Select(x => x.TEN_THUOC.Val)" },
        { name: "Any", meta: "LINQ", score: 850, doc: ".Any(predicate?) : bool\nKiểm tra có phần tử nào thỏa điều kiện\nVí dụ: XML2.Any(x => x.SO_LUONG.Val.ToDecimal() > 100)" },
        { name: "FirstOrDefault", meta: "LINQ", score: 850, doc: ".FirstOrDefault(predicate?) : T?\nLấy phần tử đầu tiên hoặc null\nVí dụ: XML2.FirstOrDefault(x => x.MA_THUOC.Val == ma)" },
        { name: "LastOrDefault", meta: "LINQ", score: 850, doc: ".LastOrDefault(predicate?) : T?\nLấy phần tử cuối cùng hoặc null\nVí dụ: XML3.LastOrDefault()" },
        { name: "Count", meta: "LINQ", score: 800, doc: ".Count(predicate?) : int\nĐếm số phần tử\nVí dụ: XML2.Count(x => x.MA_NHOM.Val == '4')" },
        { name: "Sum", meta: "LINQ", score: 800, doc: ".Sum(selector) : decimal\nTính tổng\nVí dụ: XML2.Sum(x => x.THANH_TIEN_BV.Val.ToDecimal())" },
        { name: "Max", meta: "LINQ", score: 800, doc: ".Max(selector?) : T\nLấy giá trị lớn nhất\nVí dụ: XML2.Max(x => x.DON_GIA.Val.ToDecimal())" },
        { name: "Min", meta: "LINQ", score: 800, doc: ".Min(selector?) : T\nLấy giá trị nhỏ nhất\nVí dụ: XML2.Min(x => x.DON_GIA.Val.ToDecimal())" },
        { name: "Average", meta: "LINQ", score: 750, doc: ".Average(selector) : double\nTính trung bình\nVí dụ: XML2.Average(x => x.DON_GIA.Val.ToDecimal())" },
        { name: "OrderBy", meta: "LINQ", score: 750, doc: ".OrderBy(keySelector) : IOrderedEnumerable<T>\nSắp xếp tăng dần\nVí dụ: XML2.OrderBy(x => x.STT.Val.ToInt())" },
        { name: "OrderByDescending", meta: "LINQ", score: 750, doc: ".OrderByDescending(keySelector) : IOrderedEnumerable<T>\nSắp xếp giảm dần\nVí dụ: XML2.OrderByDescending(x => x.THANH_TIEN_BV.Val.ToDecimal())" },
        { name: "ThenBy", meta: "LINQ", score: 750, doc: ".ThenBy(keySelector) : IOrderedEnumerable<T>\nSắp xếp phụ (tăng dần)\nVí dụ: XML2.OrderBy(x => x.STT.Val.ToInt()).ThenBy(x => x.DON_GIA.Val.ToDecimal())" },
        { name: "ThenByDescending", meta: "LINQ", score: 750, doc: ".ThenByDescending(keySelector) : IOrderedEnumerable<T>\nSắp xếp phụ (giảm dần)\nVí dụ: XML2.OrderBy(x => x.STT.Val.ToInt()).ThenByDescending(x => x.THANH_TIEN_BV.Val.ToDecimal())" },
        { name: "GroupBy", meta: "LINQ", score: 700, doc: ".GroupBy(keySelector) : IEnumerable<IGrouping<TKey, T>>\nNhóm các phần tử\nVí dụ: XML2.GroupBy(x => x.MA_NHOM.Val)" },
        { name: "ToList", meta: "LINQ", score: 700, doc: ".ToList() : List<T>\nChuyển sang List\nVí dụ: XML2.Where(x => x.MA_NHOM.Val == '4').ToList()" },
        { name: "Distinct", meta: "LINQ", score: 650, doc: ".Distinct() : IEnumerable<T>\nLoại bỏ trùng lặp\nVí dụ: XML2.Select(x => x.MA_THUOC.Val).Distinct()" },
        { name: "Take", meta: "LINQ", score: 650, doc: ".Take(int count) : IEnumerable<T>\nLấy n phần tử đầu\nVí dụ: XML2.Take(10)" },
        { name: "Skip", meta: "LINQ", score: 650, doc: ".Skip(int count) : IEnumerable<T>\nBỏ qua n phần tử đầu\nVí dụ: XML2.Skip(5)" },
    ],
    
    // XML Variables
    /** @type {XmlVariableEntry[]} */
    xmlVariables: [
        { name: "XML1", meta: "TONG_HOP_130", score: 1000, doc: "XML1 : TONG_HOP_130\nBảng tổng hợp hồ sơ BHYT 130" },
        { name: "XML2", meta: "List<CHI_TIET_THUOC_130>", score: 1000, doc: "XML2 : List<CHI_TIET_THUOC_130>\nDanh sách chi tiết thuốc" },
        { name: "XML3", meta: "List<CHI_TIET_DVKT_130>", score: 1000, doc: "XML3 : List<CHI_TIET_DVKT_130>\nDanh sách dịch vụ kỹ thuật/VTYT" },
        { name: "XML4", meta: "List<CHI_TIET_CLS_130>", score: 1000, doc: "XML4 : List<CHI_TIET_CLS_130>\nDanh sách cận lâm sàng" },
        { name: "XML5", meta: "List<CHI_TIET_DIEN_BIEN_BENH_130>", score: 1000, doc: "XML5 : List<CHI_TIET_DIEN_BIEN_BENH_130>\nDanh sách diễn biến lâm sàng" },
        { name: "XML6", meta: "List<HSBA_HIV_AIDS>", score: 900, doc: "XML6 : List<HSBA_HIV_AIDS>\nHồ sơ điều trị HIV/AIDS" },
        { name: "XML7", meta: "GIAY_RA_VIEN", score: 900, doc: "XML7 : CHI_TIEU_GIAY_RA_VIEN\nGiấy ra viện" },
        { name: "XML8", meta: "TOM_TAT_HSBA", score: 900, doc: "XML8 : CHI_TIEU_TOM_TAT_HSBA\nTóm tắt hồ sơ bệnh án" },
        { name: "XML9", meta: "List<CHUNG_SINH>", score: 900, doc: "XML9 : List<DU_LIEU_GIAY_CHUNG_SINH>\nDanh sách giấy chứng sinh" },
        { name: "XML10", meta: "NGHI_DUONG_THAI", score: 900, doc: "XML10 : CHI_TIEU_GIAY_NGHI_DUONG_THAI\nGiấy nghỉ dưỡng thai" },
        { name: "XML11", meta: "NGHI_OM", score: 900, doc: "XML11 : CHI_TIEU_GIAY_NGHI_OM\nGiấy chứng nhận nghỉ việc hưởng BHXH" },
        { name: "XML13", meta: "CHUYEN_TUYEN", score: 900, doc: "XML13 : CHI_TIEU_GIAYCHUYENTUYEN\nGiấy chuyển tuyến" },
        { name: "XML14", meta: "HEN_KHAM", score: 900, doc: "XML14 : CHI_TIEU_GIAYHEN_KHAMLAI\nGiấy hẹn khám lại" },
        { name: "XML15", meta: "List<CHITIET_DIEUTRI_BENHLAO>", score: 900, doc: "XML15 : List<CHITIET_DIEUTRI_BENHLAO>\nChi tiết điều trị bệnh lao" },
        { name: "XML130", meta: "HOSO130", score: 950, doc: "XML130 : HOSO130\nObject chứa toàn bộ hồ sơ 130" },
        { name: "HOSO", meta: "XmlDocument", score: 950, doc: "HOSO : XmlDocument\nXML Document gốc của hồ sơ" },
        { name: "NodesSet", meta: "dynamic", score: 800, doc: "NodesSet : dynamic\nTập hợp nodes từ Xpaths() gần nhất" },
    ],
    
    // Common XML Fields (TONG_HOP_130) - Complete field list
    /** @type {CompletionEntry[]} */
    xml1Fields: [
        // Base TONG_HOP fields
        { name: "MA_LK", meta: "StringField", score: 850, doc: "Mã liên kết hồ sơ" },
        { name: "STT", meta: "IntField", score: 800, doc: "Số thứ tự" },
        { name: "MA_BN", meta: "StringField", score: 850, doc: "Mã bệnh nhân" },
        { name: "HO_TEN", meta: "StringField", score: 850, doc: "Họ và tên bệnh nhân" },
        { name: "NGAY_SINH", meta: "StringField", score: 800, doc: "Ngày sinh (yyyyMMdd)" },
        { name: "GIOI_TINH", meta: "IntField", score: 800, doc: "Giới tính: 1=Nam, 2=Nữ" },
        { name: "DIA_CHI", meta: "StringField", score: 750, doc: "Địa chỉ bệnh nhân" },
        { name: "MA_DKBD", meta: "StringField", score: 800, doc: "Mã đăng ký ban đầu" },
        { name: "GT_THE_TU", meta: "StringField", score: 800, doc: "Giá trị thẻ từ ngày" },
        { name: "GT_THE_DEN", meta: "StringField", score: 800, doc: "Giá trị thẻ đến ngày" },
        { name: "MA_TAI_NAN", meta: "IntField", score: 750, doc: "Mã tai nạn" },
        { name: "NGAY_VAO", meta: "StringField", score: 850, doc: "Ngày vào viện (yyyyMMddHHmm)" },
        { name: "NGAY_RA", meta: "StringField", score: 850, doc: "Ngày ra viện (yyyyMMddHHmm)" },
        { name: "SO_NGAY_DTRI", meta: "IntField", score: 800, doc: "Số ngày điều trị" },
        { name: "KET_QUA_DTRI", meta: "IntField", score: 800, doc: "Kết quả điều trị: 1=Khỏi, 2=Đỡ, 3=Không đổi, 4=Nặng hơn, 5=Tử vong" },
        { name: "NGAY_TTOAN", meta: "StringField", score: 750, doc: "Ngày thanh toán" },
        { name: "T_THUOC", meta: "DecimalField", score: 850, doc: "Tổng tiền thuốc" },
        { name: "T_VTYT", meta: "DecimalField", score: 850, doc: "Tổng tiền vật tư y tế" },
        { name: "T_BNTT", meta: "DecimalField", score: 850, doc: "Tổng tiền bệnh nhân thanh toán" },
        { name: "T_BNCCT", meta: "DecimalField", score: 800, doc: "Tổng tiền BN cùng chi trả" },
        { name: "T_BHTT", meta: "DecimalField", score: 850, doc: "Tổng tiền BHYT thanh toán" },
        { name: "T_NGUONKHAC", meta: "DecimalField", score: 750, doc: "Tổng tiền nguồn khác" },
        { name: "NAM_QT", meta: "IntField", score: 800, doc: "Năm quyết toán" },
        { name: "THANG_QT", meta: "IntField", score: 800, doc: "Tháng quyết toán" },
        { name: "MA_LOAI_KCB", meta: "IntField", score: 850, doc: "Mã loại KCB: 1=Khám, 2=CC, 3=Nội trú, 4=Ngoại trú, 9=Đang ĐT" },
        { name: "MA_KHOA", meta: "StringField", score: 800, doc: "Mã khoa điều trị" },
        { name: "MA_CSKCB", meta: "StringField", score: 850, doc: "Mã cơ sở KCB" },
        { name: "MA_KHUVUC", meta: "StringField", score: 750, doc: "Mã khu vực (K1/K2/K3)" },
        { name: "MA_PTTT_QT", meta: "StringField", score: 750, doc: "Mã phẫu thuật/thủ thuật quyết toán" },
        { name: "CAN_NANG", meta: "StringField", score: 750, doc: "Cân nặng bệnh nhân" },
        // TONG_HOP_130 specific fields
        { name: "SO_CCCD", meta: "StringField", score: 800, doc: "Số căn cước công dân" },
        { name: "NHOM_MAU", meta: "StringField", score: 700, doc: "Nhóm máu" },
        { name: "MA_QUOCTICH", meta: "StringField", score: 700, doc: "Mã quốc tịch" },
        { name: "MA_DANTOC", meta: "StringField", score: 700, doc: "Mã dân tộc" },
        { name: "MA_NGHE_NGHIEP", meta: "StringField", score: 700, doc: "Mã nghề nghiệp" },
        { name: "MATINH_CU_TRU", meta: "StringField", score: 700, doc: "Mã tỉnh cư trú" },
        { name: "MAHUYEN_CU_TRU", meta: "StringField", score: 700, doc: "Mã huyện cư trú" },
        { name: "MAXA_CU_TRU", meta: "StringField", score: 700, doc: "Mã xã cư trú" },
        { name: "DIEN_THOAI", meta: "StringField", score: 700, doc: "Điện thoại liên hệ" },
        { name: "MA_THE_BHYT", meta: "StringField", score: 850, doc: "Mã thẻ BHYT (15 ký tự)" },
        { name: "NGAY_MIEN_CCT", meta: "StringField", score: 750, doc: "Ngày miễn cùng chi trả" },
        { name: "LY_DO_VV", meta: "StringField", score: 750, doc: "Lý do vào viện" },
        { name: "LY_DO_VNT", meta: "StringField", score: 750, doc: "Lý do vào nội trú" },
        { name: "MA_LY_DO_VNT", meta: "StringField", score: 750, doc: "Mã lý do vào nội trú" },
        { name: "CHAN_DOAN_VAO", meta: "StringField", score: 800, doc: "Chẩn đoán vào viện" },
        { name: "CHAN_DOAN_RV", meta: "StringField", score: 800, doc: "Chẩn đoán ra viện" },
        { name: "MA_BENH_CHINH", meta: "StringField", score: 850, doc: "Mã bệnh chính (ICD-10)" },
        { name: "MA_BENH_KT", meta: "StringField", score: 800, doc: "Mã bệnh kèm theo (ICD-10)" },
        { name: "MA_BENH_YHCT", meta: "StringField", score: 750, doc: "Mã bệnh YHCT" },
        { name: "MA_DOITUONG_KCB", meta: "StringField", score: 800, doc: "Mã đối tượng KCB" },
        { name: "MA_NOI_DI", meta: "StringField", score: 750, doc: "Mã nơi đi (chuyển tuyến)" },
        { name: "MA_NOI_DEN", meta: "StringField", score: 750, doc: "Mã nơi đến (chuyển tuyến)" },
        { name: "NGAY_VAO_NOI_TRU", meta: "StringField", score: 800, doc: "Ngày vào nội trú" },
        { name: "GIAY_CHUYEN_TUYEN", meta: "StringField", score: 750, doc: "Giấy chuyển tuyến" },
        { name: "PP_DIEU_TRI", meta: "StringField", score: 750, doc: "Phương pháp điều trị" },
        { name: "MA_LOAI_RV", meta: "IntField", score: 750, doc: "Mã loại ra viện" },
        { name: "GHI_CHU", meta: "StringField", score: 700, doc: "Ghi chú" },
        { name: "T_TONGCHI_BV", meta: "DecimalField", score: 850, doc: "Tổng chi bệnh viện" },
        { name: "T_TONGCHI_BH", meta: "DecimalField", score: 850, doc: "Tổng chi bảo hiểm" },
        { name: "T_BHTT_GDV", meta: "DecimalField", score: 800, doc: "Tổng BHTT giám định viên" },
        { name: "CAN_NANG_CON", meta: "StringField", score: 700, doc: "Cân nặng con (trường hợp sinh)" },
        { name: "NAM_NAM_LIEN_TUC", meta: "StringField", score: 700, doc: "Số năm tham gia BHYT liên tục" },
        { name: "NGAY_TAI_KHAM", meta: "StringField", score: 750, doc: "Ngày hẹn tái khám" },
        { name: "MA_HSBA", meta: "StringField", score: 800, doc: "Mã hồ sơ bệnh án" },
        { name: "MA_TTDV", meta: "StringField", score: 750, doc: "Mã trung tâm dữ liệu" },
        { name: "DU_PHONG", meta: "StringField", score: 600, doc: "Trường dự phòng" },
    ],

    // CHI_TIET_THUOC_130 fields - Complete from XmlDocumentBHYT.cs
    /** @type {CompletionEntry[]} */
    xml2Fields: [
        // Base CHI_TIET_THUOC fields
        { name: "MA_LK", meta: "StringField", score: 850, doc: "Mã liên kết hồ sơ" },
        { name: "STT", meta: "IntField", score: 800, doc: "Số thứ tự dòng" },
        { name: "MA_THUOC", meta: "StringField", score: 900, doc: "Mã thuốc theo danh mục BYT" },
        { name: "MA_NHOM", meta: "IntField", score: 900, doc: "Mã nhóm:\n• 4: Thuốc điều trị\n• 10: Máu và chế phẩm máu\n• 11: Thuốc TTPT\n• 12: VTTH\n• 15: VT chẩn đoán" },
        { name: "TEN_THUOC", meta: "StringField", score: 900, doc: "Tên thuốc theo đăng ký" },
        { name: "DON_VI_TINH", meta: "StringField", score: 800, doc: "Đơn vị tính (viên, ống, chai...)" },
        { name: "HAM_LUONG", meta: "StringField", score: 850, doc: "Hàm lượng/nồng độ" },
        { name: "DUONG_DUNG", meta: "StringField", score: 850, doc: "Đường dùng (uống, tiêm, truyền...)" },
        { name: "LIEU_DUNG", meta: "StringField", score: 850, doc: "Liều dùng mỗi lần" },
        { name: "SO_DANG_KY", meta: "StringField", score: 800, doc: "Số đăng ký thuốc" },
        { name: "TT_THAU", meta: "StringField", score: 750, doc: "Thông tin thầu (mã gói thầu)" },
        { name: "SO_LUONG", meta: "DecimalField", score: 900, doc: "Số lượng sử dụng" },
        { name: "DON_GIA", meta: "DecimalField", score: 900, doc: "Đơn giá (đồng)" },
        { name: "MUC_HUONG", meta: "DecimalField", score: 850, doc: "Mức hưởng BHYT (%)" },
        { name: "T_BNTT", meta: "DecimalField", score: 850, doc: "Tiền bệnh nhân thanh toán" },
        { name: "T_BHTT", meta: "DecimalField", score: 900, doc: "Tiền BHYT thanh toán" },
        { name: "T_BNCCT", meta: "DecimalField", score: 850, doc: "Tiền BN cùng chi trả" },
        { name: "T_NGUONKHAC", meta: "DecimalField", score: 800, doc: "Tiền nguồn khác" },
        { name: "MA_KHOA", meta: "StringField", score: 850, doc: "Mã khoa kê đơn" },
        { name: "MA_BAC_SI", meta: "StringField", score: 850, doc: "Mã bác sĩ kê đơn" },
        { name: "NGAY_YL", meta: "StringField", score: 900, doc: "Ngày y lệnh (yyyyMMddHHmm)" },
        { name: "MA_PTTT", meta: "IntField", score: 750, doc: "Mã phẫu thuật thủ thuật (nếu có)" },
        { name: "PHAM_VI", meta: "IntField", score: 850, doc: "Phạm vi:\n• 1: Trong danh mục\n• 2: Ngoài danh mục" },
        // CHI_TIET_THUOC_130 specific fields
        { name: "MA_PP_CHEBIEN", meta: "StringField", score: 750, doc: "Mã phương pháp chế biến (YHCT)" },
        { name: "MA_CSKCB_THUOC", meta: "StringField", score: 750, doc: "Mã CSKCB cung cấp thuốc" },
        { name: "DANG_BAO_CHE", meta: "StringField", score: 800, doc: "Dạng bào chế" },
        { name: "CACH_DUNG", meta: "StringField", score: 850, doc: "Cách dùng chi tiết" },
        { name: "TYLE_TT_BH", meta: "IntField", score: 850, doc: "Tỷ lệ thanh toán BHYT (%)" },
        { name: "THANH_TIEN_BV", meta: "DecimalField", score: 900, doc: "Thành tiền bệnh viện = SL × ĐG" },
        { name: "THANH_TIEN_BH", meta: "DecimalField", score: 900, doc: "Thành tiền BHYT" },
        { name: "T_NGUONKHAC_NSNN", meta: "DecimalField", score: 750, doc: "Tiền nguồn NSNN" },
        { name: "T_NGUONKHAC_VTNN", meta: "DecimalField", score: 750, doc: "Tiền nguồn viện trợ NN" },
        { name: "T_NGUONKHAC_VTTN", meta: "DecimalField", score: 750, doc: "Tiền nguồn viện trợ trong nước" },
        { name: "T_NGUONKHAC_CL", meta: "DecimalField", score: 750, doc: "Tiền nguồn chênh lệch" },
        { name: "MA_DICH_VU", meta: "StringField", score: 800, doc: "Mã dịch vụ liên quan" },
        { name: "NGAY_TH_YL", meta: "StringField", score: 850, doc: "Ngày thực hiện y lệnh" },
        { name: "NGUON_CTRA", meta: "IntField", score: 750, doc: "Nguồn chi trả" },
        { name: "VET_THUONG_TP", meta: "IntField", score: 700, doc: "Vết thương thương phẩm" },
        { name: "DU_PHONG", meta: "StringField", score: 600, doc: "Trường dự phòng" },
        { name: "MA_BENH", meta: "StringField", score: 850, doc: "Mã bệnh ICD-10 liên quan" },
        { name: "Errors", meta: "StringField", score: 500, doc: "Danh sách lỗi của dòng thuốc" },
    ],

    // CHI_TIET_DVKT_130 fields (XML3) - Dịch vụ kỹ thuật
    /** @type {CompletionEntry[]} */
    xml3Fields: [
        { name: "MA_LK", meta: "StringField", score: 850, doc: "Mã liên kết hồ sơ" },
        { name: "STT", meta: "IntField", score: 800, doc: "Số thứ tự dòng" },
        { name: "MA_DICH_VU", meta: "StringField", score: 900, doc: "Mã dịch vụ kỹ thuật theo BYT" },
        { name: "MA_VAT_TU", meta: "StringField", score: 850, doc: "Mã vật tư y tế" },
        { name: "MA_NHOM", meta: "IntField", score: 900, doc: "Mã nhóm:\n• 1: DVKT\n• 2: VTYT\n• 3: Máu\n• 6: Tiền giường\n• 7: Vận chuyển\n• 8: Phẫu thuật\n• 9: XN\n• 10: CĐHA\n• 13: Thủ thuật\n• 18: Thủ thuật PT" },
        { name: "GOI_VTYT", meta: "StringField", score: 750, doc: "Gói vật tư y tế" },
        { name: "TEN_DICH_VU", meta: "StringField", score: 900, doc: "Tên dịch vụ kỹ thuật" },
        { name: "TEN_VAT_TU", meta: "StringField", score: 850, doc: "Tên vật tư y tế" },
        { name: "DON_VI_TINH", meta: "StringField", score: 800, doc: "Đơn vị tính" },
        { name: "PHAM_VI", meta: "IntField", score: 850, doc: "Phạm vi:\n• 1: Trong danh mục\n• 2: Ngoài danh mục" },
        { name: "SO_LUONG", meta: "DecimalField", score: 900, doc: "Số lượng thực hiện" },
        { name: "DON_GIA", meta: "DecimalField", score: 900, doc: "Đơn giá" },
        { name: "TT_THAU", meta: "StringField", score: 750, doc: "Thông tin thầu" },
        { name: "TYLE_TT_DV", meta: "IntField", score: 800, doc: "Tỷ lệ thanh toán dịch vụ (%)" },
        { name: "TYLE_TT_BH", meta: "IntField", score: 850, doc: "Tỷ lệ thanh toán BHYT (%)" },
        { name: "THANH_TIEN_BV", meta: "DecimalField", score: 900, doc: "Thành tiền bệnh viện" },
        { name: "THANH_TIEN_BH", meta: "DecimalField", score: 900, doc: "Thành tiền BHYT" },
        { name: "T_TRANTT", meta: "DecimalField", score: 800, doc: "Tiền trần thanh toán" },
        { name: "MUC_HUONG", meta: "DecimalField", score: 850, doc: "Mức hưởng BHYT (%)" },
        { name: "T_BNTT", meta: "DecimalField", score: 850, doc: "Tiền bệnh nhân thanh toán" },
        { name: "T_BHTT", meta: "DecimalField", score: 900, doc: "Tiền BHYT thanh toán" },
        { name: "T_BNCCT", meta: "DecimalField", score: 850, doc: "Tiền BN cùng chi trả" },
        { name: "T_NGUONKHAC", meta: "DecimalField", score: 800, doc: "Tiền nguồn khác" },
        { name: "MA_GIUONG", meta: "StringField", score: 750, doc: "Mã giường (nếu là tiền giường)" },
        { name: "MA_KHOA", meta: "StringField", score: 850, doc: "Mã khoa thực hiện" },
        { name: "MA_BAC_SI", meta: "StringField", score: 850, doc: "Mã bác sĩ chỉ định" },
        { name: "MA_BENH", meta: "StringField", score: 850, doc: "Mã bệnh ICD-10 liên quan" },
        { name: "NGAY_YL", meta: "StringField", score: 900, doc: "Ngày y lệnh (yyyyMMddHHmm)" },
        { name: "NGAY_KQ", meta: "StringField", score: 850, doc: "Ngày có kết quả" },
        { name: "MA_PTTT", meta: "IntField", score: 800, doc: "Mã phẫu thuật thủ thuật" },
        { name: "NGUOI_THUC_HIEN", meta: "StringField", score: 800, doc: "Người thực hiện" },
        { name: "Errors", meta: "StringField", score: 500, doc: "Danh sách lỗi" },
    ],

    // CHI_TIET_CLS_130 fields (XML4) - Cận lâm sàng
    /** @type {CompletionEntry[]} */
    xml4Fields: [
        { name: "MA_LK", meta: "StringField", score: 850, doc: "Mã liên kết hồ sơ" },
        { name: "STT", meta: "IntField", score: 800, doc: "Số thứ tự dòng" },
        { name: "MA_DICH_VU", meta: "StringField", score: 900, doc: "Mã dịch vụ CLS" },
        { name: "MA_CHI_SO", meta: "StringField", score: 850, doc: "Mã chỉ số xét nghiệm" },
        { name: "TEN_CHI_SO", meta: "StringField", score: 850, doc: "Tên chỉ số" },
        { name: "GIA_TRI", meta: "StringField", score: 900, doc: "Giá trị kết quả" },
        { name: "DON_VI_DO", meta: "StringField", score: 800, doc: "Đơn vị đo" },
        { name: "MO_TA", meta: "StringField", score: 850, doc: "Mô tả kết quả" },
        { name: "KET_LUAN", meta: "StringField", score: 850, doc: "Kết luận" },
        { name: "NGAY_KQ", meta: "StringField", score: 900, doc: "Ngày có kết quả" },
        { name: "Errors", meta: "StringField", score: 500, doc: "Danh sách lỗi" },
    ],

    // CHI_TIET_DIEN_BIEN_BENH_130 fields (XML5) - Diễn biến lâm sàng
    /** @type {CompletionEntry[]} */
    xml5Fields: [
        { name: "MA_LK", meta: "StringField", score: 850, doc: "Mã liên kết hồ sơ" },
        { name: "STT", meta: "IntField", score: 800, doc: "Số thứ tự" },
        { name: "DIEN_BIEN", meta: "StringField", score: 900, doc: "Mô tả diễn biến bệnh" },
        { name: "HOI_CHAN", meta: "StringField", score: 850, doc: "Nội dung hội chẩn" },
        { name: "PHAU_THUAT", meta: "StringField", score: 850, doc: "Thông tin phẫu thuật" },
        { name: "NGAY_YL", meta: "StringField", score: 900, doc: "Ngày y lệnh (yyyyMMddHHmm)" },
        { name: "Errors", meta: "StringField", score: 500, doc: "Danh sách lỗi" },
    ],

};


// ============================================
// HIGHLIGHT RULES
// ============================================
define("ace/mode/doc_comment_highlight_rules_checkxml", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text_highlight_rules"], function (e, t, n) {
    "use strict";
    var r = e("../lib/oop"),
        i = e("./text_highlight_rules").TextHighlightRules,
        s = function () {
            this.$rules = {
                start: [{
                    token: "comment.doc.tag",
                    regex: "@\\w+(?=\\s|$)"
                }, s.getTagRule(), {
                    defaultToken: "comment.doc",
                    caseInsensitive: !0
                }]
            }
        };
    r.inherits(s, i),
        s.getTagRule = function (e) {
            return {
                token: "comment.doc.tag.storage.type",
                regex: "\\b(?:TODO|FIXME|XXX|HACK)\\b"
            }
        },
        s.getStartRule = function (e) {
            return {
                token: "comment.doc",
                regex: "\\/\\*(?=\\*)",
                next: e
            }
        },
        s.getEndRule = function (e) {
            return {
                token: "comment.doc",
                regex: "\\*\\/?",
                next: e
            }
        },
        t.DocCommentHighlightRules = s
}),
define("ace/mode/checkxml_highlight_rules", ["require", "exports", "module", "ace/lib/oop", "ace/mode/doc_comment_highlight_rules_checkxml", "ace/mode/text_highlight_rules"], function (e, t, n) {
    "use strict";
    var r = e("../lib/oop"),
        i = e("./doc_comment_highlight_rules_checkxml").DocCommentHighlightRules,
        s = e("./text_highlight_rules").TextHighlightRules,
        o = function () {
            // Custom XmlObject functions (expanded from Core.XmlObject)
            var xmlObjectFunctions = "TLookUp|StringInStr|StringExist|StringReplace|VlookUp|Sum|SumIf|SumIf2|CountIf|Xpath|Xpaths|Val|Value|Execute|Casentive|ToDateTime|Diff|Between|Year|Quater|Mon|Day|Hour|Min|Config|IsCheck|ParentId|MaQL|ShowTable|Show|showtable|Text|ReplaceFirst|IsPTTT|IsPT|IsTT|IsTruocPT|IsSauPT|Exec|IsNumber|Length|ToDecimal|ToInt|ToDouble|Date|IsDate|ShowDate|ShowDateTime|StringLeft|StringRight|CleanCode|IsRange|In|ShortName|Base64Encode|Base64Decode|Red|Blue|Green|Purple|Orange|Grey|Color|Code|Mark|Value|Val|Contains|StartsWith|EndsWith|Replace|Substring|Trim|ToUpper|ToLower|Split|IndexOf|Any|Where|Select|OrderBy|OrderByDescending|GroupBy|ToList|FirstOrDefault|LastOrDefault|First|Last|Count|Sum|Max|Min|Average|Distinct|Take|Skip";

            // LINQ functions (expanded)
            var linqFunctions = "Any|Where|Select|OrderBy|OrderByDescending|GroupBy|ToList|FirstOrDefault|LastOrDefault|First|Last|Count|Sum|Max|Min|Average|Distinct|Take|Skip";

            // Color and formatting functions
            var colorFunctions = "Red|Blue|Green|Purple|Orange|Grey|Color|Code|Mark|Kbd|Kbdlf|StrongCode";

            // C# keywords
            var csharpKeywords = "abstract|async|await|event|new|struct|as|explicit|null|switch|base|extern|object|this|bool|false|operator|throw|break|finally|out|true|byte|fixed|override|try|case|float|params|typeof|catch|for|private|uint|char|foreach|protected|ulong|checked|goto|public|unchecked|class|if|readonly|unsafe|const|implicit|ref|ushort|continue|in|return|using|decimal|int|sbyte|virtual|default|interface|sealed|volatile|delegate|internal|partial|short|void|do|is|sizeof|while|double|lock|stackalloc|else|long|static|enum|namespace|string|var|dynamic|and|or|not|when|from|select|where|orderby|join|let|group|into|ascending|descending|record|init|with|required|file|scoped|global|nameof";

            // Field types
            var fieldTypes = "StringField|DecimalField|IntField|DateTimeField|BoolField";

            // XML element types
            var xmlTypes = "TONG_HOP_130|CHI_TIET_THUOC_130|CHI_TIET_DVKT_130|CHI_TIET_CLS_130|CHI_TIET_DIEN_BIEN_BENH_130|TONG_HOP_4210|CHI_TIET_THUOC_4210|CHI_TIET_DVKT_4210|CHI_TIET_CLS_4210|CHI_TIET_DIEN_BIEN_BENH_4210|HOSO130|HOSO4210|XmlNode|XmlNodeList|XmlDocument";

            var dataTypes = "List|Dictionary|IEnumerable|IList|ICollection|HashSet|Array|DateTime|TimeSpan|Guid|KeyValuePair|Tuple|Func|Action|Task";

            // XML Variables - phải là string cho createKeywordMapper
            var xmlVariables = "XML1|XML2|XML3|XML4|XML5|XML6|XML7|XML8|XML9|XML10|XML11|XML13|XML14|XML15|XML130|HOSO|NodesSet";

            var keywordMapper = this.createKeywordMapper({
                "variable.language": "this",
                "keyword": csharpKeywords,
                "constant.language": "null|true|false",
                "support.function": xmlObjectFunctions,
                "support.function.linq": linqFunctions,
                "variable.xml": xmlVariables,
                "support.function.color": colorFunctions,
                "storage.type": fieldTypes + "|" + dataTypes,
                "support.type": xmlTypes
            }, "identifier", true);

            this.$rules = {
                start: [
                    { token: "comment", regex: "\\/\\/.*$" },
                    i.getStartRule("doc-start"),
                    { token: "comment", regex: "\\/\\*", next: "comment" },
                    { token: ["support.function", "paren.lparen", "string.xpath", "paren.rparen"], regex: "(Xpath|Xpaths)(\\()(['\"][^'\"]*['\"])(\\))" },
                    { token: ["variable.xml", "punctuation.operator", "variable.parameter"], regex: "(XML\\d+\\??)(\\.)([A-Z_][A-Z0-9_]*)", caseInsensitive: true },
                    { token: ["variable.parameter", "keyword.operator", "keyword.operator"], regex: "([a-zA-Z_][a-zA-Z0-9_]*)\\s*(=)\\s*(>)" },
                    { token: "string", regex: /'(?:.|\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n]))?'/ },
                    { token: "string", start: '"', end: '"|$', next: [{ token: "constant.language.escape", regex: /\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n])/ }, { token: "invalid", regex: /\\./ }] },
                    { token: "string", start: '@"', end: '"', next: [{ token: "constant.language.escape", regex: '""' }] },
                    { token: "string", start: /\$"/, end: '"|$', next: [{ token: "constant.language.escape", regex: /\\(:?$)|{{|}}/ }, { token: "paren.lparen", regex: /\{/ }, { token: "paren.rparen", regex: /\}/ }] },
                    { token: "constant.numeric", regex: "0[xX][0-9a-fA-F]+\\b" },
                    { token: "constant.numeric", regex: "[+-]?\\d+(?:\\.\\d*)?(?:[eE][+-]?\\d+)?[mMdDfFlL]?\\b" },
                    { token: "constant.language.boolean", regex: "(?:true|false)\\b" },
                    { token: ["punctuation.operator", "support.function.color", "paren.lparen", "paren.rparen"], regex: "(\\.)([Rr]ed|[Bb]lue|[Gg]reen|[Pp]urple|[Oo]range|[Gg]rey|[Cc]ode|[Mm]ark|[Kk]bd)(\\()(\\))" },
                    { token: ["punctuation.operator", "support.function"], regex: "(\\.)([Vv]al(?:ue)?)\\b" },
                    { token: ["punctuation.operator", "support.function"], regex: "(\\.)([Ll]ength)\\b" },
                    { token: keywordMapper, regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b" },
                    { token: "keyword.operator", regex: "\\?\\.|\\?\\?|\\?\\?=" },
                    { token: "keyword.operator", regex: "!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|&&|\\|\\||\\?:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)" },
                    { token: "keyword", regex: "^\\s*#(if|else|elif|endif|define|undef|warning|error|line|region|endregion|pragma)" },
                    { token: "punctuation.operator", regex: "\\?|:|,|;|\\." },
                    { token: "paren.lparen", regex: "[[({]" },
                    { token: "paren.rparen", regex: "[\\])}]" },
                    { token: "text", regex: "\\s+" }
                ],
                comment: [
                    { token: "comment", regex: "\\*\\/", next: "start" },
                    { defaultToken: "comment" }
                ]
            };

            this.embedRules(i, "doc-", [i.getEndRule("start")]);
            this.normalizeRules();
        };

    r.inherits(o, s);
    t.CheckXmlHighlightRules = o;
}),
define("ace/mode/matching_brace_outdent_checkxml", ["require", "exports", "module", "ace/range"], function (e, t, n) {
    "use strict";
    var r = e("../range").Range,
        i = function () {};
    (function () {
        this.checkOutdent = function (e, t) {
            return /^\s+$/.test(e) ? /^\s*\}/.test(t) : false;
        };
        this.autoOutdent = function (e, t, n) {
            var r = e.getLine(t),
                i = r.match(/^(\s*\})/);
            if (!i) return 0;
            var s = i[1].length,
                o = e.findMatchingBracket({ row: t, column: s });
            if (!o || o.row == t) return 0;
            var u = this.$getIndent(e.getLine(o.row));
            e.replace(new r(t, 0, t, s - 1), u);
        };
        this.$getIndent = function (e) {
            return e.match(/^\s*/)[0];
        };
    }).call(i.prototype);
    t.MatchingBraceOutdent = i;
}),
define("ace/mode/folding/checkxml", ["require", "exports", "module", "ace/lib/oop", "ace/range", "ace/mode/folding/fold_mode"], function (e, t, n) {
    "use strict";
    var r = e("../../lib/oop"),
        Range = e("../../range").Range,
        BaseFoldMode = e("./fold_mode").FoldMode,
        s = function () {
            this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/;
            this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/;
        };

    r.inherits(s, BaseFoldMode);
    
    (function () {
        this.getFoldWidgetRange = function (session, foldStyle, row) {
            var line = session.getLine(row);
            var match = line.match(this.foldingStartMarker);
            if (match) {
                var i = match.index;
                if (match[1]) {
                    return this.openingBracketBlock(session, match[1], row, i);
                }
                var range = session.getCommentFoldRange(row, i + match[0].length, 1);
                if (range && !range.isMultiLine()) {
                    range = null;
                }
                return range;
            }

            if (foldStyle !== "markbeginend") return;

            match = line.match(this.foldingStopMarker);
            if (match) {
                var i = match.index + match[0].length;
                if (match[1]) {
                    return this.closingBracketBlock(session, match[1], row, i);
                }
                return session.getCommentFoldRange(row, i, -1);
            }
        };

        this.getSectionRange = function (session, row) {
            var line = session.getLine(row);
            var startIndent = line.search(/\S/);
            var startRow = row;
            var startColumn = line.length;
            row = row + 1;
            var endRow = row;
            var maxRow = session.getLength();
            while (++row < maxRow) {
                line = session.getLine(row);
                var indent = line.search(/\S/);
                if (indent === -1) continue;
                if (startIndent > indent) break;
                var subRange = this.getFoldWidgetRange(session, "all", row);
                if (subRange) {
                    if (subRange.start.row <= startRow) {
                        break;
                    } else if (subRange.isMultiLine()) {
                        row = subRange.end.row;
                    } else if (startIndent == indent) {
                        break;
                    }
                }
                endRow = row;
            }
            return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
        };
        
        this.openingBracketBlock = function(session, bracket, row, column) {
            var start = { row: row, column: column + 1 };
            var end = session.$findClosingBracket(bracket, start);
            if (!end) return;
            var fw = session.foldWidgets[end.row];
            if (fw == null) fw = session.getFoldWidget(end.row);
            if (fw == "start" && end.row > start.row) {
                end.row--;
                end.column = session.getLine(end.row).length;
            }
            return Range.fromPoints(start, end);
        };

        this.closingBracketBlock = function(session, bracket, row, column) {
            var end = { row: row, column: column };
            var start = session.$findOpeningBracket(bracket, end);
            if (!start) return;
            start.column++;
            end.column--;
            return Range.fromPoints(start, end);
        };
    }).call(s.prototype);
    
    t.FoldMode = s;
}),
define("ace/mode/checkxml_worker", ["require", "exports", "module"], function(e, t, n) {
    "use strict";
    
    var knownFunctions = ['Xpath', 'Xpaths', 'TLookUp', 'VlookUp', 'Sum', 'SumIf', 'SumIf2', 'CountIf', 'ShowTable', 'Show', 'showtable', 'Text', 'Execute', 'Casentive', 'ReplaceFirst', 'IsPTTT', 'IsPT', 'IsTT', 'IsTruocPT', 'IsSauPT', 'Config', 'IsCheck', 'Exec', 'IsNumber', 'Length', 'StringInStr', 'StringExist', 'StringReplace', 'ToDecimal', 'ToInt', 'ToDouble', 'Date', 'IsDate', 'ShowDate', 'ShowDateTime', 'ToDateTime', 'DateDiff', 'DateBeTween', 'Year', 'Mon', 'Quater', 'Day', 'Hour', 'Min', 'Config', 'IsCheck', 'ParentId', 'MaQL', 'ShowTable', 'Show', 'showtable', 'Text', 'ReplaceFirst', 'IsPTTT', 'IsPT', 'IsTT', 'IsTruocPT', 'IsSauPT', 'Exec', 'IsNumber', 'Length', 'ToDecimal', 'ToInt', 'ToDouble', 'Date', 'IsDate', 'ShowDate', 'ShowDateTime', 'StringLeft', 'StringRight', 'CleanCode', 'IsRange', 'In', 'ShortName', 'Base64Encode', 'Base64Decode', 'Red', 'Blue', 'Green', 'Purple', 'Orange', 'Grey', 'Color', 'Code', 'Mark', 'Value', 'Val', 'Contains', 'StartsWith', 'EndsWith', 'Replace', 'Substring', 'Trim', 'ToUpper', 'ToLower', 'Split', 'IndexOf', 'Any', 'Where', 'Select', 'OrderBy', 'OrderByDescending', 'GroupBy', 'ToList', 'FirstOrDefault', 'LastOrDefault', 'First', 'Last', 'Count', 'Sum', 'Max', 'Min', 'Average', 'Distinct', 'Take', 'Skip'];
    var knownFunctionsLower = knownFunctions.map(function(f) { return f.toLowerCase(); });
    
    var CheckXmlValidator = function() {};
    
    (function() {
        this.validate = function(code) {
            var annotations = [];
            var lines = code.split('\n');
            var parenStack = [], bracketStack = [], braceStack = [];
            var inMultiLineComment = false;
            var self = this;
            
            for (var row = 0; row < lines.length; row++) {
                var line = lines[row];
                var lineForParsing = line;
                
                if (inMultiLineComment) { var endIdx = line.indexOf('*/'); if (endIdx >= 0) { inMultiLineComment = false; lineForParsing = line.substring(endIdx + 2); } else { continue; } }
                var startIdx = lineForParsing.indexOf('/*'); if (startIdx >= 0) { var endInSame = lineForParsing.indexOf('*/', startIdx + 2); if (endInSame < 0) { inMultiLineComment = true; lineForParsing = lineForParsing.substring(0, startIdx); } }
                var commentIdx = lineForParsing.indexOf('//'); if (commentIdx >= 0) { lineForParsing = lineForParsing.substring(0, commentIdx); }
                
                this.trackBrackets(lineForParsing, row, parenStack, bracketStack, braceStack, annotations);
                this.checkFunctionCalls(lineForParsing, row, annotations);
                this.checkMissingSemicolon(lineForParsing, row, lines, annotations, braceStack);
            }
            
            parenStack.forEach(function(p) { annotations.push({ row: p.row, column: p.col, text: "❌ '(' chưa đóng", type: "error" }); });
            bracketStack.forEach(function(b) { annotations.push({ row: b.row, column: b.col, text: "❌ '[' chưa đóng", type: "error" }); });
            braceStack.forEach(function(b) { annotations.push({ row: b.row, column: b.col, text: "❌ '}' chưa đóng", type: "error" }); });
            
            return annotations;
        };
        
        this.trackBrackets = function(line, row, parenStack, bracketStack, braceStack, annotations) {
            var inStr = false, strChar = '';
            for (var i = 0; i < line.length; i++) {
                var c = line[i], prev = i > 0 ? line[i-1] : '';
                if ((c === '"' || c === "'") && prev !== '\\') { if (!inStr) { inStr = true; strChar = c; } else if (c === strChar) { inStr = false; } continue; }
                if (inStr) continue;
                if (c === '(') parenStack.push({ row: row, col: i });
                else if (c === ')') { if (parenStack.length === 0) { annotations.push({ row: row, column: i, text: "❌ ')' thừa", type: "error" }); } else { parenStack.pop(); } }
                else if (c === '[') bracketStack.push({ row: row, col: i });
                else if (c === ']') { if (bracketStack.length === 0) { annotations.push({ row: row, column: i, text: "❌ ']' thừa", type: "error" }); } else { bracketStack.pop(); } }
                else if (c === '{') braceStack.push({ row: row, col: i });
                else if (c === '}') { if (braceStack.length === 0) { annotations.push({ row: row, column: i, text: "❌ '}' thừa", type: "error" }); } else { braceStack.pop(); } }
            }
        };
        
        this.checkFunctionCalls = function(line, row, annotations) {
            var methodRegex = /\.([A-Za-z_][A-ZaZ0-9_]*)\s*\(/g;
            var match, self = this;
            var builtins = ['tostring', 'gethashcode', 'gettype', 'equals', 'add', 'remove', 'clear'];
            
            while ((match = methodRegex.exec(line)) !== null) {
                var funcName = match[1], funcLower = funcName.toLowerCase();
                if (builtins.indexOf(funcLower) >= 0) continue;
                
                var idx = knownFunctionsLower.indexOf(funcLower);
                if (idx >= 0) {
                    var correctName = knownFunctions[idx];
                    if (funcName !== correctName) { annotations.push({ row: row, column: match.index + 1, text: "📝 Nên dùng '" + correctName + "'", type: "warning" }); }
                }
            }
        };
        
        // Kiểm tra thiếu dấu ;
        this.checkMissingSemicolon = function(line, row, allLines, annotations, braceStack) {
            var trimmed = line.trim();
            
            // Bỏ qua các trường hợp không cần ;
            if (trimmed === '') return;
            if (trimmed.startsWith('//')) return;
            if (trimmed.startsWith('/*') || trimmed.startsWith('*') || trimmed.endsWith('*/')) return;
            if (trimmed.endsWith('{') || trimmed.endsWith('}')) return;
            if (trimmed.endsWith(',')) return;
            if (trimmed.endsWith(';')) return;
            
            // Bỏ qua các câu lệnh điều khiển
            if (/^\s*(if|else|for|foreach|while|do|switch|try|catch|finally|using|lock)\s*[\(\{]?/.test(trimmed)) return;
            if (/^\s*else\s*$/.test(trimmed)) return;
            if (/^\s*(case|default)\s*.*:/.test(trimmed)) return;
            
            // Bỏ qua lambda expressions chưa hoàn thành
            if (/=>\s*$/.test(trimmed)) return;
            if (/=>\s*\{/.test(trimmed)) return;
            
            // Bỏ qua khai báo class, struct, enum, namespace
            if (/^\s*(public|private|protected|internal|static|abstract|sealed|partial)?\s*(class|struct|enum|interface|namespace)\s+/.test(trimmed)) return;
            
            // Bỏ qua dòng bắt đầu bằng [ (attribute)
            if (trimmed.startsWith('[') && !trimmed.includes('=')) return;
            
            // Kiểm tra nếu dòng tiếp theo bắt đầu bằng . hoặc && hoặc || (chained expression)
            if (row + 1 < allLines.length) {
                var nextLine = allLines[row + 1].trim();
                if (nextLine.startsWith('.') || nextLine.startsWith('&&') || nextLine.startsWith('||') || nextLine.startsWith('?') || nextLine.startsWith(':')) {
                    return;
                }
            }
            
            // Kiểm tra dòng kết thúc bằng ) hoặc ] hoặc identifier và không có ; 
            // Đây là dấu hiệu của statement cần có ;
            if (/[\)\]"'\w]\s*$/.test(trimmed)) {
                // Đảm bảo không phải dòng chưa hoàn thành (có dấu mở ngoặc chưa đóng trong cùng dòng)
                var openParen = (trimmed.match(/\(/g) || []).length;
                var closeParen = (trimmed.match(/\)/g) || []).length;
                var openBracket = (trimmed.match(/\[/g) || []).length;
                var closeBracket = (trimmed.match(/\]/g) || []).length;
                
                if (openParen === closeParen && openBracket === closeBracket) {
                    // Có thể là statement thiếu ;
                    var endCol = line.length;
                    annotations.push({ 
                        row: row, 
                        column: endCol - 1, 
                        text: "❌ Thiếu dấu ';' cuối dòng", 
                        type: "error" 
                    });
                }
            }
        };
        
    }).call(CheckXmlValidator.prototype);
    
    t.CheckXmlValidator = CheckXmlValidator;
}),
define("ace/mode/checkxml_completions", ["require", "exports", "module"], function(e, t, n) {
    "use strict";
    
    var CheckXmlCompletions = function() {};
    
    (function() {
        // Lazy load - chỉ lấy data khi cần
        function getFieldsByType() {
            return {
                'TONG_HOP_130': checkXmlCompletions.xml1Fields || [],
                'CHI_TIET_THUOC_130': checkXmlCompletions.xml2Fields || [],
                'CHI_TIET_DVKT_130': checkXmlCompletions.xml3Fields || [],
                'CHI_TIET_CLS_130': checkXmlCompletions.xml4Fields || [],
                'CHI_TIET_DIEN_BIEN_BENH_130': checkXmlCompletions.xml5Fields || []
            };
        }
        
        var xmlVarToType = { 
            'XML1': 'TONG_HOP_130', 
            'XML2': 'CHI_TIET_THUOC_130', 
            'XML3': 'CHI_TIET_DVKT_130', 
            'XML4': 'CHI_TIET_CLS_130', 
            'XML5': 'CHI_TIET_DIEN_BIEN_BENH_130' 
        };

        function createDocHTML(doc) {
            if (!doc) return '';
            return '<div style="max-width:450px;font-family:Consolas,Monaco,monospace;font-size:12px;">' + doc.replace(/\n/g, '<br>') + '</div>';
        }
        
        // Detect lambda context: XML3.Select(x => x.
        function detectLambdaContext(beforeCursor) {
            // Tìm pattern: (tên_biến) => tên_biến. ở cuối chuỗi
            // Ví dụ: "XML3.Select(x => x." -> match "x => x."
            var lambdaPattern = /(\w+)\s*=>\s*\1\.(\w*)$/;
            var lambdaMatch = beforeCursor.match(lambdaPattern);
            
            if (!lambdaMatch) {
                return null;
            }
            
            var lambdaParam = lambdaMatch[1];
            var partialField = lambdaMatch[2] || '';
            
            // Tìm XML variable trong chuỗi trước lambda
            // Lấy phần trước "lambdaParam =>"
            var beforeLambda = beforeCursor.substring(0, beforeCursor.lastIndexOf(lambdaParam + ' =>') || beforeCursor.lastIndexOf(lambdaParam + '=>'));
            
            // Tìm XML1-XML5 trong beforeLambda
            var xmlPattern = /\b(XML[1-5])\b/gi;
            var xmlMatches = beforeLambda.match(xmlPattern);
            
            if (xmlMatches && xmlMatches.length > 0) {
                // Lấy XML variable cuối cùng được đề cập
                var xmlVar = xmlMatches[xmlMatches.length - 1].toUpperCase();
                return { 
                    xmlVar: xmlVar, 
                    lambdaParam: lambdaParam,
                    partialField: partialField
                };
            }
            
            // Fallback: tìm trong toàn bộ beforeCursor
            var allXmlMatches = beforeCursor.match(xmlPattern);
            if (allXmlMatches && allXmlMatches.length > 0) {
                return { 
                    xmlVar: allXmlMatches[0].toUpperCase(), 
                    lambdaParam: lambdaParam,
                    partialField: partialField
                };
            }
            
            return null;
        }

        this.getCompletions = function(state, session, pos, prefix) {
            var completions = [];
            var line = session.getLine(pos.row);
            var beforeCursor = line.substring(0, pos.column);
            var isAfterDot = /\.\w*$/.test(beforeCursor);
            
            // Lấy fieldsByType mỗi lần gọi để đảm bảo data luôn mới nhất
            var fieldsByType = getFieldsByType();
            
            // Debug log
            console.log('CheckXML getCompletions called:', { beforeCursor: beforeCursor, isAfterDot: isAfterDot, prefix: prefix });
            console.log('fieldsByType keys:', Object.keys(fieldsByType));
            console.log('xml1Fields count:', fieldsByType['TONG_HOP_130'] ? fieldsByType['TONG_HOP_130'].length : 0);
            
            // Lambda context - highest priority
            var lambdaContext = detectLambdaContext(beforeCursor);
            
            if (lambdaContext) {
                console.log('Lambda context detected:', lambdaContext);
                var typeName = xmlVarToType[lambdaContext.xmlVar];
                var fields = typeName ? fieldsByType[typeName] : [];
                
                fields.forEach(function(f) {
                    completions.push({ 
                        caption: f.name, 
                        value: f.name, 
                        score: 2000, 
                        meta: f.meta || 'Field', 
                        docHTML: createDocHTML(f.doc) 
                    });
                });
                completions.push({ caption: 'Val', value: 'Val', score: 1950, meta: 'Property' });
                completions.push({ caption: 'Value', value: 'Value', score: 1900, meta: 'Property' });
                
                console.log('Lambda completions count:', completions.length);
                return completions;
            }
            
            // Direct XML access: XML1. hoặc XML2[0].
            var xmlDirectMatch = beforeCursor.match(/\b(XML[1-5])(?:\[\d+\])?\.(\w*)$/i);
            console.log('xmlDirectMatch:', xmlDirectMatch);
            
            if (xmlDirectMatch) {
                var xmlVarName = xmlDirectMatch[1].toUpperCase();
                var typeForVar = xmlVarToType[xmlVarName];
                
                if (typeForVar && fieldsByType[typeForVar]) {
                    fieldsByType[typeForVar].forEach(function(f) {
                        completions.push({ 
                            caption: f.name, 
                            value: f.name, 
                            score: (f.score || 800) + 200, 
                            meta: f.meta || 'Field',
                            docHTML: createDocHTML(f.doc)
                        });
                    });
                    
                    // Thêm LINQ functions cho XML2-5 (vì là List)
                    if (xmlVarName !== 'XML1') {
                        checkXmlCompletions.linqFunctions.forEach(function(item) {
                            completions.push({ 
                                caption: item.name, 
                                value: item.name, 
                                score: item.score + 100, 
                                meta: item.meta,
                                docHTML: createDocHTML(item.doc)
                            });
                        });
                    }
                    
                    completions.push({ caption: 'Val', value: 'Val', score: 1950, meta: 'Property' });
                    completions.push({ caption: 'Value', value: 'Value', score: 1900, meta: 'Property' });
                    
                    console.log('XML direct completions count:', completions.length);
                    return completions;
                }
            }
            
            // Thêm các functions khác nếu cần
            if (isAfterDot) {
                checkXmlCompletions.xmlObjectFunctions.forEach(function(item) {
                    completions.push({ 
                        caption: item.name, 
                        value: item.snippet || item.name, 
                        score: item.score + 100, 
                        meta: item.meta,
                        docHTML: createDocHTML(item.doc)
                    });
                });
                checkXmlCompletions.stringFunctions.forEach(function(item) {
                    completions.push({ 
                        caption: item.name, 
                        value: item.name, 
                        score: item.score + 50, 
                        meta: item.meta,
                        docHTML: createDocHTML(item.doc)
                    });
                });
                checkXmlCompletions.colorFunctions.forEach(function(item) {
                    completions.push({ 
                        caption: item.name, 
                        value: item.name, 
                        score: item.score + 50, 
                        meta: item.meta,
                        docHTML: createDocHTML(item.doc)
                    });
                });
            }
            
            // Nếu không sau dấu ., thêm XML variables
            if (!isAfterDot) {
                checkXmlCompletions.xmlVariables.forEach(function(item) {
                    completions.push({ 
                        caption: item.name, 
                        value: item.name, 
                        score: item.score, 
                        meta: item.meta,
                        docHTML: createDocHTML(item.doc)
                    });
                });
            }
            

            return completions;
        };
    }).call(CheckXmlCompletions.prototype);
    
    t.CheckXmlCompletions = CheckXmlCompletions;
}),
define("ace/mode/checkxml", ["require", "exports", "module", "ace/lib/oop", "ace/mode/text", "ace/mode/checkxml_highlight_rules", "ace/mode/matching_brace_outdent_checkxml", "ace/mode/folding/checkxml", "ace/mode/checkxml_completions", "ace/mode/checkxml_worker"], function (e, t, n) {
    "use strict";
    var r = e("../lib/oop"),
        i = e("./text").Mode,
        s = e("./checkxml_highlight_rules").CheckXmlHighlightRules,
        o = e("./matching_brace_outdent_checkxml").MatchingBraceOutdent,
        u = e("./folding/checkxml").FoldMode,
        c = e("./checkxml_completions").CheckXmlCompletions,
        v = e("./checkxml_worker").CheckXmlValidator,
        a = function () {
            this.HighlightRules = s;
            this.$outdent = new o;
            this.$behaviour = this.$defaultBehaviour;
            this.foldingRules = new u;
            this.$completer = new c;
            this.$validator = new v;
        };

    r.inherits(a, i);

    (function () {
        this.lineCommentStart = "//";
        this.blockComment = { start: "/*", end: "*/" };

        this.getNextLineIndent = function (e, t, n) { var r = this.$getIndent(t), i = this.getTokenizer().getLineTokens(t, e), s = i.tokens; if (s.length && s[s.length - 1].type == "comment") return r; if (e == "start") { var o = t.match(/^.*[\{\(\[]\s*$/); o && (r += n) } return r };
        this.checkOutdent = function (e, t, n) { return this.$outdent.checkOutdent(t, n) };
        this.autoOutdent = function (e, t, n) { this.$outdent.autoOutdent(t, n) };

        this.createWorker = function (session) {
            var self = this;
            var validateTimeout = null;
            
            var runValidation = function() {
                if (validateTimeout) clearTimeout(validateTimeout);
                validateTimeout = setTimeout(function() {
                    try {
                        var code = session.getValue();
                        if (!code || code.trim() === '') { 
                            session.setAnnotations([]); 
                            if (session.$checkxmlMarkers) { 
                                session.$checkxmlMarkers.forEach(function(id) { session.removeMarker(id); }); 
                            } 
                            session.$checkxmlMarkers = []; 
                            return; 
                        }
                        code = code.trim();
                        if (code[code.length - 1] !== ';') { code += ';'; }
                        var annotations = self.$validator.validate(code);
                        session.setAnnotations(annotations || []);
                        self.addErrorMarkers(session, annotations || []);
                    } catch (e) { 
                        console.error('CheckXML Validator error:', e); 
                    }
                }, 300);
            };
            
            session.on("change", runValidation);
            setTimeout(runValidation, 2000);
            
            // Setup dot trigger - cố gắng nhiều lần để đảm bảo editor đã sẵn sàng
            var setupAttempts = 0;
            var trySetupDotTrigger = function() {
                setupAttempts++;
                try {
                    var editor = null;
                    // Thử nhiều cách để lấy editor
                    if (session.selection && session.selection.$editor) {
                        editor = session.selection.$editor;
                    } else if (session.$editor) {
                        editor = session.$editor;
                    }
                    
                    if (editor) {
                        self.setupDotTrigger(editor);
                        console.log('CheckXML dot trigger setup successful after', setupAttempts, 'attempts');
                    } else if (setupAttempts < 10) {
                        // Thử lại sau 200ms
                        setTimeout(trySetupDotTrigger, 200);
                    }
                } catch(e) {
                    if (setupAttempts < 10) {
                        setTimeout(trySetupDotTrigger, 200);
                    }
                }
            };
            setTimeout(trySetupDotTrigger, 100);
            
            return null;
        };
        
        this.setupDotTrigger = function(editor) {
            if (!editor || editor.$checkxmlDotTriggerSetup) return;
            editor.$checkxmlDotTriggerSetup = true;
            
            editor.commands.on('afterExec', function(e) {
                if (e.command.name === 'insertstring' && e.args === '.') {
                    // Kiểm tra mode có phải checkxml không
                    var session = editor.getSession();
                    var mode = session.getMode();
                    if (mode && mode.$id && mode.$id.indexOf('checkxml') !== -1) {
                        setTimeout(function() { 
                            editor.execCommand('startAutocomplete'); 
                        }, 10);
                    }
                }
            });
            
            console.log('CheckXML dot trigger setup for editor');
        };
        
        this.addErrorMarkers = function(session, annotations) {
            var Range = ace.require("ace/range").Range;
            if (session.$checkxmlMarkers) { session.$checkxmlMarkers.forEach(function(id) { session.removeMarker(id); }); }
            session.$checkxmlMarkers = [];
            
            annotations.forEach(function(ann) {
                if (ann.type === 'error' || ann.type === 'warning') {
                    var line = session.getLine(ann.row);
                    var col = ann.column || 0, endCol = col + 1;
                    if (line && col < line.length) { var match = line.substring(col).match(/^[\w\.\[\]]+/); if (match) { endCol = col + match[0].length; } }
                    if (endCol <= col) endCol = col + 1;
                    if (line && endCol > line.length) endCol = line.length;
                    var markerClass = ann.type === 'error' ? 'ace_error-marker' : 'ace_warning-marker';
                    var markerId = session.addMarker(new Range(ann.row, col, ann.row, endCol), markerClass, 'text', true);
                    session.$checkxmlMarkers.push(markerId);
                }
            });
        };
        
        this.getCompletions = function(state, session, pos, prefix) { return this.$completer.getCompletions(state, session, pos, prefix); };
        this.$id = "ace/mode/checkxml"
    }).call(a.prototype);

    t.Mode = a
});


// Register completer with Ace language tools - improved version
(function() {
    function createDocHTML(doc) {
        if (!doc) return '';
        return '<div style="max-width:450px;font-family:Consolas,Monaco,monospace;font-size:12px;">' + doc.replace(/\n/g, '<br>') + '</div>';
    }
    
    function registerCompleter() {
        if (typeof ace === 'undefined' || !ace.require) {
            console.log('CheckXML: ace not available yet');
            return false;
        }
        
        try {
            var langTools = ace.require("ace/ext/language_tools");
            if (!langTools) {
                console.log('CheckXML: language_tools not loaded');
                return false;
            }
            
            // Tạo completer cho CheckXML mode
            var checkXmlCompleter = {
                getCompletions: function(editor, session, pos, prefix, callback) {
                    // Kiểm tra mode có phải checkxml không
                    var mode = session.getMode();
                    var modeId = mode ? mode.$id : null;
                    var isCheckXmlMode = modeId && modeId.indexOf('checkxml') !== -1;
                    
                    if (!mode || !isCheckXmlMode) {
                        callback(null, []);
                        return;
                    }
                    
                    // Tính toán prefix đúng - chỉ lấy phần sau dấu '.'
                    var line = session.getLine(pos.row);
                    var beforeCursor = line.substring(0, pos.column);
                    var dotMatch = beforeCursor.match(/\.(\w*)$/);
                    var actualPrefix = dotMatch ? dotMatch[1] : prefix;
                    
                    var completions = [];
                    var xmlVarToType = { 
                        'XML1': 'TONG_HOP_130', 
                        'XML2': 'CHI_TIET_THUOC_130', 
                        'XML3': 'CHI_TIET_DVKT_130', 
                        'XML4': 'CHI_TIET_CLS_130', 
                        'XML5': 'CHI_TIET_DIEN_BIEN_BENH_130' 
                    };
                    var fieldsByType = {
                        'TONG_HOP_130': checkXmlCompletions.xml1Fields || [],
                        'CHI_TIET_THUOC_130': checkXmlCompletions.xml2Fields || [],
                        'CHI_TIET_DVKT_130': checkXmlCompletions.xml3Fields || [],
                        'CHI_TIET_CLS_130': checkXmlCompletions.xml4Fields || [],
                        'CHI_TIET_DIEN_BIEN_BENH_130': checkXmlCompletions.xml5Fields || []
                    };
                    
                    var isAfterDot = /\.\w*$/.test(beforeCursor);
                    
                    // Detect lambda context: XML3.Select(x => x.
                    var lambdaPattern = /(\w+)\s*=>\s*\1\.(\w*)$/;
                    var lambdaMatch = beforeCursor.match(lambdaPattern);
                    
                    if (lambdaMatch) {
                        var lambdaParam = lambdaMatch[1];
                        var beforeLambda = beforeCursor.substring(0, beforeCursor.lastIndexOf(lambdaParam + ' =>') || beforeCursor.lastIndexOf(lambdaParam + '=>'));
                        var xmlPattern = /\b(XML[1-5])\b/gi;
                        var xmlMatches = beforeLambda.match(xmlPattern);
                        
                        if (xmlMatches && xmlMatches.length > 0) {
                            var xmlVar = xmlMatches[xmlMatches.length - 1].toUpperCase();
                            var typeName = xmlVarToType[xmlVar];
                            var fields = typeName ? fieldsByType[typeName] : [];
                            
                            fields.forEach(function(f) {
                                completions.push({ 
                                    caption: f.name, 
                                    value: f.name, 
                                    score: 2000, 
                                    meta: f.meta || 'Field', 
                                    docHTML: createDocHTML(f.doc) 
                                });
                            });
                            completions.push({ caption: 'Val', value: 'Val', score: 1950, meta: 'Property' });
                            completions.push({ caption: 'Value', value: 'Value', score: 1900, meta: 'Property' });
                        }
                    }
                    
                    // Direct XML access: XML1. hoặc XML2[0].
                    if (completions.length === 0) {
                        var xmlDirectMatch = beforeCursor.match(/\b(XML[1-5])(?:\[\d+\])?\.(\w*)$/i);
                        
                        if (xmlDirectMatch) {
                            var xmlVarName = xmlDirectMatch[1].toUpperCase();
                            var typeForVar = xmlVarToType[xmlVarName];
                            
                            if (typeForVar && fieldsByType[typeForVar]) {
                                fieldsByType[typeForVar].forEach(function(f) {
                                    completions.push({ 
                                        caption: f.name, 
                                        value: f.name, 
                                        score: (f.score || 800) + 200, 
                                        meta: f.meta || 'Field',
                                        docHTML: createDocHTML(f.doc)
                                    });
                                });
                                
                                // Thêm LINQ functions cho XML2-5 (vì là List)
                                if (xmlVarName !== 'XML1') {
                                    checkXmlCompletions.linqFunctions.forEach(function(item) {
                                        completions.push({ 
                                            caption: item.name, 
                                            value: item.name, 
                                            score: item.score + 100, 
                                            meta: item.meta,
                                            docHTML: createDocHTML(item.doc)
                                        });
                                    });
                                }
                                
                                completions.push({ caption: 'Val', value: 'Val', score: 1950, meta: 'Property' });
                                completions.push({ caption: 'Value', value: 'Value', score: 1900, meta: 'Property' });
                            }
                        }
                    }
                    
                    // Thêm các functions khác nếu cần
                    if (isAfterDot && completions.length === 0) {
                        checkXmlCompletions.xmlObjectFunctions.forEach(function(item) {
                            completions.push({ 
                                caption: item.name, 
                                value: item.snippet || item.name, 
                                score: item.score + 100, 
                                meta: item.meta,
                                docHTML: createDocHTML(item.doc)
                            });
                        });
                        checkXmlCompletions.stringFunctions.forEach(function(item) {
                            completions.push({ 
                                caption: item.name, 
                                value: item.name, 
                                score: item.score + 50, 
                                meta: item.meta,
                                docHTML: createDocHTML(item.doc)
                            });
                        });
                        checkXmlCompletions.colorFunctions.forEach(function(item) {
                            completions.push({ 
                                caption: item.name, 
                                value: item.name, 
                                score: item.score + 50, 
                                meta: item.meta,
                                docHTML: createDocHTML(item.doc)
                            });
                        });
                    }
                    
                    // Nếu không sau dấu ., thêm XML variables
                    if (!isAfterDot) {
                        checkXmlCompletions.xmlVariables.forEach(function(item) {
                            completions.push({ 
                                caption: item.name, 
                                value: item.name, 
                                score: item.score, 
                                meta: item.meta,
                                docHTML: createDocHTML(item.doc)
                            });
                        });
                    }
                    
                    // Filter theo actualPrefix
                    var filteredCompletions = completions.filter(function(c) {
                        return c.caption.indexOf(actualPrefix) === 0;
                    });
                    
                    console.log('CheckXML returning', filteredCompletions.length, 'completions (prefix:', actualPrefix, ')');
                    callback(null, filteredCompletions);
                },
                getDocTooltip: function(item) {
                    if (item.docHTML) {
                        return { docHTML: item.docHTML };
                    }
                }
            };
            
            // Thêm completer vào đầu danh sách để ưu tiên
            langTools.addCompleter(checkXmlCompleter);
            
            console.log('CheckXML completer registered successfully');
            return true;
        } catch (e) {
            console.error('Failed to register CheckXML completer:', e);
            return false;
        }
    }
    
    // Thử đăng ký ngay
    if (!registerCompleter()) {
        // Nếu chưa được, thử lại khi DOM ready
        if (typeof document !== 'undefined') {
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', function() {
                    setTimeout(registerCompleter, 100);
                });
            } else {
                setTimeout(registerCompleter, 100);
            }
        }
    }
    
    // Export function để có thể gọi lại nếu cần
    if (typeof window !== 'undefined') {
        window.registerCheckXmlCompleter = registerCompleter;
    }
})();

// Setup dot trigger for autocomplete - improved version
(function() {
    function setupDotTriggerForEditor(editor) {
        if (!editor || editor.$checkxmlDotTriggerSetup) return;
        
        editor.$checkxmlDotTriggerSetup = true;
        
        // Lắng nghe sự kiện input để trigger autocomplete khi nhấn '.'
        editor.commands.on('afterExec', function(e) {
            if (e.command.name === 'insertstring') {
                var char = e.args;
                // Trigger autocomplete khi nhấn '.' hoặc các ký tự bắt đầu identifier
                if (char === '.') {
                    var session = editor.getSession();
                    var mode = session.getMode();
                    if (mode && mode.$id && mode.$id.indexOf('checkxml') !== -1) {
                        setTimeout(function() {
                            editor.execCommand('startAutocomplete');
                        }, 10);
                    }
                }
            }
        });
        
        console.log('CheckXML dot trigger setup for editor');
    }
    
    // Export để có thể gọi từ bên ngoài
    if (typeof window !== 'undefined') {
        window.setupCheckXmlDotTrigger = setupDotTriggerForEditor;
    }
})();
