import { CommonModule, DOCUMENT } from '@angular/common';
import * as i0 from '@angular/core';
import { signal, SecurityContext, HostListener, Input, Component, createComponent, Inject, Injectable } from '@angular/core';
import { Utilities } from '@sdcorejs/utils/fns';
import { trigger, transition, style, animate } from '@angular/animations';
import { TranslatePipe } from '@sdcorejs/angular/i18n';
import * as i2 from '@angular/platform-browser';

class ToastComponent {
    notifyService;
    sanitizer;
    data;
    isExpanded = signal(false, ...(ngDevMode ? [{ debugName: "isExpanded" }] : []));
    MAX_SHOW = 2;
    // --- Logic Timer ---
    timer;
    start;
    remaining;
    constructor(notifyService, sanitizer) {
        this.notifyService = notifyService;
        this.sanitizer = sanitizer;
    }
    /**
     * Sanitize HTML tường minh trước khi render qua `[innerHTML]` — strip
     * `<script>`, event handler (`on*`), `javascript:` URL. Chỉ chạy ở nhánh
     * `data.html === true`; nhánh mặc định render text (auto-escape) nên không
     * có sink này. // why: phơi rõ việc sanitize cho review/scanner bảo mật.
     */
    sanitizeHtml(value) {
        return this.sanitizer.sanitize(SecurityContext.HTML, value) ?? '';
    }
    /** Message ở dạng chuỗi đơn (chỉ dùng khi !isMultiMessage). */
    get singleMessage() {
        return this.data.message;
    }
    ngOnInit() {
        // Khởi tạo thời gian còn lại bằng duration ban đầu
        this.remaining = this.data.duration;
        // Bắt đầu đếm ngược ngay khi hiện ra
        this.resumeTimer();
    }
    ngOnDestroy() {
        clearTimeout(this.timer);
    }
    // --- Logic Pause/Resume ---
    pauseTimer() {
        if (this.timer) {
            // Xóa timer hiện tại
            clearTimeout(this.timer);
            this.timer = null;
            // Tính toán thời gian đã trôi qua để trừ đi
            this.remaining -= Date.now() - this.start;
        }
    }
    resumeTimer() {
        // Chỉ chạy tiếp nếu còn thời gian và timer chưa chạy
        if (this.remaining > 0 && !this.timer) {
            this.start = Date.now();
            this.timer = setTimeout(() => {
                this.close();
            }, this.remaining);
        }
    }
    // --- Logic cũ ---
    close() {
        this.notifyService.remove(this.data.id);
    }
    onActionClick() {
        this.data.onAction?.();
    }
    toggleExpand() {
        this.isExpanded.update(v => !v);
    }
    get isMultiMessage() {
        return Array.isArray(this.data.message);
    }
    get messages() {
        return this.data.message;
    }
    get displayMessages() {
        const msgs = this.messages;
        if (this.isExpanded())
            return msgs;
        return msgs.slice(0, this.MAX_SHOW);
    }
    get restCount() {
        return this.messages.length - this.MAX_SHOW;
    }
    get hasMore() {
        return this.isMultiMessage && this.messages.length > this.MAX_SHOW;
    }
    /** autoid theo loại toast để E2E chọn trực tiếp, vd `services-notify-toast-success`. */
    get autoId() {
        return `services-notify-toast-${this.data.type}`;
    }
    /** Gộp message (mảng nối ' | ') để phơi ra data-message cho E2E. */
    get dataMessage() {
        return Array.isArray(this.data.message) ? this.data.message.join(' | ') : this.data.message;
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ToastComponent, deps: [{ token: SdNotifyService }, { token: i2.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: ToastComponent, isStandalone: true, selector: "toast", inputs: { data: "data" }, host: { listeners: { "mouseenter": "pauseTimer()", "mouseleave": "resumeTimer()" }, properties: { "@toastAnimation": "true", "class": "\"bg-white sd-toast\"", "attr.data-autoid": "autoId", "attr.data-type": "data.type", "attr.data-title": "data.title ?? null", "attr.data-message": "dataMessage" } }, ngImport: i0, template: "<div class=\"sd-toast__bar\" [class]=\"'bg-' + data.type\"></div>\n\n<div class=\"sd-toast__icon\" [class]=\"'text-' + data.type\">\n  @switch (data.type) {\n    @case ('success') {\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>\n    }\n    @case ('info') {\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/></svg>\n    }\n    @case ('warning') {\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>\n    }\n    @case ('error') {\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>\n    }\n  }\n</div>\n\n<div class=\"sd-toast__content\">\n  <div class=\"sd-toast__header\">\n    <strong class=\"sd-toast__title\">{{ data.title || ('core.notify.type.' + data.type | translate) }}</strong>\n    <button class=\"sd-toast__close\" (click)=\"close()\">\u2715</button>\n  </div>\n\n  <div class=\"sd-toast__body\">\n    @if (!isMultiMessage) {\n      <!-- why: m\u1EB7c \u0111\u1ECBnh render TEXT (auto-escape) \u2014 an to\u00E0n. Ch\u1EC9 render HTML\n           (\u0111\u00E3 sanitize) khi data.html=true. -->\n      @if (data.html) {\n        <div [innerHTML]=\"sanitizeHtml(singleMessage)\"></div>\n      } @else {\n        <div>{{ singleMessage }}</div>\n      }\n    }\n    @else {\n      <ul class=\"msg-list\">\n        @for (msg of displayMessages; track msg) {\n          @if (data.html) {\n            <li [innerHTML]=\"sanitizeHtml('- ' + msg)\"></li>\n          } @else {\n            <li>{{ '- ' + msg }}</li>\n          }\n        }\n      </ul>\n\n      @if (hasMore) {\n        <button type=\"button\" \n                class=\"toggle-more btn-link\" \n                [class]=\"'text-' + data.type\" \n                (click)=\"toggleExpand()\">\n          {{ isExpanded() ? ('core.notify.show-less' | translate) : ('core.notify.show-more' | translate: { count: restCount }) }}\n        </button>\n      }\n    }\n  </div>\n\n  @if (data.actionLabel) {\n    <div class=\"sd-toast__actions\">\n      <button class=\"btn-action\" \n              [class]=\"'bg-' + data.type + '-light text-' + data.type\"\n              (click)=\"onActionClick()\">\n        {{ data.actionLabel }}\n      </button>\n    </div>\n  }\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;width:350px;border-radius:8px;box-shadow:0 4px 12px #00000026;margin-bottom:12px;overflow:hidden;position:relative;font-family:Roboto,sans-serif;pointer-events:auto}.sd-toast__bar{width:6px;flex-shrink:0}.sd-toast__icon{padding:12px 0 0 12px;flex-shrink:0}.sd-toast__icon svg{width:24px;height:24px}.sd-toast__content{flex-grow:1;padding:12px;display:flex;flex-direction:column}.sd-toast__header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:4px}.sd-toast__title{font-size:16px;font-weight:600}.sd-toast__close{background:none;border:none;font-size:16px;cursor:pointer;line-height:1;padding:0;opacity:.5;transition:opacity .2s}.sd-toast__close:hover{opacity:1}.sd-toast__body{font-size:14px;line-height:1.4;word-break:break-word}.sd-toast__actions{display:flex;justify-content:flex-end;margin-top:8px}.msg-list{padding-left:0;list-style:none;margin:0}.toggle-more{background:none;border:none;padding:0;font:inherit;cursor:pointer;font-size:13px;margin-top:4px;font-weight:500;text-align:left}.toggle-more:hover{text-decoration:underline}.toggle-more:focus-visible{outline:2px solid currentColor;outline-offset:2px}.btn-action{border:none;padding:6px 12px;border-radius:4px;font-weight:600;cursor:pointer;font-size:13px;transition:filter .2s}.btn-action:hover{filter:brightness(.95)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }], animations: [
            trigger('toastAnimation', [
                transition(':enter', [
                    style({ transform: 'translateX(100%)', opacity: 0 }),
                    animate('300ms ease-out', style({ transform: 'translateX(0)', opacity: 1 }))
                ]),
                transition(':leave', [
                    animate('200ms ease-in', style({ transform: 'translateX(100%)', opacity: 0 }))
                ])
            ])
        ] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ToastComponent, decorators: [{
            type: Component,
            args: [{ selector: 'toast', standalone: true, imports: [CommonModule, TranslatePipe], animations: [
                        trigger('toastAnimation', [
                            transition(':enter', [
                                style({ transform: 'translateX(100%)', opacity: 0 }),
                                animate('300ms ease-out', style({ transform: 'translateX(0)', opacity: 1 }))
                            ]),
                            transition(':leave', [
                                animate('200ms ease-in', style({ transform: 'translateX(100%)', opacity: 0 }))
                            ])
                        ])
                    ], host: {
                        '[@toastAnimation]': 'true',
                        '[class]': '"bg-white sd-toast"',
                        // E2E hooks đọc bởi sd-autoid-inspector: autoid theo loại + state type/title/message.
                        '[attr.data-autoid]': 'autoId',
                        '[attr.data-type]': 'data.type',
                        '[attr.data-title]': 'data.title ?? null',
                        '[attr.data-message]': 'dataMessage'
                    }, template: "<div class=\"sd-toast__bar\" [class]=\"'bg-' + data.type\"></div>\n\n<div class=\"sd-toast__icon\" [class]=\"'text-' + data.type\">\n  @switch (data.type) {\n    @case ('success') {\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z\"/></svg>\n    }\n    @case ('info') {\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"/></svg>\n    }\n    @case ('warning') {\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>\n    }\n    @case ('error') {\n      <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/></svg>\n    }\n  }\n</div>\n\n<div class=\"sd-toast__content\">\n  <div class=\"sd-toast__header\">\n    <strong class=\"sd-toast__title\">{{ data.title || ('core.notify.type.' + data.type | translate) }}</strong>\n    <button class=\"sd-toast__close\" (click)=\"close()\">\u2715</button>\n  </div>\n\n  <div class=\"sd-toast__body\">\n    @if (!isMultiMessage) {\n      <!-- why: m\u1EB7c \u0111\u1ECBnh render TEXT (auto-escape) \u2014 an to\u00E0n. Ch\u1EC9 render HTML\n           (\u0111\u00E3 sanitize) khi data.html=true. -->\n      @if (data.html) {\n        <div [innerHTML]=\"sanitizeHtml(singleMessage)\"></div>\n      } @else {\n        <div>{{ singleMessage }}</div>\n      }\n    }\n    @else {\n      <ul class=\"msg-list\">\n        @for (msg of displayMessages; track msg) {\n          @if (data.html) {\n            <li [innerHTML]=\"sanitizeHtml('- ' + msg)\"></li>\n          } @else {\n            <li>{{ '- ' + msg }}</li>\n          }\n        }\n      </ul>\n\n      @if (hasMore) {\n        <button type=\"button\" \n                class=\"toggle-more btn-link\" \n                [class]=\"'text-' + data.type\" \n                (click)=\"toggleExpand()\">\n          {{ isExpanded() ? ('core.notify.show-less' | translate) : ('core.notify.show-more' | translate: { count: restCount }) }}\n        </button>\n      }\n    }\n  </div>\n\n  @if (data.actionLabel) {\n    <div class=\"sd-toast__actions\">\n      <button class=\"btn-action\" \n              [class]=\"'bg-' + data.type + '-light text-' + data.type\"\n              (click)=\"onActionClick()\">\n        {{ data.actionLabel }}\n      </button>\n    </div>\n  }\n</div>", styles: ["@charset \"UTF-8\";:host{display:flex;width:350px;border-radius:8px;box-shadow:0 4px 12px #00000026;margin-bottom:12px;overflow:hidden;position:relative;font-family:Roboto,sans-serif;pointer-events:auto}.sd-toast__bar{width:6px;flex-shrink:0}.sd-toast__icon{padding:12px 0 0 12px;flex-shrink:0}.sd-toast__icon svg{width:24px;height:24px}.sd-toast__content{flex-grow:1;padding:12px;display:flex;flex-direction:column}.sd-toast__header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:4px}.sd-toast__title{font-size:16px;font-weight:600}.sd-toast__close{background:none;border:none;font-size:16px;cursor:pointer;line-height:1;padding:0;opacity:.5;transition:opacity .2s}.sd-toast__close:hover{opacity:1}.sd-toast__body{font-size:14px;line-height:1.4;word-break:break-word}.sd-toast__actions{display:flex;justify-content:flex-end;margin-top:8px}.msg-list{padding-left:0;list-style:none;margin:0}.toggle-more{background:none;border:none;padding:0;font:inherit;cursor:pointer;font-size:13px;margin-top:4px;font-weight:500;text-align:left}.toggle-more:hover{text-decoration:underline}.toggle-more:focus-visible{outline:2px solid currentColor;outline-offset:2px}.btn-action{border:none;padding:6px 12px;border-radius:4px;font-weight:600;cursor:pointer;font-size:13px;transition:filter .2s}.btn-action:hover{filter:brightness(.95)}\n"] }]
        }], ctorParameters: () => [{ type: SdNotifyService }, { type: i2.DomSanitizer }], propDecorators: { data: [{
                type: Input,
                args: [{ required: true }]
            }], pauseTimer: [{
                type: HostListener,
                args: ['mouseenter']
            }], resumeTimer: [{
                type: HostListener,
                args: ['mouseleave']
            }] } });

class ToastContainerComponent {
    toasts = signal([], ...(ngDevMode ? [{ debugName: "toasts" }] : []));
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ToastContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.24", type: ToastContainerComponent, isStandalone: true, selector: "toast-container", ngImport: i0, template: `
    <div class="toast-container">
      @for (toast of toasts(); track toast.id) {
        <toast [data]="toast"> </toast>
      }
    </div>
  `, isInline: true, styles: [".toast-container{position:fixed;top:20px;right:20px;z-index:9999;display:flex;flex-direction:column;pointer-events:none}sd-toast{pointer-events:auto}\n"], dependencies: [{ kind: "component", type: ToastComponent, selector: "toast", inputs: ["data"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: ToastContainerComponent, decorators: [{
            type: Component,
            args: [{ selector: 'toast-container', standalone: true, imports: [ToastComponent], template: `
    <div class="toast-container">
      @for (toast of toasts(); track toast.id) {
        <toast [data]="toast"> </toast>
      }
    </div>
  `, styles: [".toast-container{position:fixed;top:20px;right:20px;z-index:9999;display:flex;flex-direction:column;pointer-events:none}sd-toast{pointer-events:auto}\n"] }]
        }] });

class SdNotifyService {
    appRef;
    injector;
    document;
    toasts = signal([], ...(ngDevMode ? [{ debugName: "toasts" }] : []));
    // Constants
    #DEBOUNCE_TIME = 500;
    #DEFAULT_SUCCESS_DURATION = 3000;
    #DEFAULT_BUFFERED_DURATION = 5000;
    #MAX_TOASTS = 5;
    // State
    #buffer = {};
    #timers = {};
    constructor(appRef, injector, document) {
        this.appRef = appRef;
        this.injector = injector;
        this.document = document;
        this.#initContainer();
    }
    #initContainer() {
        const componentRef = createComponent(ToastContainerComponent, {
            environmentInjector: this.injector,
        });
        componentRef.instance.toasts = this.toasts;
        this.appRef.attachView(componentRef.hostView);
        const domElem = componentRef.hostView.rootNodes[0];
        this.document.body.appendChild(domElem);
    }
    // Public API
    success(message, option) {
        this.#addImmediate('success', message, option);
    }
    info(message, option) {
        this.#addImmediate('info', message, option);
    }
    warning(message, option) {
        this.#addToBuffer('warning', message, option);
    }
    error(message, option) {
        this.#addToBuffer('error', message, option);
    }
    remove(id) {
        this.toasts.update((current) => current.filter((t) => t.id !== id));
    }
    clearAll() {
        this.toasts.set([]);
        this.#clearAllTimers();
    }
    clearByType(type) {
        this.toasts.update((current) => current.filter((t) => t.type !== type));
    }
    // Private helpers
    #addImmediate(type, message, option) {
        const newToast = {
            id: Utilities.generateUuid(),
            type,
            message,
            title: option?.title,
            duration: option?.duration ?? this.#DEFAULT_SUCCESS_DURATION,
            actionLabel: option?.actionLabel,
            onAction: option?.onAction,
            html: option?.html
        };
        this.toasts.update((current) => {
            const updated = [newToast, ...current];
            return updated.slice(0, this.#MAX_TOASTS);
        });
    }
    #addToBuffer(type, message, option) {
        if (!this.#buffer[type]) {
            this.#buffer[type] = [];
        }
        const msgs = Array.isArray(message) ? message : [message];
        this.#buffer[type].push(...msgs);
        // Clear existing timer and set new one
        if (this.#timers[type]) {
            clearTimeout(this.#timers[type]);
        }
        this.#timers[type] = setTimeout(() => {
            this.#flushBuffer(type, option);
        }, this.#DEBOUNCE_TIME);
    }
    #flushBuffer(type, option) {
        const messages = [...new Set(this.#buffer[type])];
        // Cleanup
        this.#buffer[type] = [];
        this.#timers[type] = null;
        if (messages.length === 0)
            return;
        const typeLabel = type === 'error' ? 'Error' : 'Warning';
        const title = option?.title ?? typeLabel;
        const finalTitle = messages.length > 1 ? `${title} (${messages.length})` : title;
        const newToast = {
            id: Utilities.generateUuid(),
            type,
            message: messages.length === 1 ? messages[0] : messages,
            title: finalTitle,
            duration: option?.duration ?? this.#DEFAULT_BUFFERED_DURATION,
            actionLabel: option?.actionLabel,
            onAction: option?.onAction,
            html: option?.html
        };
        this.toasts.update((current) => {
            const updated = [newToast, ...current];
            return updated.slice(0, this.#MAX_TOASTS);
        });
    }
    #clearAllTimers() {
        Object.values(this.#timers).forEach(timer => {
            if (timer)
                clearTimeout(timer);
        });
        this.#timers = {};
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdNotifyService, deps: [{ token: i0.ApplicationRef }, { token: i0.EnvironmentInjector }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdNotifyService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdNotifyService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [{ type: i0.ApplicationRef }, { type: i0.EnvironmentInjector }, { type: Document, decorators: [{
                    type: Inject,
                    args: [DOCUMENT]
                }] }] });

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

export { SdNotifyService };
//# sourceMappingURL=sdcorejs-angular-services-notify.mjs.map
