import * as i0 from '@angular/core';
import { Pipe, inject, LOCALE_ID, Renderer2, ElementRef, viewChild, signal, input, booleanAttribute, model, computed, output, effect, untracked, forwardRef, ChangeDetectionStrategy, Component } from '@angular/core';
import { Select, Option, SelectHeader, SelectBody, Optgroup } from '@ngstarter-ui/components/select';
import * as i1 from '@angular/forms';
import { NgControl, FormsModule } from '@angular/forms';
import { FORM_FIELD, FormFieldControl } from '@ngstarter-ui/components/form-field';
import { Subject } from 'rxjs';
import { FocusMonitor } from '@angular/cdk/a11y';
import { Icon } from '@ngstarter-ui/components/icon';
import { Button } from '@ngstarter-ui/components/button';

class TimezoneUtils {
    static _groupTimezoneIds(timezones) {
        const grouped = {};
        for (const tz of timezones) {
            const parts = tz.split('/');
            const groupName = parts[0];
            if (!grouped[groupName]) {
                grouped[groupName] = [];
            }
            grouped[groupName].push(tz);
        }
        for (const group in grouped) {
            grouped[group].sort();
        }
        return grouped;
    }
    static _groupLocalizedTimezonesToArray(timezones) {
        const intermediateGrouped = {};
        for (const tz of timezones) {
            const parts = tz.id.split('/');
            const groupNameKey = parts[0]; // Use a different variable name to avoid confusion with the 'name' property of TimezoneGroup
            if (!intermediateGrouped[groupNameKey]) {
                intermediateGrouped[groupNameKey] = [];
            }
            intermediateGrouped[groupNameKey].push(tz);
        }
        const result = [];
        const sortedGroupKeys = Object.keys(intermediateGrouped).sort();
        for (const groupKey of sortedGroupKeys) {
            const timezonesInGroup = intermediateGrouped[groupKey];
            timezonesInGroup.sort((a, b) => a.name.localeCompare(b.name));
            result.push({
                name: groupKey, // Changed from groupName
                timezones: timezonesInGroup,
            });
        }
        return result;
    }
    static getAll(groupByName) {
        let timezonesList;
        if (typeof Intl.supportedValuesOf === 'function') {
            try {
                timezonesList = Intl.supportedValuesOf('timeZone');
            }
            catch (e) {
                timezonesList = [];
            }
        }
        else {
            timezonesList = [];
        }
        if (groupByName) {
            return TimezoneUtils._groupTimezoneIds(timezonesList);
        }
        else {
            return [...timezonesList];
        }
    }
    static _getSingleLocalizedName(id, locale, style, // Kept generic styles in param for flexibility if main name needs it
    currentDate) {
        try {
            const formatter = new Intl.DateTimeFormat(locale, {
                timeZone: id,
                timeZoneName: style,
            });
            const parts = formatter.formatToParts(currentDate);
            const tzNamePart = parts.find(part => part.type === 'timeZoneName');
            return tzNamePart?.value;
        }
        catch (e) {
            return undefined;
        }
    }
    static getLocalizedAll(locale, groupByName, timeZoneNameStyle = 'long') {
        const ianaTimezoneIds = TimezoneUtils.getAll(false);
        const localizedTimezones = [];
        const currentDate = new Date();
        for (const id of ianaTimezoneIds) {
            const mainName = TimezoneUtils._getSingleLocalizedName(id, locale, timeZoneNameStyle, currentDate);
            const tzObject = {
                id: id,
                name: mainName || id,
                shortName: TimezoneUtils._getSingleLocalizedName(id, locale, 'short', currentDate),
                offsetName: TimezoneUtils._getSingleLocalizedName(id, locale, 'longOffset', currentDate),
            };
            localizedTimezones.push(tzObject);
        }
        if (groupByName) {
            return TimezoneUtils._groupLocalizedTimezonesToArray(localizedTimezones);
        }
        else {
            localizedTimezones.sort((a, b) => a.name.localeCompare(b.name));
            return localizedTimezones;
        }
    }
    static ianaGroupToRegionCodeMap = {
        'America': '019', // Americas (UN M49)
        'Europe': '150', // Europe (UN M49)
        'Asia': '142', // Asia (UN M49)
        'Africa': '002', // Africa (UN M49)
        'Australia': 'AU', // Australia (ISO 3166-1 alpha-2) - для группы IANA 'Australia'
        'Pacific': '009', // Oceania (UN M49) - для группы IANA 'Pacific'
        'Antarctica': 'AQ', // Antarctica (ISO 3166-1 alpha-2)
    };
    static _getLocalizedRegionDisplayName(ianaGroupName, locale) {
        const regionCode = TimezoneUtils.ianaGroupToRegionCodeMap[ianaGroupName];
        if (regionCode && typeof Intl.DisplayNames === 'function') {
            try {
                const displayNameService = new Intl.DisplayNames(locale, { type: 'region' });
                return displayNameService.of(regionCode) || ianaGroupName; // Fallback if .of() returns undefined/empty
            }
            catch (e) {
                // Error (e.g., old environment without support for this region code)
                return ianaGroupName; // Fallback
            }
        }
        return ianaGroupName; // Fallback if no mapping or Intl.DisplayNames not supported
    }
}

class FilterTimezonesPipe {
    transform(value, searchTerm) {
        if (!value) {
            return value;
        }
        if (!searchTerm || searchTerm.trim() === '') {
            return value;
        }
        const lowerSearchTerm = searchTerm.trim().toLowerCase();
        const timezoneMatches = (timezone, term) => {
            return (timezone.id.toLowerCase().includes(term) ||
                !!(timezone.shortName && timezone.shortName.toLowerCase().includes(term)));
        };
        const resultGroups = [];
        for (const group of value) { // Iterate directly over TimezoneGroup[]
            const groupNameMatches = group.name.toLowerCase().includes(lowerSearchTerm);
            const filteredTimezonesInGroup = group.timezones.filter(tz => timezoneMatches(tz, lowerSearchTerm));
            // A group is included if its name matches OR it has matching timezones after filtering
            if (groupNameMatches || filteredTimezonesInGroup.length > 0) {
                resultGroups.push({
                    ...group,
                    timezones: filteredTimezonesInGroup // Always use the filtered list of timezones for the group
                });
            }
        }
        return resultGroups;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FilterTimezonesPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
    static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.4", ngImport: i0, type: FilterTimezonesPipe, isStandalone: true, name: "filterTimezones" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FilterTimezonesPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'filterTimezones',
                }]
        }] });

let nextId = 0;
class TimezoneSelect {
    localeId = inject(LOCALE_ID);
    renderer = inject(Renderer2);
    _focusMonitor = inject(FocusMonitor);
    _elementRef = inject(ElementRef);
    _formField = inject(FORM_FIELD, { optional: true });
    ngControl = inject(NgControl, { optional: true, self: true });
    searchInput = viewChild.required('searchInput');
    selectRef = viewChild.required('select');
    timezoneGroups = signal([], ...(ngDevMode ? [{ debugName: "timezoneGroups" }] : /* istanbul ignore next */ []));
    touched = signal(false, ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
    stateChanges = new Subject();
    controlType = 'ngs-timezone-select';
    id = `ngs-timezone-select-${nextId++}`;
    _userAriaDescribedBy = input('', { ...(ngDevMode ? { debugName: "_userAriaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
    locale = input(this.localeId, ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
    _placeholder = input('', { ...(ngDevMode ? { debugName: "_placeholder" } : /* istanbul ignore next */ {}), alias: 'placeholder' });
    _required = input(false, { ...(ngDevMode ? { debugName: "_required" } : /* istanbul ignore next */ {}), alias: 'required',
        transform: booleanAttribute });
    _disabledByInput = input(false, { ...(ngDevMode ? { debugName: "_disabledByInput" } : /* istanbul ignore next */ {}), alias: 'disabled',
        transform: booleanAttribute });
    _value = model(null, { ...(ngDevMode ? { debugName: "_value" } : /* istanbul ignore next */ {}), alias: 'value' });
    searchTerm = model('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : /* istanbul ignore next */ []));
    _focused = signal(false, ...(ngDevMode ? [{ debugName: "_focused" }] : /* istanbul ignore next */ []));
    _disabledByCva = signal(false, ...(ngDevMode ? [{ debugName: "_disabledByCva" }] : /* istanbul ignore next */ []));
    _disabled = computed(() => this._disabledByInput() || this._disabledByCva(), ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
    opened = output();
    closed = output();
    onChange = (_) => { };
    onTouched = () => { };
    get focused() {
        return this._focused() || (this.selectRef?.()?.panelOpen() ?? false);
    }
    get empty() {
        return !this._value() || this._value()?.trim() === '';
    }
    get shouldLabelFloat() {
        return this.focused || !this.empty;
    }
    get userAriaDescribedBy() {
        return this._userAriaDescribedBy();
    }
    get placeholder() {
        return this._placeholder();
    }
    get required() {
        return this._required();
    }
    get disabled() {
        return this._disabled();
    }
    get value() {
        return this._value();
    }
    get errorState() {
        // @ts-ignore
        return this.ngControl?.invalid && this.touched();
    }
    constructor() {
        if (this.ngControl != null) {
            this.ngControl.valueAccessor = this;
        }
        effect(() => {
            const value = this._value();
            untracked(() => {
                this.onChange(value);
                this.stateChanges.next();
            });
        });
        effect(() => {
            // Read signals to trigger effect.
            this._placeholder();
            this._required();
            this._disabled();
            this._focused();
            this._value();
            this.selectRef()?.panelOpen();
            // Propagate state changes.
            untracked(() => this.stateChanges.next());
        });
    }
    ngOnInit() {
        const locale = this.locale() || this.localeId;
        this.timezoneGroups.set(TimezoneUtils.getLocalizedAll(locale, true));
        if (this._formField) {
            this.renderer.addClass(this._formField.elementRef.nativeElement, 'ngs-form-field-type-timezone-select');
        }
    }
    ngOnDestroy() {
        this.stateChanges.complete();
        this._focusMonitor.stopMonitoring(this._elementRef);
    }
    onFocusIn() {
        if (!this._focused()) {
            this._focused.set(true);
            this.stateChanges.next();
        }
    }
    onFocusOut(event) {
        if (!this._elementRef.nativeElement.contains(event.relatedTarget)) {
            this.touched.set(true);
            this._focused.set(false);
            this.onTouched();
            this.stateChanges.next();
        }
    }
    onContainerClick(event) {
        if (this.disabled) {
            return;
        }
        this.focus();
    }
    setDescribedByIds(ids) {
        const controlElement = this._elementRef.nativeElement.querySelector('.ngs-select-trigger');
        if (controlElement) {
            controlElement.setAttribute('aria-describedby', ids.join(' '));
        }
    }
    writeValue(timezone) {
        this._updateValue(timezone);
    }
    registerOnChange(fn) {
        this.onChange = fn;
    }
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    setDisabledState(isDisabled) {
        this._disabledByCva.set(isDisabled);
    }
    onSelectOpened() {
        setTimeout(() => {
            this.searchInput().nativeElement.focus();
        });
        this.opened.emit();
    }
    clearSearch(event) {
        event.stopPropagation();
        this.searchTerm.set('');
        this.searchInput().nativeElement.focus();
    }
    onSelectClosed() {
        this._focused.set(false);
        this.searchTerm.set('');
        this.closed.emit();
        this.stateChanges.next();
    }
    _updateValue(value) {
        const current = this._value();
        if (current === value) {
            return;
        }
        this._value.set(value);
    }
    focus() {
        this._elementRef.nativeElement.focus();
        setTimeout(() => {
            this.selectRef().open();
        });
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: TimezoneSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: TimezoneSelect, isStandalone: true, selector: "ngs-timezone-select", inputs: { _userAriaDescribedBy: { classPropertyName: "_userAriaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, _placeholder: { classPropertyName: "_placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, _required: { classPropertyName: "_required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, _disabledByInput: { classPropertyName: "_disabledByInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, _value: { classPropertyName: "_value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, searchTerm: { classPropertyName: "searchTerm", publicName: "searchTerm", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { _value: "valueChange", searchTerm: "searchTermChange", opened: "opened", closed: "closed" }, host: { listeners: { "focus": "onFocusIn()", "blur": "onFocusOut($event)" }, properties: { "id": "id", "attr.tabindex": "disabled ? -1 : 0" }, classAttribute: "ngs-timezone-select" }, providers: [
            {
                provide: FormFieldControl,
                useExisting: forwardRef(() => TimezoneSelect),
                multi: true,
            },
        ], viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "selectRef", first: true, predicate: ["select"], descendants: true, isSignal: true }], exportAs: ["ngsTimezoneSelect"], ngImport: i0, template: "<ngs-select #select=\"ngsSelect\"\n            [(value)]=\"_value\"\n            [aria-describedby]=\"_userAriaDescribedBy()\"\n            [disabled]=\"disabled\"\n            [placeholder]=\"_placeholder()\"\n            (opened)=\"onSelectOpened()\"\n            (closed)=\"onSelectClosed()\">\n  <ngs-select-header>\n    <div class=\"sticky top-0 z-1 bg-surface-container-lowest\">\n      <input #searchInput\n             type=\"text\"\n             placeholder=\"Search...\"\n             autocomplete=\"off\"\n             [(ngModel)]=\"searchTerm\"\n             class=\"w-full text-sm focus:outline-none border-b border-surface-container-high focus:border-b-primary h-14 px-3\">\n      @if (searchTerm().trim()) {\n        <div class=\"absolute end-1 top-1/2 -translate-y-1/2\">\n          <button\n            ngsIconButton\n            (click)=\"clearSearch($event)\"\n            class=\"clear-button\"\n            type=\"button\"\n            aria-label=\"Clear search\">\n            <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n          </button>\n        </div>\n      }\n    </div>\n  </ngs-select-header>\n  <ngs-select-body>\n    @for (timezoneGroup of timezoneGroups() | filterTimezones:searchTerm(); track timezoneGroup.name) {\n      <ngs-optgroup [label]=\"timezoneGroup.name\">\n        @for (timezone of timezoneGroup.timezones; track timezone) {\n          <ngs-option [value]=\"timezone.id\">\n            {{ timezone.id }}\n          </ngs-option>\n        }\n      </ngs-optgroup>\n    } @empty {\n      @if (searchTerm()) {\n        <div class=\"text-sm px-4 pt-1 pb-4\">\n          <span i18n>Timezone not found</span>.\n        </div>\n      }\n    }\n    @if (_value()) {\n      <ngs-option [value]=\"_value()\" class=\"hidden\" style=\"display: none\">{{ _value() }}</ngs-option>\n    }\n  </ngs-select-body>\n</ngs-select>\n", styles: [":host{display:block;cursor:pointer;width:100%}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: Select, selector: "ngs-select", inputs: ["id", "placeholder", "disabled", "required", "multiple", "hideCheckIcon", "ariaLabel", "tabIndex", "aria-describedby", "value"], outputs: ["selectionChange", "opened", "closed", "valueChange"], exportAs: ["ngsSelect"] }, { kind: "component", type: Option, selector: "ngs-option", inputs: ["value", "disabled", "selected"], outputs: ["onSelectionChange"], exportAs: ["ngsOption"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "component", type: Button, selector: "    button[ngsButton], button[ngsIconButton],    a[ngsButton], a[ngsIconButton]  ", inputs: ["ngsButton", "ngsIconButton", "loading", "disabled", "disabledInteractive", "disableRipple", "reverse", "fullWidth", "hideTextOnMobile"], exportAs: ["ngsButton"] }, { kind: "component", type: SelectHeader, selector: "ngs-select-header" }, { kind: "component", type: SelectBody, selector: "ngs-select-body", exportAs: ["ngsSelectBody"] }, { kind: "component", type: Optgroup, selector: "ngs-optgroup", inputs: ["label", "disabled"], exportAs: ["ngsOptgroup"] }, { kind: "pipe", type: FilterTimezonesPipe, name: "filterTimezones" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: TimezoneSelect, decorators: [{
            type: Component,
            args: [{ selector: 'ngs-timezone-select', exportAs: 'ngsTimezoneSelect', imports: [
                        Select,
                        Option,
                        FormsModule,
                        FilterTimezonesPipe,
                        Icon,
                        Button,
                        SelectHeader,
                        SelectBody,
                        Optgroup
                    ], providers: [
                        {
                            provide: FormFieldControl,
                            useExisting: forwardRef(() => TimezoneSelect),
                            multi: true,
                        },
                    ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
                        'class': 'ngs-timezone-select',
                        '[id]': 'id',
                        '[attr.tabindex]': 'disabled ? -1 : 0',
                        '(focus)': 'onFocusIn()',
                        '(blur)': 'onFocusOut($event)',
                    }, template: "<ngs-select #select=\"ngsSelect\"\n            [(value)]=\"_value\"\n            [aria-describedby]=\"_userAriaDescribedBy()\"\n            [disabled]=\"disabled\"\n            [placeholder]=\"_placeholder()\"\n            (opened)=\"onSelectOpened()\"\n            (closed)=\"onSelectClosed()\">\n  <ngs-select-header>\n    <div class=\"sticky top-0 z-1 bg-surface-container-lowest\">\n      <input #searchInput\n             type=\"text\"\n             placeholder=\"Search...\"\n             autocomplete=\"off\"\n             [(ngModel)]=\"searchTerm\"\n             class=\"w-full text-sm focus:outline-none border-b border-surface-container-high focus:border-b-primary h-14 px-3\">\n      @if (searchTerm().trim()) {\n        <div class=\"absolute end-1 top-1/2 -translate-y-1/2\">\n          <button\n            ngsIconButton\n            (click)=\"clearSearch($event)\"\n            class=\"clear-button\"\n            type=\"button\"\n            aria-label=\"Clear search\">\n            <ngs-icon name=\"fluent:dismiss-24-regular\"/>\n          </button>\n        </div>\n      }\n    </div>\n  </ngs-select-header>\n  <ngs-select-body>\n    @for (timezoneGroup of timezoneGroups() | filterTimezones:searchTerm(); track timezoneGroup.name) {\n      <ngs-optgroup [label]=\"timezoneGroup.name\">\n        @for (timezone of timezoneGroup.timezones; track timezone) {\n          <ngs-option [value]=\"timezone.id\">\n            {{ timezone.id }}\n          </ngs-option>\n        }\n      </ngs-optgroup>\n    } @empty {\n      @if (searchTerm()) {\n        <div class=\"text-sm px-4 pt-1 pb-4\">\n          <span i18n>Timezone not found</span>.\n        </div>\n      }\n    }\n    @if (_value()) {\n      <ngs-option [value]=\"_value()\" class=\"hidden\" style=\"display: none\">{{ _value() }}</ngs-option>\n    }\n  </ngs-select-body>\n</ngs-select>\n", styles: [":host{display:block;cursor:pointer;width:100%}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
        }], ctorParameters: () => [], propDecorators: { searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], selectRef: [{ type: i0.ViewChild, args: ['select', { isSignal: true }] }], _userAriaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], _placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], _required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], _disabledByInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], _value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], searchTerm: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTerm", required: false }] }, { type: i0.Output, args: ["searchTermChange"] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });

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

export { TimezoneSelect };
//# sourceMappingURL=ngstarter-ui-components-timezone-select.mjs.map
