/* eslint-disable no-useless-escape */
/* eslint-disable @typescript-eslint/no-explicit-any */
// declare global {
//   interface StringConstructor {
//     isValidEmail(value: any): boolean;
//     isValidPhone(value: any): boolean;
//     isValidCode(value: any): boolean;
//     changeAliasLowerCase(alias: any): string;
//     aliasIncludes(alias: any, searchText: any): boolean;
//     format(template: string, ...arr: any[]): string;
//     templateToDisplay(template: string, entity: any): string; // Convert theo format ${key}
//     encrypt(obj: any): string;
//     decrypt(encripted: string): any;
//     isNullOrEmpty(value: any): boolean;
//     isNullOrWhiteSpace(value: any): boolean;
//   }
// }
const REGEX_EMAIL = '^(([^<>()[\\].,;:\\s@"]+(\\.[^<>()[\\].,;:\\s@"]+)*)|(".+"))@(([^<>()[\\].,;:\\s@"]+\\.)+[^<>()[\\].,;:\\s@"]{2,})$';
const REGEX_PHONE = '^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\\s./0-9]*$';
const REGEX_PHONE_VN = '^(?:\\+84|0|84)(3[2-9]|5[2689]|7[06-9]|8[1-689]|9[0-9])\\d{7}$';
const REGEX_IDVN_OR_PASSPORT = '^(\\d{9}|\\d{12}|[A-Z]\\d{7})$';
const isValidEmail = (value) => {
    if (!value) {
        return false;
    }
    const re = new RegExp(REGEX_EMAIL);
    return re.test(value);
};
const isValidPhone = (value) => {
    if (!value) {
        return false;
    }
    const re = new RegExp(REGEX_PHONE);
    return re.test(value);
};
const isValidCode = (value) => {
    if (!value) {
        return false;
    }
    const re = /^[a-zA-Z0-9\@\_\-]{2,20}$/;
    return re.test(value);
};
const isNullOrEmpty = (value) => {
    return value === undefined || value === null || value === '';
};
const isNullOrWhiteSpace = (value) => {
    return value === undefined || value === null || typeof value !== 'string' || value.match(/^ *$/) !== null;
};
const changeAliasLowerCase$1 = (alias) => {
    let str = alias?.toString() ?? '';
    str = str.toString().toLowerCase();
    str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, 'a');
    str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, 'e');
    str = str.replace(/ì|í|ị|ỉ|ĩ/g, 'i');
    str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o');
    str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u');
    str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y');
    str = str.replace(/đ/g, 'd');
    str = str.replace(/!|@|%|\^|\*|\(|\)|\+|\=|\<|\>|\?|\/|,|\.|\:|\;|\'|\"|\&|\#|\[|\]|~|\$|_|`|-|{|}|\||\\/g, ' ');
    str = str.replace(/ + /g, ' ');
    str = str.trim();
    return str;
};
const aliasIncludes = (alias, searchText) => {
    return changeAliasLowerCase$1(alias).includes(changeAliasLowerCase$1(searchText));
};
const format = (template, ...arr) => {
    for (let i = 0; i < arr.length; i++) {
        const regexp = new RegExp(`\\{${i}\\}`, 'gi');
        template = template.replace(regexp, arr[i]);
    }
    return template;
};
// Convert: `[${code}] ${name}` => `[Mã] Tên`;
const templateToDisplay = (template, entity) => {
    if (!template) {
        return template;
    }
    const regex = /\$\{([A-Za-z0-9._-]*)\}/g;
    const matches = template.match(regex) || [];
    for (const match of matches) {
        const key = match.slice(2, match.length - 1);
        if (key) {
            template = template.replace(match, entity?.[key] || '');
        }
    }
    return template;
};
// Convert: `${name} === "Tên"` => true/false;
const parseExpression = (template, entity) => {
    if (!template) {
        return undefined;
    }
    const regex = /\$\{([A-Za-z0-9._-]*)\}/g;
    const matches = template.match(regex) || [];
    for (const match of matches) {
        const key = match.slice(2, match.length - 1);
        if (key) {
            // Xử lý trong trường hợp key có định dạng a.b.c ...
            let val = entity;
            const strs = key.split('.');
            for (const str of strs) {
                val = val?.[str];
            }
            if (typeof val === 'string') {
                // Nếu là chuỗi thì thêm dấu nháy khi replace giá trị để so sánh, ví dụ 'a' === 'a'
                // Nếu là số hay boolean thì không cần, ví dụ 121 === 121
                template = template.replace(match, `'${val}'`);
            }
            else {
                // Nếu là object/array thì phải dùng JSON.stringify
                if (typeof val === 'object' && !!val) {
                    template = template.replace(match, JSON.stringify(val) ?? `undefined`);
                }
                else {
                    template = template.replace(match, val ?? `undefined`);
                }
            }
        }
    }
    if (!template) {
        return undefined;
    }
    try {
        const result = new Function('return ' + template)();
        return result;
    }
    catch (ex) {
        console.error(ex);
        return undefined;
    }
};
const SALT = 'cb9f4b2a-d26c-4787-a66e-e7130ee00f95';
const encrypt = (obj) => {
    obj = JSON.stringify(obj).split('');
    for (let i = 0, l = obj.length; i < l; i++)
        if (obj[i] == '{')
            obj[i] = '}';
        else if (obj[i] == '}')
            obj[i] = '{';
    return encodeURI(SALT + obj.join(''));
};
const decrypt = (encripted) => {
    encripted = decodeURI(encripted);
    if (SALT && encripted.indexOf(SALT) != 0)
        throw new Error('object cannot be decrypted');
    const strs = encripted.substring(SALT.length).split('');
    for (let i = 0, l = strs.length; i < l; i++)
        if (strs[i] == '{')
            strs[i] = '}';
        else if (encripted[i] == '}')
            strs[i] = '{';
    return JSON.parse(strs.join(''));
};
// Conver "Đội Kỹ Thuật" thành "doi_ky_thuat"
const convertToSnakeCaseCode = (name) => {
    if (typeof name !== 'string') {
        throw new Error('Invalid name');
    }
    return name
        .normalize('NFD') // Tách dấu
        .replace(/[\u0300-\u036f]/g, '') // Xóa các dấu vừa tách
        .replace(/[đĐ]/g, 'd') // Quan trọng: Xử lý chữ đ/Đ thành d
        .trim()
        .replace(/[^a-zA-Z0-9]+/g, '_') // Thay ký tự đặc biệt bằng _
        .replace(/^_+|_+$/g, '') // Xóa _ ở đầu/cuối
        .toLowerCase(); // Chuyển thành chữ thường: doi_ky_thuat
};
/**
 * Tạo code duy nhất, tự động thêm suffix _n nếu bị trùng
 * @param name Tên cần convert
 * @param existingCodes Danh sách các code đã tồn tại
 */
const generateUniqueCode = (name, existingCodes) => {
    // 1. Tạo base code trước
    const baseCode = convertToSnakeCaseCode(name);
    // 2. Nếu code chưa tồn tại thì trả về luôn
    if (!existingCodes.includes(baseCode)) {
        return baseCode;
    }
    // 3. Nếu trùng, bắt đầu loop để tìm index phù hợp
    let index = 1;
    let newCode = `${baseCode}_${index}`;
    while (existingCodes.includes(newCode)) {
        index++;
        newCode = `${baseCode}_${index}`;
    }
    return newCode;
};
const StringUtilities = {
    REGEX_EMAIL,
    REGEX_PHONE,
    REGEX_PHONE_VN,
    REGEX_IDVN_OR_PASSPORT,
    isValidEmail,
    isValidPhone,
    isValidCode,
    changeAliasLowerCase: changeAliasLowerCase$1,
    aliasIncludes,
    format,
    templateToDisplay,
    parseExpression,
    encrypt,
    decrypt,
    isNullOrEmpty,
    isNullOrWhiteSpace,
    convertToSnakeCaseCode,
    generateUniqueCode,
};

/* eslint-disable @typescript-eslint/no-explicit-any */
// export {};
// declare global {
//   interface Array<T = any> {
//     search(searchText: string | number, fields?: (string | undefined) | (string | undefined)[], children?: string): T[];
//     paging(pageSize: number, page?: number): T[];
//     toObject(key: string): Record<string, T>;
//     union(key: string, ...args: T[][]): T[];
//     distinct(): T[];
//   }
//   interface ArrayConstructor {
//     search<T = any>(items: T[], searchText: string | number, fields?: (string | undefined) | (string | undefined)[]): T[];
//     union<T = any>(key: string, ...args: T[][]): T[];
//     toObject<T = any>(key: string, items: T[]): Record<string, T>;
//   }
// }
const search = (items, searchText, fields, children) => {
    if (!searchText?.toString()) {
        return items;
    }
    if (!items?.length) {
        return items;
    }
    if (Array.isArray(fields)) {
        const fs = fields.filter(e => e !== undefined && e !== null && e !== '');
        if (!fs.length) {
            return items.filter(item => item !== undefined && item !== null && StringUtilities.aliasIncludes(item, searchText));
        }
        return items.filter(item => (item !== undefined && item !== null && fs.some(field => StringUtilities.aliasIncludes(item[field], searchText))) ||
            (children && search(item[children], searchText, fields, children)?.length));
    }
    if (!fields) {
        return items.filter(item => item !== undefined && item !== null && StringUtilities.aliasIncludes(item, searchText));
    }
    return items.filter(item => (item !== undefined && item !== null && StringUtilities.aliasIncludes(item[fields], searchText)) ||
        (children && search(item[children], searchText, fields, children)?.length));
};
// Array.prototype.search = function <T = any>(
//   searchText: any,
//   fields?: (string | undefined) | (string | undefined)[],
//   children?: string
// ): T[] {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   const items: T[] = this;
//   return search(items, searchText, fields, children);
// };
// Array.prototype.paging = function <T extends Record<string, any> = any>(pageSize: number, page = 0): T[] {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   const items: T[] = this;
//   return items.filter((_, index) => {
//     return index >= page * pageSize && index < (page + 1) * pageSize;
//   });
// };
// Array.prototype.toObject = function <T = any>(key: string): Record<string, T> {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   const items: T[] = this;
//   return items
//     .filter(item => item !== undefined && item !== null)
//     .reduce<Record<string, any>>((obj, item: Record<string, any>) => {
//       const objKey = item?.[key]?.toString();
//       if (typeof objKey === 'string') {
//         obj[objKey] = item;
//       }
//       return obj;
//     }, {});
// };
// Array.prototype.union = function <T = Record<string, any>>(key: string, ...args: T[][]) {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   let results: T[] = this;
//   const filterUnion = <T = any>(val: T, index: number, self: T[]) =>
//     val !== undefined && val !== null && self.findIndex(e => (e as any)[key] === (val as any)[key]) === index;
//   if (!args?.length) {
//     return results.filter(filterUnion);
//   }
//   for (const arg of args) {
//     if (Array.isArray(arg)) {
//       results = [...results, ...arg].filter(filterUnion);
//     }
//   }
//   return results;
// };
// Array.prototype.distinct = function () {
//   if (Array.isArray(this)) {
//     return [...new Set(this)];
//   }
//   return this;
// };
const union = (key, ...args) => {
    let results = [];
    const filterUnion = (val, index, self) => val !== undefined && val !== null && self.findIndex(e => e[key] === val[key]) === index;
    if (!args?.length) {
        return [];
    }
    for (const arg of args) {
        if (Array.isArray(arg)) {
            results = [...results, ...arg].filter(filterUnion);
        }
    }
    return results;
};
const toObject = (key, items) => {
    if (!Array.isArray(items)) {
        return {};
    }
    return items
        .filter(item => item !== undefined && item !== null)
        .reduce((obj, item) => {
        const objKey = item?.[key]?.toString();
        if (typeof objKey === 'string') {
            obj[objKey] = item;
        }
        return obj;
    }, {});
};
const distinct = (items) => {
    if (Array.isArray(items)) {
        return [...new Set(items)];
    }
    return [];
};
const paging = (items, pageSize, page = 0) => {
    if (!Array.isArray(items)) {
        return [];
    }
    return items.filter((_, index) => {
        return index >= page * pageSize && index < (page + 1) * pageSize;
    });
};
const ArrayUtilities = {
    search,
    union,
    toObject,
    distinct,
    paging,
};

/* eslint-disable @typescript-eslint/no-explicit-any */
// export {};
// declare global {
//   interface NumberConstructor {
//     toVNCurrency(value: any): string | null;
//     toISO(value: any): string | null;
//     isNumber(value: any): boolean;
//     isPositiveInteger(value: any): boolean;
//     isPositiveNumber(value: any): boolean;
//     round(value: any, digits?: number): number | null;
//   }
// }
const toVNCurrency = (value) => {
    value = (value ?? '').toString().replace(/,/g, '');
    if (!value) {
        return null;
    }
    const val = +value;
    if (!Number.isNaN(val)) {
        return val.toLocaleString('vi-VN', { maximumFractionDigits: 10 });
    }
    return null;
};
const toVN = (value) => {
    value = (value ?? '').toString().replace(/,/g, '');
    if (!value) {
        return null;
    }
    const val = +value;
    if (!Number.isNaN(val)) {
        return val.toLocaleString('vi-VN', { maximumFractionDigits: 10 });
    }
    return null;
};
const toISO = (value) => {
    value = (value ?? '').toString().replace(/,/g, '');
    if (!value) {
        return null;
    }
    const val = +value;
    if (!Number.isNaN(val)) {
        return val.toLocaleString('en-US', { maximumFractionDigits: 10 });
    }
    return null;
};
const isPositiveInteger = (value) => {
    if (!value) {
        return false;
    }
    const regex = /^([0-9]*)$/;
    return regex.test(value) && +value > 0;
};
const isPositiveNumber = (value) => {
    if (!value) {
        return false;
    }
    const regex = /^([0-9]*)(\.[0-9]+$){0,1}$/;
    return regex.test(value) && +value > 0;
};
const isNumber = (value) => {
    if (value === undefined || value === null || value === '') {
        return false;
    }
    return !Number.isNaN(+value);
};
const round = (value, digits) => {
    digits = digits ?? 2;
    if (!NumberUtilities.isNumber(value)) {
        return null;
    }
    const val = Math.pow(10, digits);
    return Math.round(value * val) / val;
};
const NumberUtilities = {
    toVNCurrency,
    toVN,
    toISO,
    isPositiveInteger,
    isPositiveNumber,
    isNumber,
    round,
};

/* eslint-disable @typescript-eslint/no-explicit-any */
// export {};
// declare global {
//   interface Date {
//     begin(): Date;
//     end(): Date;
//     addMiliseconds(miliseconds: number): Date;
//     addHours(hours: number): Date;
//     addDays(days: number): Date;
//     addMonths(months: number): Date;
//     toFormat(format: string): string;
//   }
//   interface DateConstructor {
//     equal(date1: any, date2: any): boolean;
//     dayDiff(date1: any, date2: any): number | null;
//     monthDiff(date1: any, date2: any): number | null;
//     yearDiff(date1: any, date2: any): number | null;
//     age(date1: any, date2: any): number | null;
//     parseFrom(value: any, format: string): Date | null;
//     isDate(value: any): boolean;
//     toFormat(value: any, format: string): string;
//     addMiliseconds(value: any, miliseconds: number): Date | null;
//     addHours(value: any, hours: number): Date | null;
//     addDays(value: any, days: number): Date | null;
//     addMonths(value: any, months: number): Date | null;
//     begin(value: any): Date | null;
//     end(value: any): Date | null;
//     timeDifference(previous: any, current?: any): string;
//   }
// }
const isDate = (value) => {
    if (!value) {
        return false;
    }
    if (typeof value === 'string') {
        // Kiểm tra regex
        if (value.length < 8) {
            return false;
        }
        else {
            const date1 = value.substring(0, 10); // yyyy/MM/dd
            const date2 = value.substring(0, 9); // yyyy/M/dd, yyyy/MM/d
            const date3 = value.substring(0, 8); // yyyy/M/d
            const regex1 = /^(0[1-9]|[1-9]|1[0-2])(-|\/)(0[1-9]|[1-9]|[12][0-9]|3[01])(-|\/)(\d{4})$/; // MM/dd/yyyy, MM-dd-yyyy
            const regex2 = /^(\d{4})(-|\/)(0[1-9]|[1-9]|1[0-2])(-|\/)(0[1-9]|[1-9]|[12][0-9]|3[01])$/; // yyyy/MM/dd, yyyy-MM-dd
            const result = regex1.test(date1) || regex2.test(date1) || regex1.test(date2) || regex2.test(date2) || regex1.test(date3) || regex2.test(date3);
            if (result) {
                const date = new Date(value);
                return !isNaN(date.getTime());
            }
            else {
                return false;
            }
        }
    }
    else {
        const date = new Date(value);
        return !isNaN(date.getTime());
    }
};
// const toFormatBackup = (value: any, format: string): string => {
//   if (!isDate(value)) {
//     return '';
//   }
//   const date: Date = new Date(value);
//   let result = format;
//   result = result.replace('yyyy', date.getFullYear().toString());
//   result = result.replace('MM', (date.getMonth() + 1).toString().padStart(2, '0'));
//   result = result.replace('dd', date.getDate().toString().padStart(2, '0'));
//   result = result.replace('HH', date.getHours().toString().padStart(2, '0'));
//   result = result.replace('mm', date.getMinutes().toString().padStart(2, '0'));
//   result = result.replace('ss', date.getSeconds().toString().padStart(2, '0'));
//   return result;
// };
const toFormat = (value, format) => {
    if (!isDate(value)) {
        return '';
    }
    const isDatePart = (type) => {
        return type === 'year' || type === 'month' || type === 'day' || type === 'hour' || type === 'minute' || type === 'second';
    };
    const date = new Date(value);
    const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
    const parts = new Intl.DateTimeFormat('en-GB', {
        timeZone,
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        second: '2-digit',
        hour12: false,
    }).formatToParts(date);
    const map = {};
    parts.forEach(part => {
        if (isDatePart(part.type)) {
            map[part.type] = part.value;
        }
    });
    return format
        .replace('yyyy', map.year ?? '')
        .replace('MM', map.month ?? '')
        .replace('dd', map.day ?? '')
        .replace('HH', map.hour ?? '')
        .replace('mm', map.minute ?? '')
        .replace('ss', map.second ?? '');
};
const addDays = (value, days) => {
    if (!isDate(value)) {
        return null;
    }
    const date = new Date(value);
    date.setDate(date.getDate() + days);
    return date;
};
const addMiliseconds = (value, miliseconds) => {
    if (!isDate(value)) {
        return null;
    }
    const date = new Date(value);
    date.setMilliseconds(date.getMilliseconds() + miliseconds);
    return date;
};
const begin = (value) => {
    if (!isDate(value)) {
        return null;
    }
    return new Date(toFormat(value, 'MM/dd/yyyy'));
};
const end = (value) => {
    if (!isDate(value)) {
        return null;
    }
    return addMiliseconds(begin(addDays(value, 1)), -1);
};
const equal = (date1, date2) => {
    if (!isDate(date1) && !isDate(date2)) {
        return true;
    }
    if (!isDate(date1) || !isDate(date2)) {
        return false;
    }
    return new Date(date1).getTime() === new Date(date2).getTime();
};
const dayDiff = (date1, date2) => {
    if (!isDate(date1) || !isDate(date2)) {
        return null;
    }
    const diff = new Date(date2).getTime() - new Date(date1).getTime();
    return Math.floor(diff / (24 * 3600 * 1000));
};
const monthDiff = (date1, date2) => {
    if (!isDate(date1) || !isDate(date2)) {
        return null;
    }
    const d1Y = date1.getFullYear();
    const d2Y = date2.getFullYear();
    const d1M = date2.getMonth();
    const d2M = date2.getMonth();
    return d2M + 12 * d2Y - (d1M + 12 * d1Y);
};
const yearDiff = (date1, date2) => {
    if (!isDate(date1) || !isDate(date2)) {
        return null;
    }
    return new Date(date2).getFullYear() - new Date(date1).getFullYear();
};
const age = (date1, date2) => {
    const diff = monthDiff(date1, date2);
    if (!diff) {
        return null;
    }
    return NumberUtilities.round(diff / 12);
};
const parseFrom = (value, format) => {
    if (!value) {
        return null;
    }
    if (!format) {
        return null;
    }
    value = value.toString();
    const dmy = format.indexOf('dd') > -1 && format.indexOf('MM') > -1 && format.indexOf('yyyy') > -1;
    const hms = format.indexOf('HH') > -1 || format.indexOf('mm') > -1 || format.indexOf('ss') > -1;
    let strDate = '';
    if (dmy) {
        const dd = value.substr(format.indexOf('dd'), 'dd'.length);
        const MM = value.substr(format.indexOf('MM'), 'MM'.length);
        const yyyy = value.substr(format.indexOf('yyyy'), 'yyyy'.length);
        if (+yyyy > 0 && +MM > 0 && +dd > 0) {
            strDate += `${MM}/${dd}/${yyyy}`;
        }
        else {
            return null;
        }
    }
    else {
        const today = new Date();
        const yyyy = today.getFullYear().toString();
        const MM = (today.getMonth() + 1).toString().padStart(2, '0');
        const dd = today.getDate().toString().padStart(2, '0');
        strDate += `${MM}/${dd}/${yyyy}`;
    }
    if (hms) {
        const HH = format.indexOf('HH') > -1 ? value.substr(format.indexOf('HH'), 'HH'.length) : '00';
        const mm = format.indexOf('mm') > -1 ? value.substr(format.indexOf('mm'), 'mm'.length) : '00';
        const ss = format.indexOf('ss') > -1 ? value.substr(format.indexOf('ss'), 'ss'.length) : '00';
        strDate += ` ${HH || '00'}:${mm || '00'}:${ss || '00'}`;
    }
    if (!isDate(strDate)) {
        return null;
    }
    return new Date(strDate);
};
const addHours = (value, hours) => {
    if (!isDate(value)) {
        return null;
    }
    const date = new Date(value);
    date.setHours(date.getHours() + hours);
    return date;
};
const addMonths = (value, months) => {
    if (!isDate(value)) {
        return null;
    }
    const date = new Date(value);
    date.setMonth(date.getMonth() + months);
    return date;
};
const timeDifference = function (previous, current = new Date()) {
    if (!isDate(previous)) {
        return '';
    }
    if (!isDate(current)) {
        return '';
    }
    const from = new Date(previous);
    const to = new Date(current);
    const msPerMinute = 60 * 1000;
    const msPerHour = msPerMinute * 60;
    const msPerDay = msPerHour * 24;
    const msPerMonth = msPerDay * 30;
    const msPerYear = msPerDay * 365;
    const elapsed = to.getTime() - from.getTime();
    if (elapsed < msPerMinute) {
        return `${Math.round(elapsed / 1000)} seconds ago`;
    }
    if (elapsed < msPerHour) {
        return `${Math.round(elapsed / msPerMinute)} minutes ago`;
    }
    if (elapsed < msPerDay) {
        return `${Math.round(elapsed / msPerHour)} hours ago`;
    }
    if (elapsed < msPerMonth) {
        return `${Math.round(elapsed / msPerDay)} days ago`;
    }
    if (elapsed < msPerYear) {
        return `${Math.round(elapsed / msPerMonth)} months ago`;
    }
    return `${Math.round(elapsed / msPerYear)} years ago`;
};
// Date.prototype.begin = function () {
//   return new Date(this.toFormat('MM/dd/yyyy'));
// };
// Date.prototype.end = function () {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   const date: Date = this;
//   return date.addDays(1).begin().addMiliseconds(-1);
// };
// Date.prototype.addMiliseconds = function (miliseconds: number) {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   const date: Date = this;
//   date.setMilliseconds(date.getMilliseconds() + miliseconds);
//   return date;
// };
// Date.prototype.addHours = function (hours: number) {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   const date: Date = this;
//   date.setHours(date.getHours() + hours);
//   return date;
// };
// Date.prototype.addDays = function (days: number) {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   const date: Date = this;
//   date.setDate(date.getDate() + days);
//   return date;
// };
// Date.prototype.addMonths = function (months: number) {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   const date: Date = this;
//   date.setMonth(date.getMonth() + months);
//   return date;
// };
// Date.prototype.toFormat = function (format: string) {
//   // eslint-disable-next-line @typescript-eslint/no-this-alias
//   const date: Date = this;
//   let result = format;
//   result = result.replace('yyyy', date.getFullYear().toString());
//   result = result.replace('MM', (date.getMonth() + 1).toString().padStart(2, '0'));
//   result = result.replace('dd', date.getDate().toString().padStart(2, '0'));
//   result = result.replace('HH', date.getHours().toString().padStart(2, '0'));
//   result = result.replace('mm', date.getMinutes().toString().padStart(2, '0'));
//   result = result.replace('ss', date.getSeconds().toString().padStart(2, '0'));
//   return result;
// };
const DateUtilities = {
    equal,
    dayDiff,
    monthDiff,
    yearDiff,
    age,
    parseFrom,
    isDate,
    toFormat,
    addMiliseconds,
    addHours,
    addDays,
    addMonths,
    begin,
    end,
    timeDifference,
};

// interface SdUtilityContructor {
//   upload: (option?: {
//     extensions?: string[];
//     maxSizeInMb?: number;
//     validator?: (fileName: string) => string;
//     multiple?: boolean;
//   }) => Promise<File | File[] | null>;
//   download: (filePath: string, fileName?: string) => void;
//   downloadBlob: (blob: Blob, fileName?: string) => void;
//   changeAliasLowerCase: (alias: string) => string;
//   copyToClipboard: (text: string) => void;
//   allWithPaging: <T = any>(
//     func: (
//       pageSize: number,
//       pageNumber: number
//     ) => Promise<{
//       items: T[];
//       total: number;
//     }>,
//     defaultPageSize?: number
//   ) => Promise<T[]>;
//   isIncognito: () => Promise<boolean>;
//   isMobile: () => boolean;
// }
const upload = (option) => {
    const uploadId = 'U1e09c1c0-b647-437e-995e-d7a1a1b60550';
    const promise = new Promise((resolve, reject) => {
        const body = document.getElementsByTagName('body')?.[0];
        if (!body) {
            resolve(null);
            return;
        }
        const existedElement = document.getElementById(uploadId);
        if (existedElement) {
            existedElement.remove();
        }
        const element = document.createElement('input');
        element.setAttribute('id', uploadId);
        element.setAttribute('type', 'file');
        if (option?.multiple) {
            element.setAttribute('multiple', '');
        }
        element.style.display = 'none';
        body.appendChild(element);
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        element.addEventListener('change', (evt) => {
            try {
                const target = evt.target;
                if (option?.multiple) {
                    const files = [];
                    for (const file of target.files) {
                        if (file) {
                            const lastDot = file.name.lastIndexOf('.');
                            if (lastDot === -1) {
                                throw new Error(`[${file.name}] File tải lên không đúng định dạng. Vui lòng chọn lại`);
                            }
                            const extension = file.name.substring(lastDot + 1);
                            if (option) {
                                if (option.extensions?.length && !option.extensions.some(e => e.toLowerCase() === extension.toLowerCase())) {
                                    throw new Error(`[${file.name}] File tải lên không đúng định dạng. Vui lòng chọn lại`);
                                }
                                if (option.maxSizeInMb && option.maxSizeInMb > 0 && option.maxSizeInMb * 1024 * 1024 < file.size) {
                                    const message = `[${file.name}] Kích thước file không hợp lệ. Vui lòng chọn một file khác`;
                                    throw new Error(message);
                                }
                                if (option.validator && option.validator(file.name)) {
                                    const message = option.validator(file.name);
                                    throw new Error(message);
                                }
                            }
                            files.push(file);
                        }
                        resolve(files);
                    }
                }
                else {
                    const file = target.files.item(0);
                    if (file) {
                        const lastDot = file.name.lastIndexOf('.');
                        if (lastDot === -1) {
                            throw new Error(`[${file.name}] File tải lên không đúng định dạng. Vui lòng chọn lại`);
                        }
                        const extension = file.name.substring(lastDot + 1);
                        if (option) {
                            if (option.extensions?.length && !option.extensions.some(e => e.toLowerCase() === extension.toLowerCase())) {
                                throw new Error(`[${file.name}] File tải lên không đúng định dạng. Vui lòng chọn lại`);
                            }
                            if (option.maxSizeInMb && option.maxSizeInMb > 0 && option.maxSizeInMb * 1024 * 1024 < file.size) {
                                const message = `[${file.name}] Kích thước file không hợp lệ. Vui lòng chọn một file khác`;
                                throw new Error(message);
                            }
                            if (option.validator && option.validator(file.name)) {
                                const message = option.validator(file.name);
                                throw new Error(message);
                            }
                        }
                        resolve(file);
                    }
                }
            }
            catch (error) {
                reject(error);
            }
        });
        element.click();
    });
    return promise;
};
const generateFileName = (fileName) => {
    if (!fileName) {
        return `file_${randomId()}`;
    }
    return fileName;
};
const download = (fileOrPath, fileName) => {
    if (!fileOrPath) {
        console.warn('No file or path provided for download');
        return;
    }
    fileName = generateFileName(fileName);
    if (fileOrPath instanceof File) {
        // Hoặc sử dụng cách thông thường:
        const url = URL.createObjectURL(fileOrPath);
        const a = document.createElement('a');
        a.href = url;
        a.download = fileOrPath.name || fileName;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url); // Giải phóng tài nguyên
        return;
    }
    const a = document.createElement('a');
    a.href = fileOrPath;
    if (fileOrPath.startsWith('http')) {
        a.target = '_blank'; // mở tab mới
    }
    else {
        a.download = fileName;
    }
    a.style.visibility = 'hidden';
    document.body.appendChild(a);
    a.click();
    a.remove();
    return;
};
const downloadBlob = (blob, fileName) => {
    try {
        const url = window.URL.createObjectURL(blob);
        const link = document.createElement('a');
        if (link.download !== undefined) {
            // feature detection
            link.download = generateFileName(fileName);
            link.href = url;
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            window.URL.revokeObjectURL(url);
            link.remove();
        }
    }
    catch (e) {
        console.error('BlobToSaveAs error', e);
    }
};
const changeAliasLowerCase = (alias) => {
    let str = alias?.toString() ?? '';
    str = str.toString().toLowerCase();
    str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, 'a');
    str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, 'e');
    str = str.replace(/ì|í|ị|ỉ|ĩ/g, 'i');
    str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o');
    str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u');
    str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y');
    str = str.replace(/đ/g, 'd');
    // eslint-disable-next-line no-useless-escape
    str = str.replace(/!|@|%|\^|\*|\(|\)|\+|\=|\<|\>|\?|\/|,|\.|\:|\;|\'|\"|\&|\#|\[|\]|~|\$|_|`|-|{|}|\||\\/g, ' ');
    str = str.replace(/ + /g, ' ');
    str = str.trim();
    return str;
};
const copyToClipboard = (text) => {
    navigator.clipboard.writeText(text);
};
const allWithPaging = async (func, defaultPageSize) => {
    const pageSize = defaultPageSize || 1000;
    let pageNumber = 0;
    let count = 0;
    let items = [];
    while (true) {
        const res = await func(pageSize, pageNumber);
        if (Array(res?.items) && res?.total > 0) {
            items = [...items, ...res.items];
            count += res.items.length;
            pageNumber++;
            if (count >= res.total || !res.items?.length) {
                return items;
            }
        }
        else {
            return [];
        }
    }
};
const isIncognito = async () => {
    // Chrome-based
    if ('storage' in navigator && 'estimate' in navigator.storage) {
        try {
            const { quota } = await navigator.storage.estimate();
            if (quota && quota < 120 * 1024 * 1024)
                return true;
        }
        catch (err) {
            console.error(err);
        }
    }
    // Safari fallback
    try {
        localStorage.setItem('__incognito_test', '1');
        localStorage.removeItem('__incognito_test');
        return false;
    }
    catch {
        return true;
    }
};
const isMobile = () => {
    return /Mobi|Android/i.test(navigator.userAgent);
};
const randomId = (prefix) => {
    const id = new Date().getTime().toString(36);
    if (prefix) {
        return `${prefix}_${id}`;
    }
    return id;
};
const hash = (obj) => {
    const json = stableStringify(obj);
    let hash = 0;
    for (let i = 0; i < json.length; i++) {
        hash = (hash << 5) - hash + json.charCodeAt(i);
        hash |= 0; // Convert to 32bit integer
    }
    return `h${Math.abs(hash)}`;
};
/**
 * Convert object to stable JSON string (keys sorted)
 */
const stableStringify = (obj) => {
    if (obj === null || typeof obj !== 'object') {
        return JSON.stringify(obj);
    }
    if (obj instanceof File) {
        // Chỉ serialize các thuộc tính quan trọng của File
        return JSON.stringify({
            __type: 'File',
            name: obj.name,
            size: obj.size,
            type: obj.type,
            lastModified: obj.lastModified,
        });
    }
    if (Array.isArray(obj)) {
        return `[${obj.map(stableStringify).join(',')}]`;
    }
    const keys = Object.keys(obj).sort();
    const keyValuePairs = keys.map(key => `"${key}":${stableStringify(obj[key])}`);
    return `{${keyValuePairs.join(',')}}`;
};
// Hàm `parseQueryParams` dùng để phân tích chuỗi query string (phần sau dấu `?` trên URL)
//   và chuyển đổi nó thành một đối tượng JavaScript với các cặp key-value.
const parseQueryParams = (queryString) => {
    const params = new URLSearchParams(queryString || '');
    const result = {};
    params.forEach((value, key) => {
        result[key] = value;
    });
    return result;
};
/**
 * Lấy địa chỉ IP public của client bằng cách gọi API bên thứ ba.
 */
const getClientPublicIp = async () => {
    try {
        // Gọi đến API của ipify để lấy IP dưới dạng JSON
        const response = await fetch('https://api.ipify.org?format=json');
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        // Trả về địa chỉ IP
        return data.ip;
    }
    catch (error) {
        console.error('Không thể lấy địa chỉ IP:', error);
        return null; // Trả về null nếu có lỗi
    }
};
// var SdUtility: SdUtilityContructor = {
//   upload,
//   download,
//   downloadBlob,
//   changeAliasLowerCase,
//   copyToClipboard,
//   allWithPaging,
//   isIncognito,
//   isMobile,
// };
// declare global {
//   var SdUtility: SdUtilityContructor;
// }
// globalThis.SdUtility = SdUtility;
// Helper function với fallback
const generateUuid = () => {
    if (typeof crypto !== 'undefined' && crypto.randomUUID) {
        return crypto.randomUUID();
    }
    // Fallback cho browser cũ
    return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
};
const SdUtilities = {
    upload,
    download,
    downloadBlob,
    changeAliasLowerCase,
    copyToClipboard,
    allWithPaging,
    isIncognito,
    isMobile,
    randomId,
    hash,
    parseQueryParams,
    getClientPublicIp,
    generateUuid
};

/**
 * Color Utilities Extension
 * Các tiện ích xử lý màu
 */
/**
 * Chuyển đổi màu HSL sang định dạng hex
 * @param h - Hue (0-360)
 * @param s - Saturation (0-100)
 * @param l - Lightness (0-100)
 * @returns Mã màu hex
 */
function hslToHex(h, s, l) {
    // Chuyển đổi sang định dạng 0-1
    const hNormalized = h / 360;
    const sNormalized = s / 100;
    const lNormalized = l / 100;
    let r, g, b;
    if (sNormalized === 0) {
        // Màu xám (grayscale)
        r = g = b = lNormalized;
    }
    else {
        const hue2rgb = (p, q, t) => {
            if (t < 0)
                t += 1;
            if (t > 1)
                t -= 1;
            if (t < 1 / 6)
                return p + (q - p) * 6 * t;
            if (t < 1 / 2)
                return q;
            if (t < 2 / 3)
                return p + (q - p) * (2 / 3 - t) * 6;
            return p;
        };
        const q = lNormalized < 0.5
            ? lNormalized * (1 + sNormalized)
            : lNormalized + sNormalized - lNormalized * sNormalized;
        const p = 2 * lNormalized - q;
        r = hue2rgb(p, q, hNormalized + 1 / 3);
        g = hue2rgb(p, q, hNormalized);
        b = hue2rgb(p, q, hNormalized - 1 / 3);
    }
    // Chuyển đổi sang hex
    const toHex = (x) => {
        const hex = Math.round(x * 255).toString(16);
        return hex.length === 1 ? '0' + hex : hex;
    };
    return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
/**
 * Chuyển đổi màu RGB sang hex
 * @param r - Red (0-255)
 * @param g - Green (0-255)
 * @param b - Blue (0-255)
 * @returns Mã màu hex
 */
function rgbToHex(r, g, b) {
    const toHex = (x) => {
        const hex = Math.round(Math.max(0, Math.min(255, x))).toString(16);
        return hex.length === 1 ? '0' + hex : hex;
    };
    return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}

/**
 * Generated bundle index. Do not edit.
 */

export { ArrayUtilities, DateUtilities, NumberUtilities, SdUtilities, StringUtilities, hslToHex, rgbToHex };
//# sourceMappingURL=sd-angular-core-utilities-extensions.mjs.map
