import { providePrimeNG } from 'primeng/config';
import Aura from '@primeuix/themes/aura';
import { updatePreset, definePreset } from '@primeuix/themes';
import { MessageService, ConfirmationService } from 'primeng/api';
import { computed } from '@angular/core';
import { tap, catchError, finalize } from 'rxjs/operators';
import { HttpContextToken } from '@angular/common/http';

/**
 * Converts a hex color string to an HSL tuple.
 * @param hex - The hex color string (e.g., "#RRGGBB" or "#RGB").
 * @returns An HSL tuple [hue, saturation, lightness] where hue is in degrees (0-360),
 * and saturation/lightness are percentages (0-100).
 */
function hexToHsl(hex) {
    let r = 0, g = 0, b = 0;
    // Handle shorthand hex codes
    if (hex.length === 4) {
        r = parseInt(hex[1] + hex[1], 16);
        g = parseInt(hex[2] + hex[2], 16);
        b = parseInt(hex[3] + hex[3], 16);
    }
    else if (hex.length === 7) {
        r = parseInt(hex.substring(1, 3), 16);
        g = parseInt(hex.substring(3, 5), 16);
        b = parseInt(hex.substring(5, 7), 16);
    }
    else {
        throw new Error('Invalid hex color format. Expected #RGB or #RRGGBB.');
    }
    r /= 255;
    g /= 255;
    b /= 255;
    const max = Math.max(r, g, b);
    const min = Math.min(r, g, b);
    let h = 0, s;
    const l = (max + min) / 2;
    if (max === min) {
        h = s = 0; // achromatic
    }
    else {
        const d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        switch (max) {
            case r:
                h = (g - b) / d + (g < b ? 6 : 0);
                break;
            case g:
                h = (b - r) / d + 2;
                break;
            case b:
                h = (r - g) / d + 4;
                break;
        }
        h /= 6;
    }
    return [h * 360, s * 100, l * 100]; // HSL in degrees, percentage, percentage
}
/**
 * Converts HSL values to a hex color string.
 * @param h - Hue (0-360).
 * @param s - Saturation (0-100).
 * @param l - Lightness (0-100).
 * @returns The hex color string (e.g., "#RRGGBB").
 */
function hslToHex(h, s, l) {
    l /= 100;
    const a = (s * Math.min(l, 1 - l)) / 100;
    const f = (n) => {
        const k = (n + h / 30) % 12;
        const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
        return Math.round(255 * color)
            .toString(16)
            .padStart(2, '0');
    };
    return `#${f(0)}${f(8)}${f(4)}`;
}
/**
 * Defines the "ideal" lightness and saturation adjustment for each shade.
 * Lightness for 500 is set to 50 as a balanced default.
 */
const PALETTE_PROFILE = {
    '0': { l: 98, s_adjust: -50 },
    '50': { l: 95, s_adjust: -40 },
    '100': { l: 90, s_adjust: -30 },
    '200': { l: 80, s_adjust: -20 },
    '300': { l: 70, s_adjust: -10 },
    '400': { l: 60, s_adjust: -5 },
    '500': { l: 50, s_adjust: 0 },
    '600': { l: 45, s_adjust: 5 },
    '700': { l: 35, s_adjust: 10 },
    '800': { l: 25, s_adjust: 15 },
    '900': { l: 15, s_adjust: 20 },
    '950': { l: 8, s_adjust: 25 },
};
// Helper to ensure palette is always in the correct 50-950 order
const ALL_SHADES = [
    '0',
    '50',
    '100',
    '200',
    '300',
    '400',
    '500',
    '600',
    '700',
    '800',
    '900',
    '950',
];
/**
 * Clamps a number between a min and max value.
 */
function clamp(value, min, max) {
    return Math.max(min, Math.min(max, value));
}
/**
 * Generates a Tailwind-like color palette (50-950) from any given base color.
 *
 * @param baseColorHex - The hex string of the base color (e.g., "#0284c7").
 * @param baseColorShade - The shade of the base color (e.g., "500", "700").
 * @returns An object representing the full color palette.
 */
function generateTailwindPalette(baseColorHex, baseColorShade = '500') {
    // 1. Get HSL of the input color
    const [h_base, s_base, l_base] = hexToHsl(baseColorHex);
    // 2. Find the anchors
    const baseProfile = PALETTE_PROFILE[baseColorShade];
    /** The Hue is constant across the entire palette. */
    const h = h_base;
    /**
     * Calculate the 500-shade's saturation by "reversing" the adjustment
     * from the base color.
     * e.g., if base is 700 (s_adjust: +10) and s_base is 80,
     * s_500 will be 70.
     */
    const s_500 = clamp(s_base - baseProfile.s_adjust, 0, 100);
    /**
     * Calculate the lightness "offset". This is the difference between
     * the base color's actual lightness and its "ideal" lightness
     * from the profile.
     * e.g., if base is 700 (ideal L: 35) and l_base is 38,
     * the offset is +3. This offset will be applied to all shades.
     */
    const l_offset = l_base - baseProfile.l;
    // 3. Generate the full palette
    const palette = {};
    for (const shade of ALL_SHADES) {
        const profile = PALETTE_PROFILE[shade];
        // Calculate the new saturation and lightness for this shade
        const newS = clamp(s_500 + profile.s_adjust, 0, 100);
        const newL = clamp(profile.l + l_offset, 0, 100);
        palette[shade] = hslToHex(h, newS, newL);
    }
    // 4. Ensure the originally provided color is used exactly
    // (to avoid rounding errors from HSL conversion)
    palette[baseColorShade] = baseColorHex;
    return palette;
}

const toastStyle = {
    // borderColor: '{surface.300}',
    background: '{content.background}',
    // color: '{surface.600}',
    // closeButton: {
    //   hoverBackground: '{surface.100}',
    //   focusRing: {
    //     color: '{surface.600}',
    //   },
    // },
};
function changePrimaryColor(color) {
    updatePreset({
        semantic: {
            primary: generateTailwindPalette(color || '#2AAEC0'),
        },
    });
}
function changeBackgroundColor(color) {
    if (color) {
        const palette = generateTailwindPalette(color, '100');
        document.documentElement.style.setProperty('--app-background-light', palette['100']);
        document.documentElement.style.setProperty('--app-background-dark', palette['950']);
    }
}
function changeTextColor(color) {
    const palette = generateTailwindPalette(color ?? '#334155', '700');
    updatePreset({
        semantic: {
            colorScheme: {
                light: {
                    text: {
                        color: palette['700'],
                        hoverColor: palette['800'],
                        hoverMutedColor: palette['600'],
                        mutedColor: palette['500'],
                    },
                },
                dark: {
                    text: {
                        color: palette['0'],
                        hoverColor: palette['0'],
                        hoverMutedColor: palette['300'],
                        mutedColor: palette['400'],
                    },
                },
            },
        },
    });
}
const MTPreset = (themeOptions) => {
    const textPalette = generateTailwindPalette(themeOptions?.textColor ?? '#334155', '700');
    const configs = {
        options: {
            prefix: 'mt',
            cssLayer: {
                name: 'primeng',
                order: 'theme, base, primeng',
            },
        },
        semantic: {
            colorScheme: {
                light: {
                    text: {
                        color: textPalette['700'],
                        hoverColor: textPalette['800'],
                        hoverMutedColor: textPalette['600'],
                        mutedColor: textPalette['500'],
                    },
                },
                dark: {
                    text: {
                        color: textPalette['0'],
                        hoverColor: textPalette['0'],
                        hoverMutedColor: textPalette['300'],
                        mutedColor: textPalette['400'],
                    },
                },
            },
            primary: generateTailwindPalette(themeOptions?.primaryColor || '#2AAEC0'),
            content: {
                borderRadius: '{border.radius.lg}',
            },
            formField: {
                borderRadius: '{border.radius.lg}',
            },
        },
        components: {
            dialog: {
                header: {
                    padding: '0',
                },
                content: {
                    padding: '0',
                },
                footer: {
                    padding: '0',
                },
            },
            toast: {
                root: {
                    borderRadius: '{border.radius.xl}',
                },
                colorScheme: {
                    light: {
                        success: toastStyle,
                        info: toastStyle,
                        warn: toastStyle,
                        error: toastStyle,
                        secondary: toastStyle,
                    },
                    dark: {
                        success: toastStyle,
                        info: toastStyle,
                        warn: toastStyle,
                        error: toastStyle,
                        secondary: toastStyle,
                    },
                },
            },
            togglebutton: {
                root: {
                    padding: '1px',
                },
                colorScheme: {
                    light: {
                        root: {
                            checkedColor: '{primary.500}',
                        },
                    },
                    dark: {
                        root: {
                            checkedColor: '{primary.400}',
                        },
                    },
                },
            },
            selectbutton: {
                root: {
                    borderRadius: '{border.radius.lg}',
                },
            },
            paginator: {
                root: {
                    borderRadius: '{border.radius.lg}',
                    padding: '0 .5rem',
                },
                navButton: {
                    borderRadius: '0',
                },
                colorScheme: {
                    light: {
                        navButton: {
                            color: '{surface.600}',
                            hoverBackground: '#fff',
                            selectedBackground: '{surface.200}',
                            selectedColor: '{surface.600}',
                            focusRing: {
                                color: '{surface.200}',
                            },
                        },
                    },
                    dark: {
                        navButton: {
                            color: '{surface.200}',
                            hoverBackground: 'transparent',
                            selectedBackground: 'transparent',
                            selectedColor: '#fff',
                            focusRing: {
                                color: '#fff',
                            },
                        },
                    },
                },
                css: () => `
          .p-paginator {
            --p-paginator-gap: 0;
            --p-paginator-nav-button-width: 2.2rem;
            --p-paginator-nav-button-height: 2.2rem;
            width: fit-content;
            border: 1px solid var(--p-surface-200);
            overflow: hidden;
            font-size: .95rem;
          }

          .p-paginator-pages button,
          .p-paginator-prev,
          .p-paginator-next {
            border-color: var(--p-surface-200);
          }

          .p-paginator-pages button {
            border-style: solid;
            border-width: 0 0 0 1px;
          }

          .p-paginator-pages button:first-child {
            border-left: none;
          }

          .p-paginator-prev {
            border-right: 1px solid var(--p-surface-200);
          }

          .p-paginator-next {
            border-left: 1px solid var(--p-surface-200);
          }

          .p-paginator .p-select {
            border: 0;
            background: transparent;
          }

          .p-paginator .p-select .p-select-label {
            padding: 0;
          }

          /* Dark Mode Styles */
          .dark .p-paginator {
            border-color: var(--p-surface-500);
          }

          .dark .p-paginator-pages button,
          .dark .p-paginator-prev,
          .dark .p-paginator-next {
            border-color: var(--p-surface-500);
          }

          .dark .p-paginator-prev {
            border-right-color: var(--p-surface-500);
          }

          .dark .p-paginator-next {
            border-left-color: var(--p-surface-500);
          }
        `,
            },
            toggleswitch: {
                root: {
                    width: '2.5rem',
                    height: '1.4rem',
                },
            },
            select: {
                optionGroup: {
                    fontWeight: 'bold',
                    padding: '6px 8px',
                    background: '{surface.50}',
                    color: '{primary.700}',
                },
            },
            // steps: {
            //   item: {
            //     link: {
            //       gap: 'calc(50% + 0.25rem)',
            //     },
            //     number: {
            //       font: {
            //         size: '0.9rem',
            //       },
            //       active: {
            //         color: 'white',
            //         background: '{primary.500}',
            //         border: {
            //           color: '{primary.500}',
            //         },
            //       },
            //     },
            //   },
            // },
        },
    };
    return definePreset(Aura, configs);
};
function provideMTComponents(themeOptions) {
    changeBackgroundColor(themeOptions?.backgroundColor);
    return providePrimeNG({
        zIndex: {
            modal: 1900,
            overlay: 1500,
            menu: 1500,
            tooltip: 1600,
        },
        theme: {
            preset: MTPreset(themeOptions),
            options: {
                darkModeSelector: '.dark',
            },
        },
    });
}

function provideMTMessages() {
    return MessageService;
}

function provideMTConfirmation() {
    return ConfirmationService;
}

class ValidatorConfig {
    type;
    value;
    message;
    customValidator;
    constructor(config) {
        this.type = config.type;
        this.value = config.value;
        this.message = config.message;
        this.customValidator = config.customValidator;
    }
    // Factory methods for common validators
    static required(message = 'This field is required') {
        return new ValidatorConfig({ type: 'required', message });
    }
    static email(message = 'Please enter a valid email') {
        return new ValidatorConfig({ type: 'email', message });
    }
    static minLength(length, message) {
        return new ValidatorConfig({
            type: 'minLength',
            value: length,
            message: message || `Minimum length is ${length} characters`,
        });
    }
    static maxLength(length, message) {
        return new ValidatorConfig({
            type: 'maxLength',
            value: length,
            message: message || `Maximum length is ${length} characters`,
        });
    }
    static min(value, message) {
        return new ValidatorConfig({
            type: 'min',
            value,
            message: message || `Minimum value is ${value}`,
        });
    }
    static max(value, message) {
        return new ValidatorConfig({
            type: 'max',
            value,
            message: message || `Maximum value is ${value}`,
        });
    }
    static pattern(pattern, message = 'Invalid format') {
        return new ValidatorConfig({ type: 'pattern', value: pattern, message });
    }
    static custom(validator, message = 'Invalid value') {
        return new ValidatorConfig({
            type: 'custom',
            customValidator: validator,
            message,
        });
    }
}
class BaseFieldConfig {
    key;
    label;
    type;
    required;
    disabled;
    readonly;
    hidden;
    placeholder;
    hint;
    cssClass;
    validators;
    order;
    colSpan;
    defaultValue;
    customTemplate;
    relations;
    formulaCondition;
    constructor(config) {
        this.key = config.key || 'Key';
        this.label = config.label || '';
        this.type = config.type;
        this.required = config.required || false;
        this.disabled = config.disabled || false;
        this.readonly = config.readonly || false;
        this.hidden = config.hidden || false;
        this.placeholder = config.placeholder || '';
        this.hint = config.hint || '';
        this.cssClass = config.cssClass || '';
        this.validators = config.validators || [];
        this.order = config.order || 0;
        this.relations = config.relations || [];
        this.formulaCondition = config.formulaCondition;
        this.colSpan = config.colSpan || 12;
    }
}
// Specific configurations for different field types
class TextFieldConfig extends BaseFieldConfig {
    inputType;
    constructor(config) {
        super({ ...config, type: 'text' });
        this.inputType = config.inputType || 'text';
    }
}
class TextareaFieldConfig extends BaseFieldConfig {
    rows;
    autoResize;
    constructor(config) {
        super({ ...config, type: 'textarea' });
        this.rows = config.rows || 3;
        this.autoResize = config.autoResize || false;
    }
}
class SelectFieldConfig extends BaseFieldConfig {
    options;
    optionLabel;
    optionValue;
    group;
    optionGroupLabel;
    optionGroupChildren;
    multiple;
    filter;
    filterBy;
    filterPlaceholder;
    showClear;
    emptyMessage;
    size;
    hasPlaceholderPrefix;
    constructor(config) {
        super({ ...config, type: 'select' });
        this.options = config.options;
        this.optionLabel = config.optionLabel || 'label';
        this.optionValue = config.optionValue || 'value';
        this.group = config.group || false;
        this.optionGroupLabel = config.optionGroupLabel || 'label';
        this.optionGroupChildren = config.optionGroupChildren || 'items';
        this.multiple = config.multiple || false;
        this.filter = config.filter || false;
        this.filterBy = config.filterBy || '';
        this.filterPlaceholder = config.filterPlaceholder || 'Search...';
        this.showClear = config.showClear || false;
        this.emptyMessage = config.emptyMessage || 'No options available';
        this.size = config.size || '';
        this.hasPlaceholderPrefix =
            config.hasPlaceholderPrefix === undefined
                ? true
                : config.hasPlaceholderPrefix;
    }
}
class RadioButtonFieldConfig extends BaseFieldConfig {
    options;
    optionLabel;
    optionValue;
    orientation;
    constructor(config) {
        super({ ...config, type: 'radio-button' });
        this.options = config.options;
        this.optionLabel = config.optionLabel || 'label';
        this.optionValue = config.optionValue || 'value';
        this.orientation = config.orientation || 'vertical';
    }
}
class RadioCardsFieldConfig extends BaseFieldConfig {
    options;
    optionLabel;
    optionValue;
    size;
    constructor(config) {
        super({ ...config, type: 'radio-cards' });
        this.options = config.options;
        this.optionLabel = config.optionLabel || 'label';
        this.optionValue = config.optionValue || 'value';
        this.size = config.size || 'small';
    }
}
class UserSearchFieldConfig extends BaseFieldConfig {
    optionLabel;
    optionValue;
    size;
    apiUrl;
    context;
    constructor(config) {
        super({ ...config, type: 'user-search' });
        this.optionLabel = config.optionLabel || 'label';
        this.optionValue = config.optionValue || 'value';
        this.size = config.size || '';
        this.hint = config.hint || '';
        this.context = config.context || undefined;
        this.apiUrl = config.apiUrl || '';
        this.placeholder = config.placeholder || 'Search';
    }
}
class UploadFileFieldConfig extends BaseFieldConfig {
    size;
    endPoint;
    userImgClass;
    shape;
    multiple;
    accept;
    fileSizeLimit;
    context;
    title;
    description;
    constructor(config) {
        super({ ...config, type: 'upload-file' });
        this.size = config.size || 'normal';
        this.endPoint = config.endPoint || 'uploader';
        this.userImgClass =
            config.userImgClass || 'w-25! h-25! text-4xl! text-gray-400!';
        this.shape = config.shape || 'field';
        this.multiple = config.multiple ?? false;
        this.accept = config.accept || '.pdf,.doc,.docx,.xlsx,image/*';
        this.fileSizeLimit = config.fileSizeLimit || undefined;
        this.context = config.context || undefined;
        this.title = config.title || 'Upload File';
        this.description = config.description || 'Click or drop a file to upload';
    }
}
class DateFieldConfig extends BaseFieldConfig {
    dateFormat;
    showTime;
    showSeconds;
    hourFormat;
    minDate;
    maxDate;
    disabledDates;
    disabledDays;
    yearRange;
    showIcon;
    icon;
    showButtonBar;
    showClear;
    inline;
    constructor(config) {
        super({ ...config, type: 'date' });
        this.dateFormat = config.dateFormat || 'yyyy-mm-dd';
        this.showTime = config.showTime || false;
        this.showSeconds = config.showSeconds || false;
        this.hourFormat = config.hourFormat || '24';
        this.minDate = config.minDate;
        this.maxDate = config.maxDate;
        this.disabledDates = config.disabledDates || [];
        this.disabledDays = config.disabledDays || [];
        this.yearRange = config.yearRange || '1900:2030';
        this.showIcon = config.showIcon || true;
        this.icon = config.icon || 'pi pi-calendar';
        this.showButtonBar = config.showButtonBar || false;
        this.showClear = config.showClear || true;
        this.inline = config.inline || false;
    }
}
class NumberFieldConfig extends BaseFieldConfig {
    min;
    max;
    step;
    prefix;
    suffix;
    currency;
    locale;
    minFractionDigits;
    maxFractionDigits;
    useGrouping;
    showButtons;
    buttonLayout;
    incrementButtonClass;
    decrementButtonClass;
    incrementButtonIcon;
    decrementButtonIcon;
    constructor(config) {
        super({ ...config, type: 'number' });
        this.min = config.min;
        this.max = config.max;
        this.step = config.step || 1;
        this.prefix = config.prefix;
        this.suffix = config.suffix;
        this.currency = config.currency;
        this.locale = config.locale;
        this.minFractionDigits = config.minFractionDigits;
        this.maxFractionDigits = config.maxFractionDigits;
        this.useGrouping = config.useGrouping || false;
        this.showButtons = config.showButtons || false;
        this.buttonLayout = config.buttonLayout || 'stacked';
        this.incrementButtonClass = config.incrementButtonClass;
        this.decrementButtonClass = config.decrementButtonClass;
        this.incrementButtonIcon = config.incrementButtonIcon;
        this.decrementButtonIcon = config.decrementButtonIcon;
    }
}
class SliderFieldConfig extends BaseFieldConfig {
    min;
    max;
    step;
    orientation;
    range;
    animate;
    unit;
    constructor(config) {
        super({ ...config, type: 'slider' });
        this.min = config.min || 0;
        this.max = config.max || 100;
        this.step = config.step || 1;
        this.orientation = config.orientation || 'horizontal';
        this.range = config.range || false;
        this.animate = config.animate || false;
        this.unit = config.unit || '';
    }
}
class MultiSelectFieldConfig extends BaseFieldConfig {
    options;
    optionLabel;
    optionValue;
    filter;
    filterBy;
    filterPlaceholder;
    showClear;
    emptyMessage;
    display;
    maxSelectedLabels;
    selectedItemsLabel;
    showToggleAll;
    resetFilterOnHide;
    constructor(config) {
        super({ ...config, type: 'multi-select' });
        this.options = config.options;
        this.optionLabel = config.optionLabel || 'label';
        this.optionValue = config.optionValue || 'value';
        this.filter = config.filter || false;
        this.filterBy = config.filterBy || '';
        this.filterPlaceholder = config.filterPlaceholder || 'Search...';
        this.showClear = config.showClear || false;
        this.emptyMessage = config.emptyMessage || 'No options available';
        this.display = config.display || 'comma';
        this.maxSelectedLabels = config.maxSelectedLabels || 3;
        this.selectedItemsLabel = config.selectedItemsLabel || '{0} items selected';
        this.showToggleAll = config.showToggleAll || true;
        this.resetFilterOnHide = config.resetFilterOnHide || false;
    }
}
class PickListFieldConfig extends BaseFieldConfig {
    options;
    optionLabel;
    optionValue;
    sourceHeader;
    targetHeader;
    showSourceControls;
    showTargetControls;
    showSourceFilter;
    showTargetFilter;
    filterBy;
    dataKey;
    dragdrop;
    responsive;
    breakpoint;
    constructor(config) {
        super({ ...config, type: 'pick-list' });
        this.options = config.options;
        this.optionLabel = config.optionLabel || 'label';
        this.optionValue = config.optionValue || 'value';
        this.sourceHeader = config.sourceHeader || 'Available';
        this.targetHeader = config.targetHeader || 'Selected';
        this.showSourceControls =
            config.showSourceControls !== undefined
                ? config.showSourceControls
                : true;
        this.showTargetControls =
            config.showTargetControls !== undefined
                ? config.showTargetControls
                : true;
        this.showSourceFilter =
            config.showSourceFilter !== undefined ? config.showSourceFilter : false;
        this.showTargetFilter =
            config.showTargetFilter !== undefined ? config.showTargetFilter : false;
        this.filterBy = config.filterBy || this.optionLabel;
        this.dataKey = config.dataKey || this.optionValue;
        this.dragdrop = config.dragdrop ?? false;
        this.responsive = config.responsive ?? true;
    }
}
class CheckboxFieldConfig extends BaseFieldConfig {
    binary;
    trueValue;
    falseValue;
    checkboxIcon;
    constructor(config) {
        super({ ...config, type: 'checkbox' });
        this.binary = config.binary !== false; // Default to true
        this.trueValue = config.trueValue !== undefined ? config.trueValue : true;
        this.falseValue =
            config.falseValue !== undefined ? config.falseValue : false;
        this.checkboxIcon = config.checkboxIcon;
    }
}
class ToggleFieldConfig extends BaseFieldConfig {
    toggleShape;
    icon;
    descriptionCard;
    constructor(config) {
        super({ ...config, type: 'toggle' });
        this.toggleShape = config.toggleShape ?? 'toggle';
        this.icon = config.icon;
        this.descriptionCard = config.descriptionCard;
    }
}
class EditorFieldConfig extends BaseFieldConfig {
    constructor(config) {
        super({ ...config, type: 'editor-field' });
    }
}
class ColorPickerFieldConfig extends BaseFieldConfig {
    format;
    inline;
    appendTo;
    variant;
    constructor(config) {
        super({ ...config, type: 'color-picker' });
        this.format = config.format || 'hex';
        this.inline = config.inline || false;
        this.appendTo = config.appendTo || 'body';
        this.variant = config.variant || 'outlined';
    }
}
class IconFieldConfig extends BaseFieldConfig {
    constructor(config) {
        super({ ...config, type: 'icon-field' });
    }
}
class SpacerFieldConfig extends BaseFieldConfig {
    constructor(config) {
        super({
            ...config,
            type: 'spacer',
            key: config.key || `spacer_${Date.now()}`,
            label: config.label || '',
        });
    }
}
class SchemaConnectionFieldConfig extends BaseFieldConfig {
    configuration;
    context;
    filter;
    constructor(config) {
        super({ ...config, type: 'schema-connection' });
        this.configuration = config.configuration;
        this.context = config.context || undefined;
        this.filter = config.filter ?? true;
    }
}
class SchedulePredecessorFieldConfig extends BaseFieldConfig {
    configuration;
    runtimeContext;
    constructor(config) {
        super({ ...config, type: 'predecessor' });
        this.configuration = config.configuration ?? null;
        this.runtimeContext = config.runtimeContext ?? null;
    }
}
function createCustomValidator(customValidator, message) {
    return (control) => {
        const result = customValidator(control.value);
        if (result instanceof Promise) {
            // Handle async validation
            result.then((isValid) => {
                if (!isValid) {
                    control.setErrors({
                        custom: { message: message || 'Invalid value' },
                    });
                }
            });
            return null; // For async, return null initially
        }
        else {
            // Handle sync validation
            return result
                ? null
                : { custom: { message: message || 'Invalid value' } };
        }
    };
}
function wrapValidatorWithMessage(validator, errorKey, message) {
    return (control) => {
        const result = validator(control);
        if (result) {
            // Replace the default error with custom message
            return { [errorKey]: { ...result[Object.keys(result)[0]], message } };
        }
        return null;
    };
}
// DynamicFieldConfig = input.required<any>({
//   transform: (value: any) => this.transformToDateFieldConfig(value)
// });
// transformToDateFieldConfig(value: any){
// return  new TextFieldConfig()
// }

/**
 * @deprecated Use direct `select()` + `computed()` pattern instead for better performance.
 *
 * **Problem with BaseFacade:**
 * - All computed signals depend on the full state, causing unnecessary recomputations
 * - Bypasses NGXS selector memoization
 * - Uses `as any` casting, losing type safety
 *
 * **New Pattern - Use slice-based selectors:**
 *
 * ```typescript
 * // In your State class - add slice selectors:
 * @Selector()
 * static getLoadingActive(state: MyStateModel): string[] {
 *   return state.loadingActive;
 * }
 *
 * @Selector()
 * static getErrors(state: MyStateModel): Record<string, string | null> {
 *   return state.errors;
 * }
 *
 * // In your Facade - don't extend BaseFacade:
 * @Injectable({ providedIn: 'root' })
 * export class MyFacade {
 *   private readonly store = inject(Store);
 *
 *   // Data selectors - memoized by NGXS
 *   readonly items = select(MyState.getItems);
 *
 *   // Loading/Error slices - memoized by NGXS
 *   private readonly loadingActive = select(MyState.getLoadingActive);
 *   private readonly errors = select(MyState.getErrors);
 *
 *   // Loading signals - computed from slice (minimal reactivity)
 *   readonly isLoadingItems = computed(() =>
 *     this.loadingActive().includes(MyActionKey.GetItems)
 *   );
 *
 *   // Error signals - computed from slice (minimal reactivity)
 *   readonly itemsError = computed(() =>
 *     this.errors()[MyActionKey.GetItems] ?? null
 *   );
 *
 *   // Dispatchers
 *   loadItems() {
 *     return this.store.dispatch(new GetItems());
 *   }
 * }
 * ```
 *
 * **Template Usage:**
 * ```html
 * @if (facade.isLoadingItems()) {
 *   <loading-spinner />
 * } @else if (facade.itemsError(); as error) {
 *   <error-alert>{{ error }}</error-alert>
 * } @else {
 *   @for (item of facade.items(); track item.id) {
 *     <item-card [item]="item" />
 *   }
 * }
 * ```
 */
class BaseFacade {
    /**
     * Creates a query object with separate signals for data, loading, and error states
     * Each property is its own signal for granular reactivity
     *
     * @example
     * readonly modulesQuery = this.query(
     *   'getModulesLoading',
     *   (state) => state.allModules
     * );
     *
     * // In template - each is a separate signal:
     * @if (modulesQuery.isPending()) { Loading... }
     * @if (modulesQuery.error(); as error) { Error: {{ error }} }
     * @if (modulesQuery.data(); as modules) {
     *   <div *ngFor="let m of modules">{{ m.title }}</div>
     * }
     */
    query(loadingKey, dataSelector) {
        const stateSignal = this.state();
        const isPending = computed(() => {
            const currentState = stateSignal();
            return currentState.loadingActive?.includes(loadingKey) ?? false;
        }, ...(ngDevMode ? [{ debugName: "isPending" }] : []));
        const error = computed(() => {
            const currentState = stateSignal();
            return currentState.errors?.[loadingKey] ?? null;
        }, ...(ngDevMode ? [{ debugName: "error" }] : []));
        const data = computed(() => {
            const currentState = stateSignal();
            const hasError = currentState.errors?.[loadingKey];
            return hasError ? null : dataSelector(currentState);
        }, ...(ngDevMode ? [{ debugName: "data" }] : []));
        const isSuccess = computed(() => !isPending() && !error() && data() !== null, ...(ngDevMode ? [{ debugName: "isSuccess" }] : []));
        const isError = computed(() => !!error(), ...(ngDevMode ? [{ debugName: "isError" }] : []));
        return {
            data,
            isPending,
            isSuccess,
            isError,
            error,
        };
    }
    /**
     * Creates a loading signal for a specific operation
     */
    loading(loadingKey) {
        const stateSignal = this.state();
        return computed(() => {
            const currentState = stateSignal();
            return currentState.loadingActive?.includes(loadingKey) ?? false;
        });
    }
    /**
     * Creates an error signal for a specific operation
     */
    errorSignal(loadingKey) {
        const stateSignal = this.state();
        return computed(() => {
            const currentState = stateSignal();
            return currentState.errors?.[loadingKey] ?? null;
        });
    }
}

function createEntityAdapter() {
    return {
        addOne: (state, entity) => [...state, entity],
        upsertOne: (state, entity, uniqueKey = 'id') => {
            const key = entity[uniqueKey];
            if (key == null)
                return state;
            const index = state.findIndex((e) => e[uniqueKey] === key);
            if (index === -1) {
                return [...state, entity];
            }
            const clone = [...state];
            clone[index] = entity;
            return clone;
        },
        updateOne: (state, id, changes, uniqueKey = 'id') => state.map((e) => (e[uniqueKey] === id ? { ...e, ...changes } : e)),
        removeOne: (state, id, uniqueKey = 'id') => state.filter((e) => e[uniqueKey] !== id),
        setAll: (_state, entities) => [...entities],
    };
}

function startLoading(ctx, loadingName) {
    const { loadingActive, errors } = ctx.getState();
    if (!loadingActive.includes(loadingName)) {
        ctx.patchState({
            loadingActive: [...loadingActive, loadingName],
        });
    }
    if (errors && errors[loadingName]) {
        const { [loadingName]: _removed, ...rest } = errors;
        ctx.patchState({ errors: rest });
    }
}
function endLoading(ctx, loadingName) {
    const { loadingActive } = ctx.getState();
    ctx.patchState({
        loadingActive: loadingActive.filter((name) => name !== loadingName),
    });
}
function setLoadingError(ctx, loadingName, message) {
    const { errors } = ctx.getState();
    ctx.patchState({
        errors: { ...errors, [loadingName]: message },
    });
}

function handleApiRequest(config) {
    const { ctx, key, request$, onSuccess, onError, errorMessage } = config;
    startLoading(ctx, key);
    return request$.pipe(tap((response) => {
        const state = ctx.getState();
        const patch = onSuccess(response, state);
        if (patch) {
            ctx.patchState(patch);
        }
    }), catchError((error) => {
        const state = ctx.getState();
        const message = error?.error?.message ?? error?.message ?? errorMessage;
        setLoadingError(ctx, key, message);
        // Call onError callback if provided
        if (onError) {
            const patch = onError(error, state);
            if (patch) {
                ctx.patchState(patch);
            }
        }
        throw error;
    }), finalize(() => endLoading(ctx, key)));
}

/**
 * T      = entity type (Module, User, ...)
 * TState = full state model (must include LoadingStateShape)
 * TLoad  = union type of loading keys for this feature
 */
class CrudStateBase {
    adapter = createEntityAdapter();
    /**
     * Helper to create default updateState function
     */
    getDefaultUpdateState(stateProperty) {
        // Try to extract the property name from the stateProperty function
        const funcStr = stateProperty.toString();
        const match = funcStr.match(/\.([a-zA-Z_$][a-zA-Z0-9_$]*)/); // Match .propertyName
        const key = match ? match[1] : 'items';
        return (state, items) => ({ [key]: items });
    }
    /**
     * @deprecated Use `handleApiRequest` directly with `onSuccess` callback.
     * The `load()` method provides no significant benefit over `handleApiRequest`.
     *
     * @example
     * // Instead of this.load(), use:
     * return handleApiRequest({
     *   ctx,
     *   key: ActionKey.GetItems,
     *   request$: req$,
     *   onSuccess: (response) => ({
     *     items: response.data ?? [],
     *   }),
     * });
     */
    load(ctx, config) {
        return handleApiRequest({
            ctx,
            key: config.key,
            request$: config.request$,
            onSuccess: (response, state) => {
                const data = config.transform
                    ? config.transform(response.data)
                    : response.data;
                // If stateProperty provided, use adapter for array operations
                if (config.stateProperty) {
                    const items = this.adapter.setAll(config.stateProperty(state), data);
                    const updateState = config.updateState ??
                        this.getDefaultUpdateState(config.stateProperty);
                    return updateState(state, items);
                }
                // Otherwise, use custom updateState directly
                if (config.updateState) {
                    return config.updateState(state, data);
                }
                throw new Error('Either stateProperty or updateState must be provided');
            },
            onError: config.onError,
            errorMessage: config.errorMessage ?? 'Failed to load data',
        });
    }
    /**
     * Create one entity with config object
     */
    create(ctx, config) {
        const updateState = config.updateState ?? this.getDefaultUpdateState(config.stateProperty);
        return handleApiRequest({
            ctx,
            key: config.key,
            request$: config.request$,
            onSuccess: (response, state) => {
                const entity = config.transform
                    ? config.transform(response.data)
                    : response.data;
                const items = this.adapter.addOne(config.stateProperty(state), entity);
                return updateState(state, items);
            },
            onError: config.onError,
            errorMessage: config.errorMessage ?? 'Failed to create item',
        });
    }
    /**
     * Update one entity with config object
     */
    update(ctx, config) {
        const updateState = config.updateState ?? this.getDefaultUpdateState(config.stateProperty);
        return handleApiRequest({
            ctx,
            key: config.key,
            request$: config.request$,
            onSuccess: (response, state) => {
                const entity = config.transform
                    ? config.transform(response.data)
                    : response.data;
                const items = this.adapter.upsertOne(config.stateProperty(state), entity, config.uniqueKey);
                return updateState(state, items);
            },
            onError: config.onError,
            errorMessage: config.errorMessage ?? 'Failed to update item',
        });
    }
    /**
     * Delete one entity with config object
     */
    delete(ctx, config) {
        const updateState = config.updateState ?? this.getDefaultUpdateState(config.stateProperty);
        return handleApiRequest({
            ctx,
            key: config.key,
            request$: config.request$,
            onSuccess: (_response, state) => {
                const items = this.adapter.removeOne(config.stateProperty(state), config.id, config.uniqueKey);
                return updateState(state, items);
            },
            onError: config.onError,
            errorMessage: config.errorMessage ?? 'Failed to delete item',
        });
    }
}

function isInvalid(control) {
    if (!control)
        return false;
    return control && control?.invalid && control?.touched;
}

/**
 * Shared HTTP context token for request configuration.
 * Used by interceptors to determine base URL routing.
 *
 * @example
 * ```typescript
 * import { REQUEST_CONTEXT } from '@masterteam/components';
 *
 * // In a service
 * private context = new HttpContext().set(REQUEST_CONTEXT, { useBaseUrl: true });
 *
 * getMetadata(): Observable<any> {
 *   return this.http.get('metadata', { context: this.context });
 * }
 *
 * // In interceptor
 * const config = req.context.get(REQUEST_CONTEXT);
 * if (config?.useBaseUrl) {
 *   // Use base URL
 * }
 * ```
 */
const REQUEST_CONTEXT = new HttpContextToken(() => ({ useBaseUrl: false }));

/**
 * Color utility functions for brand display.
 */
/**
 * Converts a hex color to a light shade (equivalent to primary-100 in Tailwind).
 * This creates a color with ~10% opacity or a very light tint by mixing with white.
 */
function getLightColor(hexColor) {
    const hex = hexColor.replace('#', '');
    const r = parseInt(hex.substring(0, 2), 16);
    const g = parseInt(hex.substring(2, 4), 16);
    const b = parseInt(hex.substring(4, 6), 16);
    const lightR = Math.round(r * 0.1 + 255 * 0.9);
    const lightG = Math.round(g * 0.1 + 255 * 0.9);
    const lightB = Math.round(b * 0.1 + 255 * 0.9);
    return `#${lightR.toString(16).padStart(2, '0')}${lightG
        .toString(16)
        .padStart(2, '0')}${lightB.toString(16).padStart(2, '0')}`;
}
function getContrastColor(bgColor) {
    let r = 0, g = 0, b = 0;
    if (bgColor.startsWith('#')) {
        const hex = bgColor.slice(1);
        if (hex.length === 3) {
            r = parseInt(hex[0] + hex[0], 16);
            g = parseInt(hex[1] + hex[1], 16);
            b = parseInt(hex[2] + hex[2], 16);
        }
        else if (hex.length === 6) {
            r = parseInt(hex.slice(0, 2), 16);
            g = parseInt(hex.slice(2, 4), 16);
            b = parseInt(hex.slice(4, 6), 16);
        }
    }
    const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
    return luminance > 0.5 ? '' : '#ffffff';
}

/*
 * Public API Surface of components
 */
// Config

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

export { BaseFacade, BaseFieldConfig, CheckboxFieldConfig, ColorPickerFieldConfig, CrudStateBase, DateFieldConfig, EditorFieldConfig, IconFieldConfig, MultiSelectFieldConfig, NumberFieldConfig, PickListFieldConfig, REQUEST_CONTEXT, RadioButtonFieldConfig, RadioCardsFieldConfig, SchedulePredecessorFieldConfig, SchemaConnectionFieldConfig, SelectFieldConfig, SliderFieldConfig, SpacerFieldConfig, TextFieldConfig, TextareaFieldConfig, ToggleFieldConfig, UploadFileFieldConfig, UserSearchFieldConfig, ValidatorConfig, changeBackgroundColor, changePrimaryColor, changeTextColor, createCustomValidator, createEntityAdapter, endLoading, generateTailwindPalette, getContrastColor, getLightColor, handleApiRequest, isInvalid, provideMTComponents, provideMTConfirmation, provideMTMessages, setLoadingError, startLoading, wrapValidatorWithMessage };
//# sourceMappingURL=masterteam-components.mjs.map
