import * as i0 from '@angular/core';
import { Injectable, InjectionToken, inject, DestroyRef, signal, computed, PLATFORM_ID, effect } from '@angular/core';
import { zNormalize, zFormatNumExcel, zConvertColorToArgb } from '@shival99/z-ui/utils';
import * as ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';
import * as XLSX from 'xlsx';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Router } from '@angular/router';
import { firstValueFrom, from, switchMap, of, catchError, throwError, tap, map, defer, Subject, BehaviorSubject } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { TranslateService } from '@ngx-translate/core';
import { isPlatformBrowser, DOCUMENT } from '@angular/common';

const Z_CACHE_SCOPE_SEPARATOR = '::';
const Z_DB_SCOPE_SEPARATOR = '__';
const Z_SCOPE_FALLBACK = 'global';
const Z_RUNTIME_SCOPE = (() => {
    if (typeof window === 'undefined') {
        return Z_SCOPE_FALLBACK;
    }
    const origin = zNormalize(window.location.origin);
    const baseHref = typeof document !== 'undefined' ? zNormalize(document.querySelector('base')?.getAttribute('href') ?? '') : '';
    if (!origin && !baseHref) {
        return Z_SCOPE_FALLBACK;
    }
    if (!origin) {
        return baseHref || Z_SCOPE_FALLBACK;
    }
    if (!baseHref) {
        return origin;
    }
    return `${origin}__${baseHref}`;
})();
const Z_CACHE_SCOPE_SUFFIX = `${Z_CACHE_SCOPE_SEPARATOR}${Z_RUNTIME_SCOPE}`;
const Z_DB_SCOPE_SUFFIX = `${Z_DB_SCOPE_SEPARATOR}${Z_RUNTIME_SCOPE}`;
function zAppendScope(value, scopeSuffix) {
    const trimmed = value.trim();
    if (!trimmed || trimmed.endsWith(scopeSuffix)) {
        return value;
    }
    return `${trimmed}${scopeSuffix}`;
}
function zBuildScopedCacheKey(baseKey) {
    return zAppendScope(baseKey, Z_CACHE_SCOPE_SUFFIX);
}
function zBuildScopedDbName(baseDbName) {
    return zAppendScope(baseDbName, Z_DB_SCOPE_SUFFIX);
}
function zStripScopedCacheKey(key) {
    const normalizedKey = key.trim();
    if (!normalizedKey) {
        return key;
    }
    if (!normalizedKey.endsWith(Z_CACHE_SCOPE_SUFFIX)) {
        return normalizedKey;
    }
    return normalizedKey.slice(0, normalizedKey.length - Z_CACHE_SCOPE_SUFFIX.length);
}

const Z_EXCEL_COLORS = {
    'green-50': 'FFF0FDF4',
    'green-100': 'FFDCFCE7',
    'green-200': 'FFBBF7D0',
    'green-300': 'FF86EFAC',
    'green-400': 'FF4ADE80',
    'green-500': 'FF22C55E',
    'blue-50': 'FFEFF6FF',
    'blue-100': 'FFDBEAFE',
    'blue-200': 'FFBFDBFE',
    'blue-300': 'FF93C5FD',
    'blue-400': 'FF60A5FA',
    'blue-500': 'FF3B82F6',
    'gray-50': 'FFF9FAFB',
    'gray-100': 'FFF3F4F6',
    'gray-200': 'FFE5E7EB',
    'gray-300': 'FFD1D5DB',
    'gray-400': 'FF9CA3AF',
    'gray-500': 'FF6B7280',
    'yellow-50': 'FFFFFBEB',
    'yellow-100': 'FFFEF3C7',
    'yellow-200': 'FFFDE68A',
    'yellow-300': 'FFFCD34D',
    'red-50': 'FFFEF2F2',
    'red-100': 'FFFEE2E2',
    'red-200': 'FFFECACA',
    'red-300': 'FFFCA5A5',
    white: 'FFFFFFFF',
    black: 'FF000000',
    transparent: '00000000',
};
const Z_EXCEL_DEFAULT_CONFIG = {
    font: {
        name: 'Calibri',
        size: 11,
    },
    headerFont: {
        name: 'Calibri',
        size: 11,
        bold: true,
    },
    headerColors: {
        level0: Z_EXCEL_COLORS['green-300'],
        level1: Z_EXCEL_COLORS['blue-200'],
    },
};
const Z_EXCEL_BORDER_THIN = {
    top: { style: 'thin' },
    left: { style: 'thin' },
    bottom: { style: 'thin' },
    right: { style: 'thin' },
};
const Z_EXCEL_CHAR_WIDTH_MAP = {
    W: 1.4,
    M: 1.4,
    D: 1.2,
    O: 1.2,
    Q: 1.2,
    A: 1.1,
    B: 1.1,
    C: 1.1,
    G: 1.1,
    H: 1.1,
    N: 1.1,
    R: 1.1,
    S: 1.1,
    U: 1.1,
    V: 1.1,
    X: 1.1,
    Y: 1.1,
    Z: 1.1,
    E: 1.0,
    F: 1.0,
    K: 1.0,
    P: 1.0,
    T: 1.0,
    J: 0.9,
    L: 0.9,
    I: 0.6,
    i: 0.5,
    l: 0.5,
    t: 0.6,
    f: 0.6,
    j: 0.5,
    '1': 0.8,
    '!': 0.5,
    '.': 0.5,
    ',': 0.5,
    ':': 0.5,
    ';': 0.5,
    '|': 0.5,
    ' ': 0.5,
};
const Z_EXCEL_FONT_MULTIPLIERS = {
    'Times New Roman': 1.0,
    Arial: 0.95,
    Calibri: 0.95,
    'Courier New': 1.2,
    'Comic Sans MS': 1.15,
    Verdana: 1.05,
    Tahoma: 0.9,
    Georgia: 1.05,
    Impact: 0.85,
    'Trebuchet MS': 0.95,
};
const Z_EXCEL_WIDTH_LIMITS = {
    min: 5,
    max: 50,
    tableMin: 8,
    tableMax: 30,
    default: 20,
};

const Z_INDEXDB_DEFAULT_CONFIG = {
    dbName: 'ZDatabase',
    version: 1,
    mode: 'readwrite',
    defaultStore: 'ZStore',
};
const Z_INDEXDB_BATCH_SIZE = 100;
const Z_INDEXDB_MAX_VERSION = 1000;

const Z_LANG_TO_LOCALE = {
    vi: 'vi-VN',
    en: 'en-US',
};

class ZCacheService {
    static _prefix = 'z_';
    static _encrypt = true;
    static configure(config) {
        if (config.prefix) {
            ZCacheService._prefix = config.prefix;
        }
        if (config.encrypt !== undefined) {
            ZCacheService._encrypt = config.encrypt;
        }
    }
    static _encode(data) {
        const payload = { __z: true, __d: data };
        return btoa(encodeURIComponent(JSON.stringify(payload)));
    }
    static _decode(data) {
        try {
            const decoded = JSON.parse(decodeURIComponent(atob(data)));
            if (!decoded.__z) {
                return null;
            }
            return decoded.__d;
        }
        catch {
            return null;
        }
    }
    static _normalizeLegacyKey(key) {
        return key.trim().toLowerCase();
    }
    static _normalizeKey(key) {
        return zBuildScopedCacheKey(ZCacheService._normalizeLegacyKey(key));
    }
    static _toStorageKey(normalizedKey, encrypt) {
        const prefixedKey = `${ZCacheService._prefix}${normalizedKey}`;
        return encrypt ? ZCacheService._encode(prefixedKey) : prefixedKey;
    }
    static _getFullKey(key, encrypt) {
        return ZCacheService._toStorageKey(ZCacheService._normalizeKey(key), encrypt);
    }
    static _getLegacyFullKey(key, encrypt) {
        const legacyNormalized = ZCacheService._normalizeLegacyKey(key);
        const scopedNormalized = ZCacheService._normalizeKey(key);
        if (legacyNormalized === scopedNormalized) {
            return null;
        }
        return ZCacheService._toStorageKey(legacyNormalized, encrypt);
    }
    static _getCandidateStorageKeys(key, encrypt) {
        const scopedKey = ZCacheService._getFullKey(key, encrypt);
        const legacyKey = ZCacheService._getLegacyFullKey(key, encrypt);
        return legacyKey ? [scopedKey, legacyKey] : [scopedKey];
    }
    static _decodeStorageKey(storageKey) {
        const decoded = ZCacheService._decode(storageKey);
        if (typeof decoded !== 'string') {
            return null;
        }
        return decoded;
    }
    static _isManagedStorageKey(storageKey) {
        if (storageKey.startsWith(ZCacheService._prefix)) {
            return true;
        }
        const decoded = ZCacheService._decodeStorageKey(storageKey);
        if (!decoded) {
            return false;
        }
        return decoded.startsWith(ZCacheService._prefix);
    }
    static _safeOperation(operation, fallback) {
        try {
            return operation();
        }
        catch (error) {
            console.error('[ZCache] Operation error:', error);
            return fallback;
        }
    }
    static get(key, defaultValue, encrypt = ZCacheService._encrypt) {
        return ZCacheService._safeOperation(() => {
            const candidateKeys = ZCacheService._getCandidateStorageKeys(key, encrypt);
            for (let i = 0; i < candidateKeys.length; i++) {
                const result = localStorage.getItem(candidateKeys[i]);
                if (!result) {
                    continue;
                }
                const data = encrypt ? ZCacheService._decode(result) : JSON.parse(result);
                if (data === null || data === undefined) {
                    continue;
                }
                if (i > 0) {
                    const scopedKey = candidateKeys[0];
                    localStorage.setItem(scopedKey, result);
                    localStorage.removeItem(candidateKeys[i]);
                }
                return data;
            }
            return defaultValue;
        }, defaultValue);
    }
    static set(key, data, encrypt = ZCacheService._encrypt) {
        return ZCacheService._safeOperation(() => {
            const _key = ZCacheService._getFullKey(key, encrypt);
            const legacyKey = ZCacheService._getLegacyFullKey(key, encrypt);
            const _value = encrypt ? ZCacheService._encode(data) : JSON.stringify(data);
            localStorage.setItem(_key, _value);
            if (legacyKey) {
                localStorage.removeItem(legacyKey);
            }
            return true;
        }, false);
    }
    static delete(key) {
        return ZCacheService._safeOperation(() => {
            const encryptedKeys = ZCacheService._getCandidateStorageKeys(key, true);
            const plainKeys = ZCacheService._getCandidateStorageKeys(key, false);
            encryptedKeys.forEach(item => localStorage.removeItem(item));
            plainKeys.forEach(item => localStorage.removeItem(item));
            return true;
        }, false);
    }
    static deleteMultiple(keys) {
        return ZCacheService._safeOperation(() => {
            keys.forEach(key => {
                const encryptedKeys = ZCacheService._getCandidateStorageKeys(key, true);
                const plainKeys = ZCacheService._getCandidateStorageKeys(key, false);
                encryptedKeys.forEach(item => localStorage.removeItem(item));
                plainKeys.forEach(item => localStorage.removeItem(item));
            });
            return true;
        }, false);
    }
    static clear() {
        return ZCacheService._safeOperation(() => {
            const keysToRemove = [];
            for (let i = 0; i < localStorage.length; i++) {
                const key = localStorage.key(i);
                if (!key) {
                    continue;
                }
                if (ZCacheService._isManagedStorageKey(key)) {
                    keysToRemove.push(key);
                }
            }
            keysToRemove.forEach(key => localStorage.removeItem(key));
            return true;
        }, false);
    }
    static has(key, encrypt = ZCacheService._encrypt) {
        return ZCacheService._safeOperation(() => {
            const candidateKeys = ZCacheService._getCandidateStorageKeys(key, encrypt);
            return candidateKeys.some(storageKey => localStorage.getItem(storageKey) !== null);
        }, false);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZCacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZCacheService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZCacheService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class ZExcelService {
    static _isHeaderConfig(config) {
        if (!config || typeof config !== 'object') {
            return false;
        }
        return config.constructor === Object;
    }
    static _getHeaderConfig(col) {
        const empty = {
            content: undefined,
            class: undefined,
            style: undefined,
            align: undefined,
            tooltip: undefined,
            rowSpan: undefined,
            colSpan: undefined,
        };
        if (!col) {
            return empty;
        }
        if (!ZExcelService._isHeaderConfig(col.header)) {
            return { ...empty, content: col.header };
        }
        return {
            content: col.header.content,
            class: undefined,
            style: undefined,
            align: col.header.align,
            tooltip: undefined,
            rowSpan: undefined,
            colSpan: undefined,
        };
    }
    async exportExcel(config, options = {}) {
        try {
            const result = await ZExcelService.exportExcelStatic(config);
            await this._saveFile(result, options);
        }
        catch (error) {
            console.error('Error exporting Excel:', error);
            options.toast?.error?.('Failed to export Excel file');
            throw error;
        }
    }
    async exportFromTable(config, options = {}) {
        try {
            const excelColumns = this._convertTableColumnsToExcel(config);
            const excelConfig = {
                fileName: config.fileName,
                columns: excelColumns,
                source: config.source,
            };
            await this.exportExcel(excelConfig, options);
        }
        catch (error) {
            console.error('Error exporting from table:', error);
            options.toast?.error?.('Failed to export Excel file');
            throw error;
        }
    }
    async exportFromElement(elementId, fileName = 'export', options = {}) {
        try {
            const result = await ZExcelService.exportFromElementStatic(elementId, fileName);
            await this._saveFile(result, options);
        }
        catch (error) {
            console.error('Error exporting from element:', error);
            options.toast?.error?.('Failed to export Excel file');
            throw error;
        }
    }
    async exportFromElementXlsx(elementId, fileName = 'export', options = {}) {
        try {
            const element = document.getElementById(elementId);
            if (!element) {
                throw new Error(`Element with id "${elementId}" not found`);
            }
            const table = element.querySelector('table') || element;
            if (table.tagName !== 'TABLE') {
                throw new Error('Element must be a table or contain a table');
            }
            const workbook = XLSX.utils.table_to_book(table, { sheet: 'Sheet1' });
            const { addTimestamp = true } = options;
            let finalFileName = `${fileName}.xlsx`;
            if (addTimestamp) {
                finalFileName = `${fileName}_${ZExcelService._formatTimestamp()}.xlsx`;
            }
            XLSX.writeFile(workbook, finalFileName);
            if (options.showNotification !== false) {
                options.toast?.success?.(`File "${finalFileName}" has been downloaded`);
            }
        }
        catch (error) {
            console.error('Error exporting from element with XLSX:', error);
            options.toast?.error?.('Failed to export Excel file');
            throw error;
        }
    }
    static async exportExcelStatic(config) {
        const workbook = new ExcelJS.Workbook();
        const { fileName = 'export', columns, source } = config;
        for (const sheetSource of source) {
            const sheet = workbook.addWorksheet(sheetSource.sheetName || 'Sheet1');
            const flatColumns = ZExcelService._flattenColumns(columns);
            const headerLevels = ZExcelService._calculateHeaderLevels(columns);
            sheet.columns = flatColumns.map(col => ({
                key: col.id,
                header: col.header || col.id,
                width: col.width || Z_EXCEL_WIDTH_LIMITS.default,
                style: { font: Z_EXCEL_DEFAULT_CONFIG.font },
            }));
            ZExcelService._createHeaderRows(sheet, columns, headerLevels);
            const dataStartRow = headerLevels + 1;
            const mergedCells = new Set();
            const mergeRegions = ZExcelService._processMergeRegions(flatColumns, sheetSource.data, dataStartRow);
            sheetSource.data.forEach((dataItem, rowIndex) => {
                const row = sheet.addRow([]);
                flatColumns.forEach((col, colIndex) => {
                    const cell = row.getCell(colIndex + 1);
                    const cellValue = ZExcelService._getCellValue(col, dataItem, rowIndex, sheetSource.data);
                    cell.value = cellValue;
                });
                row.eachCell((cell, colNumber) => {
                    const colConfig = flatColumns[colNumber - 1];
                    const currentRow = dataStartRow + rowIndex + 1;
                    const cellKey = `${currentRow}:${colNumber}`;
                    if (mergedCells.has(cellKey)) {
                        return;
                    }
                    cell.font = {
                        name: Z_EXCEL_DEFAULT_CONFIG.font.name,
                        size: Z_EXCEL_DEFAULT_CONFIG.font.size,
                        ...(cell.font || {}),
                    };
                    if (colConfig.formatNum) {
                        const rawValue = ZExcelService._getCellValue(colConfig, dataItem, rowIndex, sheetSource.data);
                        zFormatNumExcel(cell, rawValue, colConfig.formatNum);
                    }
                    if (!colConfig.formatNum && colConfig.format) {
                        const context = {
                            row: dataItem,
                            rowIndex,
                            data: sheetSource.data,
                            cell: cell,
                        };
                        colConfig.format(context);
                    }
                    if (colConfig.align) {
                        cell.alignment = {
                            horizontal: colConfig.align,
                            vertical: 'middle',
                            ...(cell.alignment || {}),
                        };
                    }
                    if (colConfig.note) {
                        const noteText = colConfig.note(dataItem, rowIndex, sheetSource.data);
                        if (noteText) {
                            cell.note = noteText;
                        }
                    }
                    cell.border = Z_EXCEL_BORDER_THIN;
                });
            });
            mergeRegions.forEach(region => {
                try {
                    sheet.mergeCells(region.startRow, region.startCol, region.endRow, region.endCol);
                    const mergedCell = sheet.getCell(region.startRow, region.startCol);
                    if (!mergedCell.alignment) {
                        mergedCell.alignment = { vertical: 'middle' };
                    }
                    for (let r = region.startRow; r <= region.endRow; r++) {
                        for (let c = region.startCol; c <= region.endCol; c++) {
                            mergedCells.add(`${r}:${c}`);
                        }
                    }
                }
                catch (error) {
                    console.warn(`Failed to merge cells:`, error);
                }
            });
            ZExcelService._applyAutoFitWidth(sheet, flatColumns);
        }
        const buffer = await workbook.xlsx.writeBuffer();
        return { buffer: buffer, fileName: `${fileName}.xlsx` };
    }
    static async exportFromElementStatic(elementId, fileName = 'export') {
        const workbook = new ExcelJS.Workbook();
        const element = document.getElementById(elementId);
        if (!element) {
            throw new Error(`Element with id "${elementId}" not found`);
        }
        const sheet = workbook.addWorksheet('Sheet1');
        const table = element.querySelector('table') || element;
        if (table.tagName !== 'TABLE') {
            throw new Error('Element must be a table or contain a table');
        }
        ZExcelService._processTableElement(sheet, table);
        ZExcelService._applyAutoFitWidthForTable(sheet);
        const buffer = await workbook.xlsx.writeBuffer();
        return { buffer: buffer, fileName: `${fileName}.xlsx` };
    }
    async _saveFile(result, options) {
        const { addTimestamp = true, showNotification = true } = options;
        let finalFileName = result.fileName;
        if (addTimestamp) {
            const timestamp = ZExcelService._formatTimestamp();
            if (!finalFileName.includes(timestamp.slice(0, 8))) {
                finalFileName = finalFileName.replace('.xlsx', `_${timestamp}.xlsx`);
            }
        }
        const blob = new Blob([result.buffer], {
            type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        });
        saveAs(blob, finalFileName);
        if (showNotification) {
            options.toast?.success?.(`File "${finalFileName}" has been downloaded`);
        }
    }
    _convertTableColumnsToExcel(config) {
        const { tableColumns, excelOverrides = {}, excludeColumns = [], headerResolver } = config;
        const convertColumn = (col) => {
            if (excludeColumns.includes(col.id)) {
                return null;
            }
            if (['select', 'expand', 'actions'].includes(col.id)) {
                return null;
            }
            const headerConfig = ZExcelService._getHeaderConfig(col);
            let headerText = '';
            headerText = col.id;
            if (headerResolver) {
                headerText = headerResolver(col);
            }
            if (!headerResolver && typeof headerConfig.content === 'string') {
                headerText = headerConfig.content;
            }
            if (!headerResolver && typeof headerConfig.content === 'function') {
                const result = headerConfig.content();
                headerText = typeof result === 'string' ? result : col.id;
            }
            const excelCol = {
                id: col.id,
                header: headerText,
                accessorKey: col.accessorKey,
                accessorFn: col.accessorFn ? (row) => col.accessorFn(row) : undefined,
                autoFit: true,
                align: headerConfig.align,
                visible: typeof col.visible === 'boolean' ? col.visible : true,
            };
            const override = excelOverrides[col.id];
            if (override) {
                Object.assign(excelCol, override);
            }
            if (col.columns && col.columns.length > 0) {
                excelCol.columns = col.columns
                    .map((childCol) => convertColumn(childCol))
                    .filter((c) => c !== null);
                if (excelCol.columns && excelCol.columns.length > 0) {
                    excelCol.headerColSpan = ZExcelService._getLeafCount(excelCol.columns);
                }
            }
            return excelCol;
        };
        return tableColumns
            .map((col) => convertColumn(col))
            .filter((c) => c !== null);
    }
    static _flattenColumns(columns) {
        const result = [];
        const flatten = (cols) => {
            for (const col of cols) {
                if (col.visible === false) {
                    continue;
                }
                if (col.columns && col.columns.length > 0) {
                    flatten(col.columns);
                    continue;
                }
                result.push(col);
            }
        };
        flatten(columns);
        return result;
    }
    static _calculateHeaderLevels(columns) {
        const getLevel = (cols, currentLevel = 1) => {
            let maxLevel = currentLevel;
            for (const col of cols) {
                let currentColLevel = currentLevel;
                if (col.headerRowSpan && col.headerRowSpan > 1) {
                    currentColLevel = currentLevel + col.headerRowSpan - 1;
                }
                if (col.columns && col.columns.length > 0) {
                    if (!col.headerRowSpan || col.headerRowSpan <= 1) {
                        const childLevel = getLevel(col.columns, currentLevel + 1);
                        currentColLevel = Math.max(currentColLevel, childLevel);
                    }
                }
                maxLevel = Math.max(maxLevel, currentColLevel);
            }
            return maxLevel;
        };
        return getLevel(columns);
    }
    static _createHeaderRows(sheet, columns, maxLevels) {
        const headerStructure = [];
        for (let i = 0; i < maxLevels; i++) {
            headerStructure[i] = [];
        }
        ZExcelService._buildHeaderStructure(columns, headerStructure, 0, maxLevels);
        const processedMergeRegions = new Set();
        headerStructure.forEach((row, rowIndex) => {
            const headerRow = sheet.getRow(rowIndex + 1);
            const sortedRow = row.sort((a, b) => (a.startCol || 0) - (b.startCol || 0));
            sortedRow.forEach((headerCell) => {
                if (headerCell.isProcessed || headerCell.isEmpty) {
                    return;
                }
                const colIndex = (headerCell.startCol || 0) + 1;
                const cell = headerRow.getCell(colIndex);
                cell.value = headerCell.header;
                if (headerCell.colSpan > 1 || headerCell.rowSpan > 1) {
                    const startRow = rowIndex + 1;
                    const endRow = startRow + headerCell.rowSpan - 1;
                    const startCol = colIndex;
                    const endCol = colIndex + headerCell.colSpan - 1;
                    const mergeKey = `${startRow}:${startCol}-${endRow}:${endCol}`;
                    if (!processedMergeRegions.has(mergeKey)) {
                        try {
                            sheet.mergeCells(startRow, startCol, endRow, endCol);
                            processedMergeRegions.add(mergeKey);
                            headerCell.isProcessed = true;
                        }
                        catch (error) {
                            console.warn(`Failed to merge header cells:`, error);
                        }
                    }
                }
                const defaultColor = rowIndex === 0 ? Z_EXCEL_DEFAULT_CONFIG.headerColors.level0 : Z_EXCEL_DEFAULT_CONFIG.headerColors.level1;
                cell.fill = {
                    type: 'pattern',
                    pattern: 'solid',
                    fgColor: { argb: defaultColor },
                };
                cell.font = Z_EXCEL_DEFAULT_CONFIG.headerFont;
                cell.alignment = { horizontal: 'center', vertical: 'middle' };
                cell.border = Z_EXCEL_BORDER_THIN;
                if (headerCell.formatHeader) {
                    headerCell.formatHeader(cell);
                }
            });
        });
    }
    static _buildHeaderStructure(columns, headerStructure, level, maxLevels, startCol = 0) {
        let currentCol = startCol;
        for (const col of columns) {
            if (col.visible === false) {
                continue;
            }
            if (col.columns && col.columns.length > 0) {
                const childrenLeafCount = ZExcelService._getLeafCount(col.columns);
                const colSpan = col.headerColSpan || childrenLeafCount;
                const rowSpan = col.headerRowSpan || 1;
                headerStructure[level].push({
                    header: col.header || col.id,
                    colSpan,
                    rowSpan,
                    formatHeader: col.formatHeader,
                    startCol: currentCol,
                    endCol: currentCol + colSpan - 1,
                });
                if (col.headerRowSpan && col.headerRowSpan > 1) {
                    currentCol += colSpan;
                    continue;
                }
                ZExcelService._buildHeaderStructure(col.columns, headerStructure, level + 1, maxLevels, currentCol);
                currentCol += colSpan;
                continue;
            }
            const colSpan = col.headerColSpan || 1;
            const rowSpan = col.headerRowSpan || maxLevels - level;
            headerStructure[level].push({
                header: col.header || col.id,
                colSpan,
                rowSpan,
                formatHeader: col.formatHeader,
                startCol: currentCol,
                endCol: currentCol + colSpan - 1,
            });
            currentCol += colSpan;
        }
        return currentCol;
    }
    static _getLeafCount(columns) {
        let count = 0;
        for (const col of columns) {
            if (col.visible === false) {
                continue;
            }
            if (col.columns && col.columns.length > 0) {
                count += ZExcelService._getLeafCount(col.columns);
                continue;
            }
            count++;
        }
        return count;
    }
    static _getCellValue(col, row, rowIndex, data) {
        if (col.accessorFn) {
            return col.accessorFn(row, rowIndex, data);
        }
        if (col.accessorKey) {
            return row[col.accessorKey];
        }
        return '';
    }
    static _processMergeRegions(flatColumns, data, dataStartRow) {
        const mergeRegions = [];
        const dataLength = data.length;
        flatColumns.forEach((colConfig, colIndex) => {
            const colNumber = colIndex + 1;
            if (colConfig.rowSpan || colConfig.colSpan) {
                let currentGroupStart = 0;
                while (currentGroupStart < dataLength) {
                    const currentItem = data[currentGroupStart];
                    const rowSpan = typeof colConfig.rowSpan === 'function'
                        ? colConfig.rowSpan(currentItem, currentGroupStart, data)
                        : colConfig.rowSpan || 1;
                    const colSpan = typeof colConfig.colSpan === 'function'
                        ? colConfig.colSpan(currentItem, currentGroupStart, data)
                        : colConfig.colSpan || 1;
                    const hasRowSpan = rowSpan > 1;
                    const hasColSpan = colSpan > 1;
                    if (hasRowSpan || hasColSpan) {
                        const startRow = dataStartRow + currentGroupStart;
                        const endRow = hasRowSpan ? Math.min(startRow + rowSpan - 1, dataStartRow + dataLength - 1) : startRow;
                        const startCol = colNumber;
                        const endCol = hasColSpan ? colNumber + colSpan - 1 : colNumber;
                        if ((endRow > startRow || endCol > startCol) && endRow < dataStartRow + dataLength) {
                            mergeRegions.push({
                                startRow,
                                startCol,
                                endRow,
                                endCol,
                                firstItemIndex: currentGroupStart,
                            });
                        }
                    }
                    currentGroupStart += Math.max(1, rowSpan);
                }
            }
        });
        return mergeRegions;
    }
    static _applyAutoFitWidth(sheet, flatColumns) {
        flatColumns.forEach((col, index) => {
            const colNumber = index + 1;
            const column = sheet.getColumn(colNumber);
            if (col.autoFit && !col.width) {
                let maxWidth = 0;
                sheet.eachRow((row) => {
                    const cell = row.getCell(colNumber);
                    if (cell.value) {
                        const cellWidth = ZExcelService._calculateCellWidth(cell, String(cell.value)) /
                            ZExcelService._getMergedColumnSpan(sheet, row, cell);
                        maxWidth = Math.max(maxWidth, cellWidth);
                    }
                });
                const calculatedWidth = Math.max(Z_EXCEL_WIDTH_LIMITS.min, Math.min(Z_EXCEL_WIDTH_LIMITS.max, maxWidth + 1));
                column.width = calculatedWidth;
            }
        });
    }
    static _applyAutoFitWidthForTable(sheet) {
        const { columnCount } = sheet;
        for (let colNumber = 1; colNumber <= columnCount; colNumber++) {
            const column = sheet.getColumn(colNumber);
            let maxWidth = 0;
            sheet.eachRow((row) => {
                const cell = row.getCell(colNumber);
                if (cell.value) {
                    const cellWidth = ZExcelService._calculateCellWidth(cell, String(cell.value)) /
                        ZExcelService._getMergedColumnSpan(sheet, row, cell);
                    maxWidth = Math.max(maxWidth, cellWidth);
                }
            });
            const calculatedWidth = Math.max(Z_EXCEL_WIDTH_LIMITS.tableMin, Math.min(Z_EXCEL_WIDTH_LIMITS.tableMax, maxWidth * 0.8 + 2));
            column.width = calculatedWidth;
        }
    }
    static _getMergedColumnSpan(sheet, row, cell) {
        if (!cell.isMerged) {
            return 1;
        }
        let colSpan = 0;
        for (let colNumber = 1; colNumber <= sheet.columnCount; colNumber++) {
            const mergedCell = row.getCell(colNumber);
            if (mergedCell.address === cell.master.address || mergedCell.isMergedTo(cell.master)) {
                colSpan++;
            }
        }
        return Math.max(1, colSpan);
    }
    static _calculateCellWidth(cell, text) {
        const font = cell.font || {};
        const fontSize = font.size || Z_EXCEL_DEFAULT_CONFIG.font.size;
        const fontFamily = font.name || Z_EXCEL_DEFAULT_CONFIG.font.name;
        const isBold = font.bold || false;
        const isItalic = font.italic || false;
        const fontSizeMultiplier = fontSize / 11;
        const fontMultiplier = Z_EXCEL_FONT_MULTIPLIERS[fontFamily] || 1.0;
        let adjustedWidth = 0;
        for (const char of text) {
            const charWidth = Z_EXCEL_CHAR_WIDTH_MAP[char] || 1.0;
            adjustedWidth += charWidth;
        }
        adjustedWidth *= fontSizeMultiplier * fontMultiplier;
        if (isBold) {
            adjustedWidth *= 1.1;
        }
        if (isItalic) {
            adjustedWidth *= 1.05;
        }
        return Math.ceil(adjustedWidth * 1.2);
    }
    static _processTableElement(sheet, table) {
        const rows = Array.from(table.rows);
        const mergedCells = new Set();
        rows.forEach((row, rowIndex) => {
            const cells = Array.from(row.cells);
            let colOffset = 0;
            cells.forEach((cell, cellIndex) => {
                const computedStyle = window.getComputedStyle(cell);
                if (computedStyle.display === 'none' || computedStyle.visibility === 'hidden') {
                    return;
                }
                const rect = cell.getBoundingClientRect();
                if (rect.width === 0 && rect.height === 0) {
                    return;
                }
                while (mergedCells.has(`${rowIndex + 1}:${cellIndex + colOffset + 1}`)) {
                    colOffset++;
                }
                const excelRow = sheet.getRow(rowIndex + 1);
                const colIndex = cellIndex + colOffset;
                const excelCell = excelRow.getCell(colIndex + 1);
                const cellContent = cell.textContent?.trim() || '';
                excelCell.value = ZExcelService._parseContentValue(cellContent);
                if (!excelCell.font) {
                    excelCell.font = Z_EXCEL_DEFAULT_CONFIG.font;
                }
                ZExcelService._applyCellFormatFromHTML(excelCell, cell);
                const rowSpan = parseInt(cell.getAttribute('rowspan') || '1');
                const colSpan = parseInt(cell.getAttribute('colspan') || '1');
                if (rowSpan > 1 || colSpan > 1) {
                    const startRow = rowIndex + 1;
                    const startCol = colIndex + 1;
                    const endRow = startRow + rowSpan - 1;
                    const endCol = startCol + colSpan - 1;
                    const mergeKey = `${startRow}:${startCol}-${endRow}:${endCol}`;
                    if (!mergedCells.has(mergeKey)) {
                        sheet.mergeCells(startRow, startCol, endRow, endCol);
                        mergedCells.add(mergeKey);
                        for (let r = startRow; r <= endRow; r++) {
                            for (let c = startCol; c <= endCol; c++) {
                                mergedCells.add(`${r}:${c}`);
                            }
                        }
                    }
                }
            });
        });
    }
    static _parseContentValue(content) {
        if (!content || content.trim() === '') {
            return '';
        }
        const trimmedContent = content.trim();
        if (/^-?\d+(\.\d+)?$/.test(trimmedContent)) {
            const parsed = parseFloat(trimmedContent);
            if (!isNaN(parsed) && isFinite(parsed)) {
                return parsed;
            }
        }
        if (/^-?\d+(\.\d+)?%$/.test(trimmedContent)) {
            const numberPart = trimmedContent.replace('%', '');
            const parsed = parseFloat(numberPart);
            if (!isNaN(parsed) && isFinite(parsed)) {
                return parsed / 100;
            }
        }
        return trimmedContent;
    }
    static _applyCellFormatFromHTML(excelCell, htmlCell) {
        const computedStyle = window.getComputedStyle(htmlCell);
        excelCell.font = {
            name: Z_EXCEL_DEFAULT_CONFIG.font.name,
            bold: computedStyle.fontWeight === 'bold' || parseInt(computedStyle.fontWeight) >= 600,
            italic: computedStyle.fontStyle === 'italic',
            size: parseInt(computedStyle.fontSize) || Z_EXCEL_DEFAULT_CONFIG.font.size,
            color: { argb: zConvertColorToArgb(computedStyle.color) },
        };
        if (computedStyle.backgroundColor && computedStyle.backgroundColor !== 'rgba(0, 0, 0, 0)') {
            excelCell.fill = {
                type: 'pattern',
                pattern: 'solid',
                fgColor: { argb: zConvertColorToArgb(computedStyle.backgroundColor) },
            };
        }
        excelCell.alignment = {
            horizontal: ZExcelService._getExcelAlignment(computedStyle.textAlign),
            vertical: ZExcelService._getExcelVerticalAlignment(computedStyle.verticalAlign),
        };
        excelCell.border = Z_EXCEL_BORDER_THIN;
    }
    static _getExcelAlignment(textAlign) {
        switch (textAlign) {
            case 'center':
                return 'center';
            case 'right':
                return 'right';
            case 'left':
                return 'left';
            default:
                return undefined;
        }
    }
    static _getExcelVerticalAlignment(verticalAlign) {
        switch (verticalAlign) {
            case 'middle':
                return 'middle';
            case 'top':
                return 'top';
            case 'bottom':
                return 'bottom';
            default:
                return 'middle';
        }
    }
    static _formatTimestamp() {
        const now = new Date();
        const year = now.getFullYear();
        const month = String(now.getMonth() + 1).padStart(2, '0');
        const day = String(now.getDate()).padStart(2, '0');
        const hours = String(now.getHours()).padStart(2, '0');
        const minutes = String(now.getMinutes()).padStart(2, '0');
        const seconds = String(now.getSeconds()).padStart(2, '0');
        return `${year}${month}${day}_${hours}${minutes}${seconds}`;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZExcelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZExcelService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZExcelService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class ZIndexDbService {
    _db = null;
    _dbName;
    _legacyDbName = null;
    _version;
    _mode;
    _dbReady;
    _stores = new Map();
    _defaultStoreName;
    _globalProtectedKeys;
    _encrypt = true;
    constructor(config = {}) {
        const baseDbName = config.dbName ?? Z_INDEXDB_DEFAULT_CONFIG.dbName;
        const scopedDbName = zBuildScopedDbName(baseDbName);
        this._dbName = scopedDbName;
        this._legacyDbName = scopedDbName === baseDbName ? null : baseDbName;
        this._version = config.version ?? Z_INDEXDB_DEFAULT_CONFIG.version;
        this._mode = config.mode ?? Z_INDEXDB_DEFAULT_CONFIG.mode;
        this._defaultStoreName = config.defaultStore ?? Z_INDEXDB_DEFAULT_CONFIG.defaultStore;
        this._globalProtectedKeys = config.protectedKeys ?? [];
        this._encrypt = config.encrypt ?? true;
        if (config.stores?.length) {
            config.stores.forEach(store => this._stores.set(store.name, store));
        }
        if (!this._stores.has(this._defaultStoreName)) {
            this._stores.set(this._defaultStoreName, { name: this._defaultStoreName });
        }
        this._dbReady = this._initDb();
    }
    _getStoreConfig(storeName) {
        const name = storeName ?? this._defaultStoreName;
        return this._stores.get(name) ?? { name };
    }
    /**
     * Determines if encryption should be used.
     * Priority: per-call > store-level > global
     * @param storeConfig - Store configuration
     * @param perCallEncrypt - Per-call encrypt override (highest priority)
     */
    _shouldEncrypt(storeConfig, perCallEncrypt) {
        if (perCallEncrypt !== undefined) {
            return perCallEncrypt;
        }
        return storeConfig.encrypt ?? this._encrypt;
    }
    _normalizeLegacyKey(key) {
        return key.trim().toLowerCase();
    }
    _normalizeKey(key) {
        return zBuildScopedCacheKey(this._normalizeLegacyKey(key));
    }
    _resolveLegacyNormalizedKey(key) {
        const legacyKey = this._normalizeLegacyKey(key);
        const scopedKey = this._normalizeKey(key);
        return legacyKey === scopedKey ? null : legacyKey;
    }
    _buildStorageKeys(key, encrypt) {
        const scopedKey = this._normalizeKey(key);
        const legacyKey = this._resolveLegacyNormalizedKey(key);
        const logicalKeys = legacyKey ? [scopedKey, legacyKey] : [scopedKey];
        if (!encrypt) {
            return logicalKeys;
        }
        return logicalKeys.map(item => this._encryptData(item));
    }
    _encryptData(data) {
        const payload = {
            __z: true,
            __t: typeof data,
            __d: data,
        };
        return btoa(encodeURIComponent(JSON.stringify(payload)));
    }
    _decryptData(data) {
        try {
            const decoded = JSON.parse(decodeURIComponent(atob(data)));
            if (!decoded.__z) {
                return null;
            }
            return decoded.__d;
        }
        catch {
            return null;
        }
    }
    _closeConnection() {
        if (this._db) {
            this._db.close();
            this._db = null;
        }
    }
    async _reconnect() {
        this._closeConnection();
        this._dbReady = this._initDb();
        await this._dbReady;
    }
    async _withRetry(operation, retries = 3) {
        let lastError;
        for (let i = 0; i < retries; i++) {
            try {
                return await operation();
            }
            catch (error) {
                lastError = error;
                if (error instanceof Error && error.name === 'InvalidStateError') {
                    await this._reconnect();
                }
                await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 100));
            }
        }
        throw lastError;
    }
    async _initDb() {
        if (this._db) {
            return;
        }
        await this._adoptLegacyDbNameIfNeeded();
        const currentVersion = await this._getCurrentDbVersion();
        if (currentVersion >= Z_INDEXDB_MAX_VERSION) {
            await this._resetDatabase();
            this._version = 1;
        }
        if (currentVersion > this._version && currentVersion < Z_INDEXDB_MAX_VERSION) {
            this._version = currentVersion;
        }
        return new Promise((resolve, reject) => {
            try {
                const request = indexedDB.open(this._dbName, this._version);
                request.onupgradeneeded = event => {
                    const db = event.target.result;
                    const existingStores = Array.from(db.objectStoreNames);
                    existingStores.forEach(storeName => {
                        if (!this._stores.has(storeName)) {
                            db.deleteObjectStore(storeName);
                        }
                    });
                    this._stores.forEach((_, storeName) => {
                        if (!db.objectStoreNames.contains(storeName)) {
                            db.createObjectStore(storeName);
                        }
                    });
                };
                request.onsuccess = event => {
                    this._db = event.target.result;
                    this._version = this._db.version;
                    this._db.onerror = e => console.error('[ZIndexDb] Error:', e.target.error);
                    this._db.onclose = () => {
                        this._db = null;
                    };
                    const missingStores = this._getMissingStores();
                    if (missingStores.length > 0) {
                        this._triggerUpgrade().then(resolve).catch(reject);
                        return;
                    }
                    resolve();
                };
                request.onerror = event => {
                    console.error('[ZIndexDb] Open error:', event.target.error);
                    reject(event.target.error);
                };
            }
            catch (error) {
                reject(error);
            }
        });
    }
    async _adoptLegacyDbNameIfNeeded() {
        if (!this._legacyDbName) {
            return;
        }
        const supportsDatabasesApi = typeof indexedDB.databases === 'function';
        if (!supportsDatabasesApi) {
            const scopedMeta = await this._probeDbMetadataByOpen(this._dbName);
            if (scopedMeta.hasStores) {
                return;
            }
            const legacyMeta = await this._probeDbMetadataByOpen(this._legacyDbName);
            if (!legacyMeta.hasStores) {
                return;
            }
            this._dbName = this._legacyDbName;
            this._version = Math.max(this._version, legacyMeta.version);
            this._legacyDbName = null;
            return;
        }
        const scopedVersion = await this._getExistingDbVersion(this._dbName);
        if (scopedVersion > 0) {
            return;
        }
        const legacyVersion = await this._getExistingDbVersion(this._legacyDbName);
        if (legacyVersion <= 0) {
            return;
        }
        this._dbName = this._legacyDbName;
        this._version = Math.max(this._version, legacyVersion);
        this._legacyDbName = null;
    }
    _getExistingDbVersion(dbName) {
        const databasesApi = indexedDB.databases;
        if (!databasesApi) {
            return Promise.resolve(0);
        }
        return databasesApi
            .call(indexedDB)
            .then(databases => {
            const matchedDb = databases.find(database => database.name === dbName);
            return typeof matchedDb?.version === 'number' ? matchedDb.version : 0;
        })
            .catch(() => 0);
    }
    _probeDbMetadataByOpen(dbName) {
        return new Promise(resolve => {
            try {
                const request = indexedDB.open(dbName);
                request.onsuccess = event => {
                    const db = event.target.result;
                    const { version } = db;
                    const hasStores = db.objectStoreNames.length > 0;
                    db.close();
                    if (hasStores || version > 1) {
                        resolve({ version, hasStores });
                        return;
                    }
                    try {
                        const deleteRequest = indexedDB.deleteDatabase(dbName);
                        deleteRequest.onsuccess = () => resolve({ version: 0, hasStores: false });
                        deleteRequest.onerror = () => resolve({ version: 0, hasStores: false });
                        deleteRequest.onblocked = () => resolve({ version: 0, hasStores: false });
                    }
                    catch {
                        resolve({ version: 0, hasStores: false });
                    }
                };
                request.onerror = () => resolve({ version: 0, hasStores: false });
            }
            catch {
                resolve({ version: 0, hasStores: false });
            }
        });
    }
    _getCurrentDbVersion() {
        return this._getExistingDbVersion(this._dbName).then(version => {
            if (version > 0) {
                return version;
            }
            return this._probeDbMetadataByOpen(this._dbName).then(metadata => (metadata.version > 0 ? metadata.version : 1));
        });
    }
    async _triggerUpgrade() {
        this._closeConnection();
        const isVersionLimitReached = this._version >= Z_INDEXDB_MAX_VERSION;
        if (isVersionLimitReached) {
            await this._resetDatabase();
        }
        this._version = isVersionLimitReached ? 1 : this._version + 1;
        this._dbReady = this._initDb();
        await this._dbReady;
    }
    async _resetDatabase() {
        this._closeConnection();
        return new Promise((resolve, reject) => {
            const deleteRequest = indexedDB.deleteDatabase(this._dbName);
            deleteRequest.onsuccess = () => resolve();
            deleteRequest.onerror = () => reject(deleteRequest.error);
            deleteRequest.onblocked = () => {
                setTimeout(() => this._resetDatabase().then(resolve).catch(reject), 100);
            };
        });
    }
    _getMissingStores() {
        if (!this._db) {
            return [];
        }
        const existingStores = Array.from(this._db.objectStoreNames);
        const requiredStores = Array.from(this._stores.keys());
        return requiredStores.filter(store => !existingStores.includes(store));
    }
    _ensureStoreExists(storeName) {
        if (!this._db) {
            return false;
        }
        return this._db.objectStoreNames.contains(storeName);
    }
    async get(key, options) {
        const storeConfig = this._getStoreConfig(options?.storeName);
        const shouldEncrypt = this._shouldEncrypt(storeConfig, options?.encrypt);
        const defaultValue = options?.defaultValue;
        return this._withRetry(async () => {
            await this._dbReady;
            if (!this._db) {
                await this._reconnect();
                if (!this._db) {
                    return defaultValue;
                }
            }
            if (!this._ensureStoreExists(storeConfig.name)) {
                await this._triggerUpgrade();
                if (!this._ensureStoreExists(storeConfig.name)) {
                    return defaultValue;
                }
            }
            const storageKeys = this._buildStorageKeys(key, shouldEncrypt);
            return new Promise(resolve => {
                try {
                    const transaction = this._db.transaction([storeConfig.name], this._mode);
                    const store = transaction.objectStore(storeConfig.name);
                    const request = store.get(storageKeys[0]);
                    request.onsuccess = () => {
                        const resolveData = (rawValue) => {
                            try {
                                if (!rawValue) {
                                    resolve(defaultValue);
                                    return;
                                }
                                const decrypted = shouldEncrypt ? this._decryptData(rawValue) : rawValue;
                                resolve(decrypted ?? defaultValue);
                            }
                            catch {
                                resolve(defaultValue);
                            }
                        };
                        if (request.result) {
                            resolveData(request.result);
                            return;
                        }
                        if (storageKeys.length < 2) {
                            resolve(defaultValue);
                            return;
                        }
                        const legacyRequest = store.get(storageKeys[1]);
                        legacyRequest.onsuccess = () => resolveData(legacyRequest.result);
                        legacyRequest.onerror = () => resolve(defaultValue);
                    };
                    request.onerror = () => resolve(defaultValue);
                    transaction.onerror = () => resolve(defaultValue);
                }
                catch {
                    resolve(defaultValue);
                }
            });
        });
    }
    async set(key, value, options) {
        const storeConfig = this._getStoreConfig(options?.storeName);
        const shouldEncrypt = this._shouldEncrypt(storeConfig, options?.encrypt);
        return this._withRetry(async () => {
            await this._dbReady;
            if (!this._db) {
                await this._reconnect();
                if (!this._db) {
                    throw new Error('Database not initialized');
                }
            }
            if (!this._ensureStoreExists(storeConfig.name)) {
                await this._triggerUpgrade();
                if (!this._ensureStoreExists(storeConfig.name)) {
                    throw new Error(`Failed to create store "${storeConfig.name}"`);
                }
            }
            const storageKeys = this._buildStorageKeys(key, shouldEncrypt);
            const _key = storageKeys[0];
            const _value = shouldEncrypt ? this._encryptData(value) : value;
            return new Promise((resolve, reject) => {
                try {
                    const transaction = this._db.transaction([storeConfig.name], 'readwrite');
                    const store = transaction.objectStore(storeConfig.name);
                    const request = store.put(_value, _key);
                    if (storageKeys.length > 1) {
                        store.delete(storageKeys[1]);
                    }
                    request.onerror = () => reject(request.error);
                    transaction.oncomplete = () => resolve();
                    transaction.onerror = () => reject(transaction.error);
                }
                catch (error) {
                    reject(error);
                }
            });
        });
    }
    /**
     * Delete key(s) from IndexDB. If storeName not provided, deletes from all stores.
     * Tries both encrypted and non-encrypted versions of the key.
     */
    async delete(key, storeName) {
        if (storeName === undefined) {
            await Promise.all(this.getStoreNames().map(name => this._deleteFromStore(key, name)));
            return;
        }
        await this._deleteFromStore(key, storeName);
    }
    async _deleteFromStore(key, storeName) {
        const storeConfig = this._getStoreConfig(storeName);
        return this._withRetry(async () => {
            await this._dbReady;
            if (!this._db) {
                await this._reconnect();
                if (!this._db) {
                    throw new Error('Database not initialized');
                }
            }
            if (!this._ensureStoreExists(storeConfig.name)) {
                return;
            }
            const keys = Array.isArray(key) ? key : [key];
            return new Promise((resolve, reject) => {
                try {
                    const transaction = this._db.transaction([storeConfig.name], 'readwrite');
                    const store = transaction.objectStore(storeConfig.name);
                    keys.forEach(k => {
                        const plainKeys = this._buildStorageKeys(k, false);
                        const encryptedKeys = this._buildStorageKeys(k, true);
                        plainKeys.forEach(item => store.delete(item));
                        encryptedKeys.forEach(item => store.delete(item));
                    });
                    transaction.oncomplete = () => resolve();
                    transaction.onerror = () => reject(transaction.error);
                }
                catch (error) {
                    reject(error);
                }
            });
        });
    }
    async clear(storeName) {
        const storeConfig = this._getStoreConfig(storeName);
        const protectedKeys = [...this._globalProtectedKeys, ...(storeConfig.protectedKeys ?? [])];
        return this._withRetry(async () => {
            await this._dbReady;
            if (!this._db) {
                await this._reconnect();
                if (!this._db) {
                    throw new Error('Database not initialized');
                }
            }
            if (!this._ensureStoreExists(storeConfig.name)) {
                return;
            }
            if (protectedKeys.length === 0) {
                return new Promise((resolve, reject) => {
                    const transaction = this._db.transaction([storeConfig.name], 'readwrite');
                    const store = transaction.objectStore(storeConfig.name);
                    const request = store.clear();
                    request.onsuccess = () => resolve();
                    request.onerror = () => reject(request.error);
                });
            }
            return new Promise((resolve, reject) => {
                try {
                    const transaction = this._db.transaction([storeConfig.name], 'readwrite');
                    const store = transaction.objectStore(storeConfig.name);
                    const keyRequest = store.getAllKeys();
                    const protectedSet = new Set();
                    const protectedLogicalKeys = new Set();
                    protectedKeys.forEach(k => {
                        const plainKeys = this._buildStorageKeys(k, false);
                        const encryptedKeys = this._buildStorageKeys(k, true);
                        plainKeys.forEach(item => {
                            protectedSet.add(item);
                            protectedLogicalKeys.add(item);
                        });
                        encryptedKeys.forEach(item => protectedSet.add(item));
                    });
                    keyRequest.onsuccess = () => {
                        const allKeys = keyRequest.result;
                        allKeys.forEach(k => {
                            if (!protectedSet.has(k)) {
                                const decrypted = this._decryptData(k);
                                if (!decrypted || !protectedLogicalKeys.has(decrypted)) {
                                    store.delete(k);
                                }
                            }
                        });
                    };
                    keyRequest.onerror = () => reject(keyRequest.error);
                    transaction.oncomplete = () => resolve();
                    transaction.onerror = () => reject(transaction.error);
                }
                catch (error) {
                    reject(error);
                }
            });
        });
    }
    async getAll(storeName) {
        const storeConfig = this._getStoreConfig(storeName);
        return this._withRetry(async () => {
            await this._dbReady;
            if (!this._db) {
                await this._reconnect();
                if (!this._db) {
                    return {};
                }
            }
            if (!this._ensureStoreExists(storeConfig.name)) {
                return {};
            }
            return new Promise(resolve => {
                try {
                    const transaction = this._db.transaction([storeConfig.name], this._mode);
                    const store = transaction.objectStore(storeConfig.name);
                    const keysRequest = store.getAllKeys();
                    const valuesRequest = store.getAll();
                    const results = {};
                    keysRequest.onsuccess = () => {
                        const keys = keysRequest.result;
                        const values = valuesRequest.result;
                        keys.forEach((key, index) => {
                            if (typeof key === 'string') {
                                try {
                                    const _key = this._shouldEncrypt(storeConfig) ? this._decryptData(key) : key;
                                    const _value = this._shouldEncrypt(storeConfig) ? this._decryptData(values[index]) : values[index];
                                    if (_key && _value !== null) {
                                        results[zStripScopedCacheKey(_key)] = _value;
                                    }
                                }
                                catch {
                                    console.warn(`[ZIndexDb] Failed to decrypt entry for key: ${key}`);
                                }
                            }
                        });
                        resolve(results);
                    };
                    keysRequest.onerror = () => resolve({});
                    transaction.onerror = () => resolve({});
                }
                catch {
                    resolve({});
                }
            });
        });
    }
    async setMultiple(items, options) {
        const storeConfig = this._getStoreConfig(options?.storeName);
        const shouldEncrypt = this._shouldEncrypt(storeConfig, options?.encrypt);
        const entries = Object.entries(items);
        await this._dbReady;
        if (this._db && !this._ensureStoreExists(storeConfig.name)) {
            await this._triggerUpgrade();
        }
        for (let i = 0; i < entries.length; i += Z_INDEXDB_BATCH_SIZE) {
            const batch = entries.slice(i, i + Z_INDEXDB_BATCH_SIZE);
            await this._withRetry(async () => {
                await this._dbReady;
                if (!this._db) {
                    await this._reconnect();
                    if (!this._db) {
                        throw new Error('Database not initialized');
                    }
                }
                if (!this._ensureStoreExists(storeConfig.name)) {
                    throw new Error(`Store "${storeConfig.name}" does not exist`);
                }
                return new Promise((resolve, reject) => {
                    const transaction = this._db.transaction([storeConfig.name], 'readwrite');
                    const store = transaction.objectStore(storeConfig.name);
                    batch.forEach(([key, value]) => {
                        const storageKeys = this._buildStorageKeys(key, shouldEncrypt);
                        const _key = storageKeys[0];
                        const _value = shouldEncrypt ? this._encryptData(value) : value;
                        store.put(_value, _key);
                        if (storageKeys.length > 1) {
                            store.delete(storageKeys[1]);
                        }
                    });
                    transaction.oncomplete = () => resolve();
                    transaction.onerror = () => reject(transaction.error);
                });
            });
        }
    }
    getStoreNames() {
        return Array.from(this._stores.keys());
    }
    hasStore(storeName) {
        return this._stores.has(storeName);
    }
    async addStore(config) {
        if (this._stores.has(config.name)) {
            return;
        }
        this._stores.set(config.name, config);
        await this._triggerUpgrade();
    }
    async clearAll() {
        const storeNames = this.getStoreNames();
        await Promise.all(storeNames.map(storeName => this.clear(storeName)));
    }
}

const Z_THEME_CONFIG = new InjectionToken('Z_THEME_CONFIG');
const Z_DEFAULT_THEME = 'neutral';
/** Single cache key for all theme preferences */
const Z_THEME_PREFERENCES_CACHE_KEY = 'Z_THEME_PREFERENCES';
const Z_THEME_CSS_MAP = {
    neutral: '',
    green: 'assets/css/themes/green.css',
    orange: 'assets/css/themes/orange.css',
    violet: 'assets/css/themes/violet.css',
    stone: 'assets/css/themes/stone.css',
    zinc: 'assets/css/themes/zinc.css',
    gray: 'assets/css/themes/gray.css',
    slate: 'assets/css/themes/slate.css',
    hospital: 'assets/css/themes/hospital.css',
};

const Z_LANG_CACHE_KEY = 'Z_LANGUAGE';

class ZTranslateService {
    _translate = inject(TranslateService);
    _destroyRef = inject(DestroyRef);
    currentLang = signal('vi', ...(ngDevMode ? [{ debugName: "currentLang" }] : []));
    availableLangs = signal(['vi', 'en'], ...(ngDevMode ? [{ debugName: "availableLangs" }] : []));
    constructor() {
        this._initialize();
    }
    _initialize() {
        const currentLang = this._translate.getCurrentLang() || ZCacheService.get(Z_LANG_CACHE_KEY) || 'vi';
        this._setCurrentLang(currentLang);
        this._syncAvailableLangs();
        this._bindTranslateEvents();
    }
    _bindTranslateEvents() {
        this._translate.onLangChange.pipe(takeUntilDestroyed(this._destroyRef)).subscribe(event => {
            if (!event.lang) {
                return;
            }
            this._setCurrentLang(event.lang);
        });
    }
    _setCurrentLang(lang) {
        this.currentLang.set(lang);
        ZCacheService.set(Z_LANG_CACHE_KEY, lang);
        document.documentElement.lang = lang;
    }
    _syncAvailableLangs() {
        const langs = this._translate.getLangs();
        if (langs.length) {
            this.availableLangs.set([...langs]);
        }
    }
    use(lang) {
        if (!lang || lang === this.currentLang()) {
            return;
        }
        void firstValueFrom(this._translate.use(lang)).catch(() => {
            // Keep current language when target language load fails.
        });
    }
    getLang() {
        return this.currentLang();
    }
    getLocale() {
        return Z_LANG_TO_LOCALE[this.currentLang()] ?? 'vi-VN';
    }
    locale() {
        return computed(() => Z_LANG_TO_LOCALE[this.currentLang()] ?? 'vi-VN');
    }
    instant(key, params) {
        if (!key) {
            return '';
        }
        return this._translate.instant(key, params);
    }
    /**
     * Returns a computed signal that automatically updates when language changes.
     * Use this instead of instant() when you need reactive translations.
     *
     * @example
     * ```typescript
     * // In component
     * protected readonly title = this._zTranslate.translate('my_title_key');
     *
     * // In template
     * <h1>{{ title() }}</h1>
     * ```
     */
    translate(key, params) {
        return computed(() => {
            this.currentLang();
            return this.instant(key, params);
        });
    }
    async get(key, params) {
        if (!key) {
            return '';
        }
        return firstValueFrom(this._translate.get(key, params));
    }
    get$(key, params) {
        return this._translate.get(key, params);
    }
    stream$(key, params) {
        return this._translate.stream(key, params);
    }
    async getMany(keys, params) {
        const results = {};
        await Promise.all(keys.map(async (key) => {
            results[key] = await this.get(key, params);
        }));
        return results;
    }
    has(key) {
        const translation = this._translate.instant(key);
        return translation !== key;
    }
    setTranslation(lang, translations, shouldMerge = true) {
        this._translate.setTranslation(lang, translations, shouldMerge);
        this._syncAvailableLangs();
    }
    getNgxTranslate() {
        return this._translate;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZTranslateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZTranslateService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZTranslateService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [] });

const Z_HTTP_DEFAULT_CONFIG = {
    defaultCacheTime: 3 * 60 * 1000,
    networkCheckCacheTime: 5000,
    tokenKey: 'Z_TOKEN',
    userInfoKey: 'Z_USER_INFO',
    lastUrlKey: 'Z_LAST_URL',
    loginRoute: '/login',
    homeRoute: '/',
    unauthorizedRoute: '/401',
};
class ZHttpAbstractService {
    http = inject(HttpClient);
    router = inject(Router);
    cacheService = ZCacheService;
    translateService = inject(ZTranslateService);
    indexDbService;
    _httpConfig = null;
    _networkCache = null;
    constructor() {
        this.indexDbService = new ZIndexDbService({
            dbName: 'ZHttpCache',
            defaultStore: 'zHttpCache',
            stores: [{ name: 'zHttpCache', encrypt: true }],
        });
    }
    /**
     * Returns the HTTP configuration. Override this method in subclasses to customize.
     * @example
     * ```typescript
     * protected override setHttpConfig(): Partial<ZHttpConfig> {
     *   return {
     *     tokenKey: 'MY_TOKEN',
     *     loginRoute: '/auth/login',
     *   };
     * }
     * ```
     */
    setHttpConfig() {
        return {};
    }
    get config() {
        if (!this._httpConfig) {
            this._httpConfig = { ...Z_HTTP_DEFAULT_CONFIG, ...this.setHttpConfig() };
        }
        return this._httpConfig;
    }
    get(endpoint, options) {
        const httpCall = () => {
            const { headers, params } = this._buildRequest(options);
            const httpOptions = this._buildHttpOptions({ ...options, headers, params });
            const url = `${this.resolveBaseUrl(options?.api)}${endpoint}`;
            return this.http.get(url, httpOptions);
        };
        return this._handleCacheAndRequest('GET', endpoint, httpCall, options);
    }
    post(endpoint, body, options) {
        const httpCall = () => {
            const { headers, body: transformedBody } = this._buildRequest(options, body);
            const httpOptions = this._buildHttpOptions({ ...options, headers });
            const url = `${this.resolveBaseUrl(options?.api)}${endpoint}`;
            return this.http.post(url, transformedBody, httpOptions);
        };
        return this._handleCacheAndRequest('POST', endpoint, httpCall, options, body);
    }
    put(endpoint, body, options) {
        const httpCall = () => {
            const { headers, body: transformedBody } = this._buildRequest(options, body);
            const httpOptions = this._buildHttpOptions({ ...options, headers });
            const url = `${this.resolveBaseUrl(options?.api)}${endpoint}`;
            return this.http.put(url, transformedBody, httpOptions);
        };
        return this._handleCacheAndRequest('PUT', endpoint, httpCall, options, body);
    }
    patch(endpoint, body, options) {
        const httpCall = () => {
            const { headers, body: transformedBody } = this._buildRequest(options, body);
            const httpOptions = this._buildHttpOptions({ ...options, headers });
            const url = `${this.resolveBaseUrl(options?.api)}${endpoint}`;
            return this.http.patch(url, transformedBody, httpOptions);
        };
        return this._handleCacheAndRequest('PATCH', endpoint, httpCall, options, body);
    }
    delete(endpoint, body, options) {
        const httpCall = () => {
            const { headers, body: transformedBody } = this._buildRequest(options, body);
            const httpOptions = this._buildHttpOptions({
                ...options,
                headers,
                body: transformedBody,
            });
            if ('transferCache' in httpOptions) {
                delete httpOptions['transferCache'];
            }
            const url = `${this.resolveBaseUrl(options?.api)}${endpoint}`;
            return this.http.delete(url, httpOptions);
        };
        return this._handleCacheAndRequest('DELETE', endpoint, httpCall, options, body);
    }
    _buildRequest(options, body) {
        let headers = new HttpHeaders().set('Accept', 'application/json');
        const contentType = options?.contentType ?? 'json';
        let finalBody = body;
        let params;
        if (contentType === 'json') {
            headers = headers.set('Content-Type', 'application/json');
            if (body !== undefined && body !== null) {
                finalBody = JSON.stringify(body);
            }
        }
        if (contentType === 'urlencoded') {
            headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');
            if (body && typeof body === 'object') {
                const urlParams = new URLSearchParams();
                Object.entries(body).forEach(([key, value]) => {
                    urlParams.set(key, String(value));
                });
                finalBody = urlParams.toString();
            }
        }
        if (contentType === 'form-data') {
            if (body instanceof FormData) {
                finalBody = body;
            }
            if (!(body instanceof FormData) && body && typeof body === 'object') {
                const formData = new FormData();
                Object.entries(body).forEach(([key, value]) => {
                    formData.append(key, value);
                });
                finalBody = formData;
            }
        }
        if (options?.headers && !(options.headers instanceof HttpHeaders)) {
            const headersObj = options.headers;
            Object.keys(headersObj).forEach(key => {
                const value = headersObj[key];
                headers = headers.set(key, Array.isArray(value) ? value.join(',') : value);
            });
        }
        const token = this.cacheService.get(this.config.tokenKey);
        if (token && !options?.skipToken) {
            headers = headers.set('Authorization', `Bearer ${token}`);
        }
        if (options?.params && !(options.params instanceof Object.getPrototypeOf(URLSearchParams).constructor)) {
            const paramsObj = options.params;
            params = {};
            Object.keys(paramsObj).forEach(key => {
                const value = paramsObj[key];
                if (value !== undefined && value !== null) {
                    params[key] = String(value);
                }
            });
        }
        return { headers, body: finalBody, params };
    }
    _buildHttpOptions(options) {
        const httpOptions = { ...options };
        delete httpOptions['enableCache'];
        delete httpOptions['keyCache'];
        delete httpOptions['timeCache'];
        delete httpOptions['contentType'];
        delete httpOptions['api'];
        delete httpOptions['skipToken'];
        delete httpOptions['skipErrorToast'];
        return httpOptions;
    }
    _handleCacheAndRequest(method, endpoint, httpCall, options, body) {
        if (!options?.enableCache) {
            return this._executeHttpCall(httpCall, options, method, endpoint, body);
        }
        const cacheKey = this._resolveCacheKey(method, endpoint, options, body);
        return from(this._getCachedData(cacheKey)).pipe(switchMap(cachedData => {
            if (cachedData !== null) {
                return of(cachedData);
            }
            return this._executeHttpCall(httpCall, options, method, endpoint, body);
        }), catchError(() => this._executeHttpCall(httpCall, options, method, endpoint, body)));
    }
    _executeHttpCall(httpCall, options, method, endpoint, body) {
        return this._detectNetworkStatus().pipe(switchMap(isOnline => {
            if (!isOnline) {
                const error = {
                    message: this.translateService.instant('i18n_z_ui_http_network_offline'),
                    status: 0,
                };
                return throwError(() => error);
            }
            return httpCall().pipe(catchError(err => this._handleError(err, options, method, endpoint, body)), tap(data => {
                if (options?.log) {
                    console.log('[ZHttp] Request succeeded:', {
                        method,
                        body,
                        endpoint: `${this.resolveBaseUrl(options?.api)}${endpoint}`,
                        data,
                    });
                }
                if (options?.enableCache && method && endpoint) {
                    const cacheKey = this._resolveCacheKey(method, endpoint, options, body);
                    const ttl = options.timeCache ?? this.config.defaultCacheTime;
                    this._cacheData(cacheKey, data, ttl).catch(console.warn);
                }
            }));
        }));
    }
    _generateCacheKey(method, endpoint, options, body) {
        const api = options?.api ?? 'default';
        const params = options?.params ? JSON.stringify(options.params) : '';
        const bodyStr = body ? JSON.stringify(body) : '';
        return `${method}_${api}_${endpoint}_${params}_${bodyStr}`;
    }
    _resolveCacheKey(method, endpoint, options, body) {
        if (options?.keyCache !== undefined) {
            return options.keyCache;
        }
        return this._generateCacheKey(method, endpoint, options, body);
    }
    async _cacheData(key, data, ttl) {
        const cacheEntry = { data, timestamp: Date.now(), ttl };
        await this.indexDbService.set(key, cacheEntry, { storeName: 'HttpCache' });
    }
    async _getCachedData(key) {
        try {
            const cached = await this.indexDbService.get(key, { storeName: 'HttpCache' });
            if (!cached) {
                return null;
            }
            if (Date.now() - cached.timestamp > cached.ttl) {
                await this.indexDbService.delete(key, 'HttpCache');
                return null;
            }
            return cached.data;
        }
        catch {
            return null;
        }
    }
    _detectNetworkStatus() {
        const now = Date.now();
        if (this._networkCache && now - this._networkCache.time < this.config.networkCheckCacheTime) {
            return of(this._networkCache.isOnline);
        }
        if (!navigator.onLine) {
            this._networkCache = { time: now, isOnline: false };
            return of(false);
        }
        return this._checkNetworkWithRetry().pipe(map(isOnline => {
            this._networkCache = { time: Date.now(), isOnline };
            return isOnline;
        }), catchError(() => {
            this._networkCache = { time: Date.now(), isOnline: false };
            return of(false);
        }));
    }
    _checkNetworkWithRetry(retries = 2) {
        return defer(() => this._performNetworkCheck()).pipe(catchError(() => {
            if (retries > 0) {
                return this._checkNetworkWithRetry(retries - 1);
            }
            return of(false);
        }));
    }
    _performNetworkCheck() {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), 3000);
        return from(fetch('data:,', { method: 'HEAD', signal: controller.signal })).pipe(tap(() => clearTimeout(timeoutId)), map(() => true), catchError(() => {
            clearTimeout(timeoutId);
            throw new Error('Network check failed');
        }));
    }
    _handleError(error, options, method, endpoint, body) {
        const statusCode = error instanceof HttpErrorResponse ? error.status : 500;
        const fullEndpoint = `${this.resolveBaseUrl(options?.api)}${endpoint}`;
        const httpError = this._buildErrorObject(error, statusCode, fullEndpoint, method, body, options);
        const localizedError = this._localizeError(httpError, statusCode);
        console.error('[ZHttp] Request failed:', {
            method,
            endpoint: fullEndpoint,
            status: statusCode,
            error: localizedError,
        });
        void this._handleStatusCode(statusCode);
        return throwError(() => localizedError);
    }
    _buildErrorObject(error, statusCode, endpoint, method, body, options) {
        const baseError = { message: '', status: statusCode, endpoint, method, body, options };
        if (!(error instanceof HttpErrorResponse)) {
            return {
                ...baseError,
                error: error.message,
                message: error.message || this.translateService.instant('i18n_z_ui_http_error_default'),
            };
        }
        const errorBody = error.error;
        if (typeof errorBody?.message === 'string') {
            const message = errorBody.message === 'Failed to fetch'
                ? this.translateService.instant('i18n_z_ui_http_error_failed_to_fetch', { url: endpoint })
                : errorBody.message;
            return { ...baseError, error: errorBody, message };
        }
        if (Array.isArray(errorBody?.errors) && errorBody.errors.length > 0) {
            const message = errorBody.errors
                .map((err) => (typeof err === 'string' ? err : JSON.stringify(err)))
                .join(', ');
            return { ...baseError, error: errorBody, message };
        }
        if (typeof errorBody === 'object' &&
            errorBody !== null &&
            !Array.isArray(errorBody) &&
            Object.keys(errorBody).length > 0) {
            return { ...baseError, error: errorBody, message: JSON.stringify(errorBody) };
        }
        if (typeof errorBody === 'string') {
            return { ...baseError, error: errorBody, message: errorBody };
        }
        return {
            ...baseError,
            error: error.message,
            message: error.message || this.translateService.instant('i18n_z_ui_http_error_default'),
        };
    }
    _localizeError(error, statusCode) {
        const localizedError = { ...error };
        const errorMessages = {
            400: error.message || this.translateService.instant('i18n_z_ui_http_error_400'),
            401: this._get401Message(),
            403: this.translateService.instant('i18n_z_ui_http_error_403'),
            404: this.translateService.instant('i18n_z_ui_http_error_404'),
            500: this.translateService.instant('i18n_z_ui_http_error_500'),
        };
        if (errorMessages[statusCode]) {
            localizedError.message = errorMessages[statusCode];
        }
        return localizedError;
    }
    _get401Message() {
        const token = this.cacheService.get(this.config.tokenKey);
        if (!token) {
            return this.translateService.instant('i18n_z_ui_http_error_401_expired');
        }
        return this.translateService.instant('i18n_z_ui_http_error_401_permission');
    }
    async _handleStatusCode(statusCode) {
        if (statusCode !== 401) {
            return;
        }
        await this._redirectToLogin();
    }
    async _redirectToLogin() {
        const currentUrl = this.router.url;
        const shouldSaveUrl = currentUrl &&
            currentUrl !== '/' &&
            !currentUrl.includes(this.config.loginRoute) &&
            !currentUrl.includes(this.config.unauthorizedRoute);
        if (shouldSaveUrl) {
            this.cacheService.set(this.config.lastUrlKey, currentUrl);
            await this.indexDbService.set(this.config.lastUrlKey, currentUrl).catch(console.error);
        }
        this.cacheService.deleteMultiple([this.config.tokenKey, this.config.userInfoKey]);
        await this.indexDbService.clear('HttpCache').catch(console.error);
        this.router.navigate([this.config.loginRoute]).catch(console.error);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZHttpAbstractService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZHttpAbstractService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZHttpAbstractService, decorators: [{
            type: Injectable
        }], ctorParameters: () => [] });

/**
 * Service to coordinate overlay container lifecycle events.
 * Used by overlay containers (z-modal, z-drawer, etc.) to notify
 * child components (z-popover, z-select dropdown, etc.) when they should close.
 *
 * This enables smooth closing behavior when a parent overlay container closes -
 * all child popovers/dropdowns will close immediately without waiting for DOM removal.
 */
class ZOverlayContainerService {
    _closeAll$ = new Subject();
    /**
     * Observable that emits when an overlay container starts closing.
     * Child components should subscribe to this and close themselves immediately.
     */
    closeAll$ = this._closeAll$.asObservable();
    /**
     * Called by overlay containers (z-modal, z-drawer, etc.) when they start closing.
     * This will notify all subscribed child components to close immediately.
     */
    notifyClosing() {
        this._closeAll$.next();
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZOverlayContainerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZOverlayContainerService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZOverlayContainerService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

/**
 * Global z-index counter for overlay components (z-modal, z-drawer, etc.).
 *
 * Each time an overlay opens, it calls `next()` to get a unique, incrementing
 * z-index value. This guarantees that overlays opened later always stack on top
 * of earlier ones — regardless of DOM order or HMR re-creation.
 */
class ZOverlayZIndexService {
    _currentZIndex = 1000;
    _platformId = inject(PLATFORM_ID);
    /**
     * Returns the next z-index value and increments the counter.
     * Kept for backward compatibility with existing overlay flow.
     */
    next() {
        const zIndex = this._currentZIndex;
        this._currentZIndex += 2; // +2 to leave room for backdrop (zIndex - 1)
        return zIndex;
    }
    applyToOverlay(_overlayRef) {
        return this.next();
    }
    deferMoveToTop(overlayRef) {
        if (!isPlatformBrowser(this._platformId)) {
            return;
        }
        queueMicrotask(() => {
            this.moveToTop(overlayRef);
        });
    }
    moveToTop(overlayRef) {
        if (!isPlatformBrowser(this._platformId)) {
            return;
        }
        const wrapperElement = overlayRef.hostElement;
        if (!wrapperElement) {
            return;
        }
        if (wrapperElement.hasAttribute('popover')) {
            const popoverWrapper = wrapperElement;
            if (popoverWrapper.hidePopover && popoverWrapper.showPopover) {
                try {
                    popoverWrapper.hidePopover();
                    popoverWrapper.showPopover();
                    return;
                }
                catch {
                    // fall through to non-popover fallback
                }
            }
        }
        const parent = wrapperElement.parentElement;
        if (parent) {
            parent.appendChild(wrapperElement);
        }
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZOverlayZIndexService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZOverlayZIndexService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZOverlayZIndexService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class ZSubjectService {
    _subjects = new Map();
    _behaviorSubjects = new Map();
    /**
     * Emit a value to a subject
     * @param key - Subject identifier
     * @param type - 'subject' for Subject, 'behavior' for BehaviorSubject
     * @param value - Value to emit
     */
    emit(key, type, value) {
        if (type === 'behavior') {
            const existingSubject = this._behaviorSubjects.get(key);
            if (existingSubject) {
                existingSubject.next(value);
                return;
            }
            const newSubject = new BehaviorSubject(value);
            this._behaviorSubjects.set(key, newSubject);
            return;
        }
        const existingSubject = this._subjects.get(key);
        if (existingSubject) {
            existingSubject.next(value);
            return;
        }
        const newSubject = new Subject();
        this._subjects.set(key, newSubject);
        newSubject.next(value);
    }
    /**
     * Get an observable for a subject
     * @param key - Subject identifier
     * @param type - 'subject' for Subject, 'behavior' for BehaviorSubject
     * @param initialValue - Initial value for BehaviorSubject (required for new behavior subjects)
     */
    on$(key, type, initialValue) {
        if (type === 'behavior') {
            const existingSubject = this._behaviorSubjects.get(key);
            if (existingSubject) {
                return existingSubject.asObservable();
            }
            const newSubject = new BehaviorSubject(initialValue);
            this._behaviorSubjects.set(key, newSubject);
            return newSubject.asObservable();
        }
        const existingSubject = this._subjects.get(key);
        if (existingSubject) {
            return existingSubject.asObservable();
        }
        const newSubject = new Subject();
        this._subjects.set(key, newSubject);
        return newSubject.asObservable();
    }
    /**
     * Get the current value of a BehaviorSubject
     * @param key - Subject identifier
     */
    getValue(key) {
        const subject = this._behaviorSubjects.get(key);
        if (!subject) {
            return undefined;
        }
        return subject.getValue();
    }
    /**
     * Complete and remove a subject
     * @param key - Subject identifier
     * @param type - 'subject' for Subject, 'behavior' for BehaviorSubject
     */
    complete(key, type) {
        if (type === 'behavior') {
            const subject = this._behaviorSubjects.get(key);
            if (!subject) {
                return;
            }
            subject.complete();
            this._behaviorSubjects.delete(key);
            return;
        }
        const subject = this._subjects.get(key);
        if (!subject) {
            return;
        }
        subject.complete();
        this._subjects.delete(key);
    }
    /**
     * Complete all subjects
     */
    completeAll() {
        this._subjects.forEach(subject => subject.complete());
        this._behaviorSubjects.forEach(subject => subject.complete());
        this._subjects.clear();
        this._behaviorSubjects.clear();
    }
    /**
     * Check if a subject exists
     * @param key - Subject identifier
     * @param type - 'subject' for Subject, 'behavior' for BehaviorSubject
     */
    has(key, type) {
        if (type === 'behavior') {
            return this._behaviorSubjects.has(key);
        }
        return this._subjects.has(key);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZSubjectService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZSubjectService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZSubjectService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class ZThemeService {
    _document = inject(DOCUMENT);
    _platformId = inject(PLATFORM_ID);
    _config = inject(Z_THEME_CONFIG, { optional: true });
    _isBrowser = isPlatformBrowser(this._platformId);
    _defaultTheme = this._config?.defaultTheme ?? Z_DEFAULT_THEME;
    _defaultDarkMode = this._config?.defaultDarkMode ?? true;
    _preferencesCacheKey = this._config?.cacheKey?.trim() || Z_THEME_PREFERENCES_CACHE_KEY;
    _isDark = signal(this._getInitialPreferences().isDark, ...(ngDevMode ? [{ debugName: "_isDark" }] : []));
    _currentTheme = signal(this._getInitialPreferences().theme, ...(ngDevMode ? [{ debugName: "_currentTheme" }] : []));
    _loadedThemes = new Set(['neutral']);
    isDark = this._isDark.asReadonly();
    currentTheme = this._currentTheme.asReadonly();
    constructor() {
        if (this._isBrowser) {
            this._initializeTheme();
        }
        effect(() => {
            const theme = this._currentTheme();
            const isDark = this._isDark();
            if (this._isBrowser) {
                this._savePreferences({ theme, isDark });
            }
        });
    }
    setTheme(theme) {
        this._currentTheme.set(theme);
        if (this._loadedThemes.has(theme)) {
            this._document.documentElement.setAttribute('z-theme', theme);
            return;
        }
        if (!Z_THEME_CSS_MAP[theme]) {
            this._document.documentElement.setAttribute('z-theme', theme);
            return;
        }
        this._loadThemeCSS(theme, () => {
            this._document.documentElement.setAttribute('z-theme', theme);
        });
    }
    toggleDarkMode(isDark) {
        this._isDark.set(isDark);
        if (isDark) {
            this._document.documentElement.classList.add('dark');
            return;
        }
        this._document.documentElement.classList.remove('dark');
    }
    preloadTheme(theme) {
        if (this._loadedThemes.has(theme) || !Z_THEME_CSS_MAP[theme]) {
            return;
        }
        this._loadThemeCSS(theme);
    }
    _initializeTheme() {
        const { theme, isDark } = this._getInitialPreferences();
        this.setTheme(theme);
        this.toggleDarkMode(isDark);
    }
    _getInitialPreferences() {
        const defaultPreferences = {
            theme: this._defaultTheme,
            isDark: this._defaultDarkMode,
        };
        if (!this._isBrowser) {
            return defaultPreferences;
        }
        const cached = ZCacheService.get(this._preferencesCacheKey);
        if (!cached) {
            return defaultPreferences;
        }
        return {
            theme: this._isValidTheme(cached.theme) ? cached.theme : defaultPreferences.theme,
            isDark: typeof cached.isDark === 'boolean' ? cached.isDark : defaultPreferences.isDark,
        };
    }
    _savePreferences(preferences) {
        ZCacheService.set(this._preferencesCacheKey, preferences);
    }
    _isValidTheme(theme) {
        const validThemes = [
            'green',
            'orange',
            'violet',
            'neutral',
            'stone',
            'zinc',
            'gray',
            'slate',
            'hospital',
        ];
        return typeof theme === 'string' && validThemes.includes(theme);
    }
    _loadThemeCSS(theme, onLoad) {
        const cssPath = Z_THEME_CSS_MAP[theme];
        if (!cssPath) {
            onLoad?.();
            return;
        }
        const existingLink = this._document.querySelector(`link[data-theme="${theme}"]`);
        if (existingLink) {
            this._loadedThemes.add(theme);
            onLoad?.();
            return;
        }
        const link = this._document.createElement('link');
        link.rel = 'stylesheet';
        link.href = cssPath;
        link.setAttribute('data-theme', theme);
        link.onload = () => {
            this._loadedThemes.add(theme);
            onLoad?.();
        };
        link.onerror = () => {
            console.error(`Failed to load theme "${theme}" from ${cssPath}`);
            onLoad?.();
        };
        this._document.head.appendChild(link);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZThemeService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: ZThemeService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [] });

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

export { ZCacheService, ZExcelService, ZHttpAbstractService, ZIndexDbService, ZOverlayContainerService, ZOverlayZIndexService, ZSubjectService, ZThemeService, ZTranslateService, Z_DEFAULT_THEME, Z_EXCEL_BORDER_THIN, Z_EXCEL_CHAR_WIDTH_MAP, Z_EXCEL_COLORS, Z_EXCEL_DEFAULT_CONFIG, Z_EXCEL_FONT_MULTIPLIERS, Z_EXCEL_WIDTH_LIMITS, Z_HTTP_DEFAULT_CONFIG, Z_INDEXDB_BATCH_SIZE, Z_INDEXDB_DEFAULT_CONFIG, Z_INDEXDB_MAX_VERSION, Z_LANG_CACHE_KEY, Z_LANG_TO_LOCALE, Z_THEME_CONFIG, Z_THEME_CSS_MAP, Z_THEME_PREFERENCES_CACHE_KEY, zBuildScopedCacheKey, zBuildScopedDbName, zStripScopedCacheKey };
//# sourceMappingURL=shival99-z-ui-services.mjs.map
