import * as i0 from '@angular/core';
import { Injectable, Pipe, Input, ChangeDetectionStrategy, Component, viewChild, input, inject, ChangeDetectorRef, ElementRef, HostListener, booleanAttribute, signal, Injector, afterNextRender, NgModuleFactory, createNgModule } from '@angular/core';
import * as i2 from '@angular/router';
import { Router, RoutesRecognized, NavigationEnd, RouterOutlet, ActivatedRoute } from '@angular/router';
import * as i1$1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i3 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { BehaviorSubject, Subscription, from, isObservable, lastValueFrom, Subject } from 'rxjs';
import { debounceTime, startWith, filter, concatMap, take } from 'rxjs/operators';
import { SdNotifyService } from '@sdcorejs/angular/services/notify';
import { I18nService } from '@sdcorejs/angular/i18n';
import { Utilities } from '@sdcorejs/utils/fns';
import * as i1 from '@angular/cdk/drag-drop';
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
import { SdBadge } from '@sdcorejs/angular/components/badge';

class SdTabBase {
    #tab;
    constructor(tab) {
        this.#tab = tab;
    }
    get tab() {
        return this.#tab;
    }
}
class SdTabActivated extends SdTabBase {
}
class SdTabDeactivated extends SdTabBase {
}

class SdTabRouterService {
    events = new BehaviorSubject(new SdTabBase(undefined));
    actions = new BehaviorSubject(undefined);
    builders = new BehaviorSubject([]);
    currentTabChanges = new BehaviorSubject(undefined);
    newTabs = new BehaviorSubject(undefined);
    updateTabs = new BehaviorSubject(undefined);
    #currentTab = undefined;
    #componentBuilders = [];
    constructor() { }
    addBuilder = (builder) => {
        if (!this.#componentBuilders.some(e => e.component === builder.component)) {
            this.#componentBuilders.push(builder);
            this.builders.next(this.#componentBuilders);
        }
    };
    get currentTab() {
        return this.#currentTab;
    }
    get currentKey() {
        return this.#currentTab?.key || null;
    }
    // select = (tabOrKey: string | SdTab): void => {
    //   const tab = this.#tabs.find(e => {
    //     if (typeof (tabOrKey) === 'string') {
    //       return e.key === tabOrKey;
    //     }
    //     return e.key === tabOrKey?.key;
    //   });
    //   if (tab) {
    //     this.#currentTab = tab;
    //   }
    // }
    // add = (tab: SdTab): void => {
    //   if (!tab.key) {
    //     this.notifyService.notify.warning('Tab key is required');
    //   }
    //   if (!tab.component) {
    //     this.notifyService.notify.warning('Tab component is required');
    //   }
    //   const existedTab = this.#tabs.find(e => e.key === tab.key);
    //   if (!existedTab) {
    //     this.#tabs.push(tab);
    //     this.select(tab);
    //   } else {
    //     this.select(existedTab);
    //   }
    // }
    // remove = (tabOrKey: string | SdTab): void => {
    //   this.#tabs = this.#tabs.filter(e => {
    //     if (typeof (tabOrKey) === 'string') {
    //       return e.key !== tabOrKey;
    //     }
    //     return e.key !== tabOrKey?.key;
    //   });
    // }
    setCurrentTab = (tab) => {
        this.#currentTab = tab;
        this.currentTabChanges.next(tab);
    };
    pushEvent = (tab, Event) => {
        this.events.next(new Event(tab));
    };
    setOptions = () => { };
    close = (tab) => {
        tab = tab || this.#currentTab;
        if (tab) {
            this.actions.next({
                type: 'close',
                tab,
            });
        }
    };
    // Gọi hàm này để thực hiện update tab
    updateTab = (tab) => { };
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [] });

class SdTabDecoratorService {
    static tabRouterService = new BehaviorSubject(undefined);
    constructor(tabRouterService) {
        SdTabDecoratorService.tabRouterService.next(tabRouterService);
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabDecoratorService, deps: [{ token: SdTabRouterService }], target: i0.ɵɵFactoryTarget.Injectable });
    static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabDecoratorService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabDecoratorService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: () => [{ type: SdTabRouterService }] });

class SdTabInfoPipe {
    tabRouterService;
    constructor(tabRouterService) {
        this.tabRouterService = tabRouterService;
    }
    transform(tabInfo, tab) {
        if (tabInfo) {
            return tabInfo;
        }
        const builders = this.tabRouterService.builders.getValue();
        const builder = builders.find(e => e.component === tab.component);
        if (builder) {
            const { url, params, queryParams, data } = tab;
            return {
                name: typeof builder.name === 'function' ? builder.name({ url, params, queryParams, data }) : builder.name,
                icon: typeof builder.icon === 'function' ? builder.icon({ url, params, queryParams }) : builder.icon,
                tooltip: typeof builder.tooltip === 'function' ? builder.tooltip({ url, params, queryParams }) : builder.tooltip,
                color: typeof builder.color === 'function' ? builder.color({ url, params, queryParams }) : builder.color,
            };
        }
        return {
            name: tab.url,
            icon: undefined,
        };
    }
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabInfoPipe, deps: [{ token: SdTabRouterService }], target: i0.ɵɵFactoryTarget.Pipe });
    static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: SdTabInfoPipe, isStandalone: true, name: "sdTabInfo" });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabInfoPipe, decorators: [{
            type: Pipe,
            args: [{
                    name: 'sdTabInfo',
                    standalone: true,
                }]
        }], ctorParameters: () => [{ type: SdTabRouterService }] });

class SdTabRouterItemComponent {
    cdRef;
    tabRouterService;
    router;
    tab;
    #subsctiption = new Subscription();
    tabInfo;
    constructor(cdRef, tabRouterService, router) {
        this.cdRef = cdRef;
        this.tabRouterService = tabRouterService;
        this.router = router;
    }
    ngOnInit() {
        this.#subsctiption.add(this.tabRouterService.events.pipe(debounceTime(100)).subscribe(() => {
            this.cdRef.markForCheck();
        }));
        this.#subsctiption.add(this.tab.tabInfoChanges.pipe(startWith(null)).subscribe(tabInfo => {
            if (tabInfo) {
                this.tabInfo = tabInfo;
                this.cdRef.markForCheck();
            }
        }));
    }
    ngOnDestroy() {
        this.#subsctiption.unsubscribe();
    }
    onTabClick = (event) => {
        event.preventDefault();
        this.router.navigate([this.tab.url], {
            queryParams: this.tab.queryParams,
            state: { switchTab: true },
        });
    };
    close = (event) => {
        event.preventDefault();
        event.stopPropagation();
        this.#closeTab();
    };
    onMousedown = (event) => {
        if (event.button === 1) {
            event.preventDefault();
        }
    };
    onMouseup = (event) => {
        if (event.button === 1) {
            event.preventDefault();
            event.stopPropagation();
            this.#closeTab();
        }
    };
    #closeTab = async () => {
        if (this.tab?.beforeClose) {
            //
            if (this.tab?.beforeClose()) {
                const result = this.tab?.beforeClose();
                if (typeof result === 'boolean') {
                    if (result) {
                        this.tabRouterService.close(this.tab);
                    }
                }
                else {
                    if (await result) {
                        this.tabRouterService.close(this.tab);
                    }
                }
            }
        }
        else {
            this.tabRouterService.close(this.tab);
        }
    };
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterItemComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: SdTabRouterService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdTabRouterItemComponent, isStandalone: true, selector: "sd-tab-router-item", inputs: { tab: "tab" }, ngImport: i0, template: "<a\n  [href]=\"[tab.url]\"\n  class=\"tab-router__item d-flex align-items-center gap-8\"\n  [class.tab-router__item--active]=\"tab.isActive\"\n  (click)=\"onTabClick($event)\"\n  (mousedown)=\"onMousedown($event)\"\n  (mouseup)=\"onMouseup($event)\">\n  @let info = tabInfo | sdTabInfo: tab;\n  @if (info) {\n    <sd-badge\n      style=\"overflow: hidden;white-space: nowrap;\"\n      [icon]=\"info.icon\"\n      [title]=\"info.icon\"\n      [tooltip]=\"info.tooltip || info.name\"\n      [title]=\"info.name\"\n      [color]=\"info.color\"\n      (click)=\"onTabClick($event)\"></sd-badge>\n    <button\n      aria-hidden=\"true\"\n      class=\"tab-router__close d-flex align-items-center justify-content-center ml-auto p-0\"\n      (click)=\"close($event)\"\n      (mousedown)=\"$event.stopPropagation()\">\n      <mat-icon aria-hidden=\"true\" fontIcon=\"close\"></mat-icon>\n    </button>\n  }\n</a>\n", styles: [":host{display:block;overflow:hidden;position:relative;flex:1 1 64px;max-width:240px}:host:after{content:\"\";position:absolute;right:0;top:0;bottom:0;height:16px;width:1px;background:#dde0e5;margin:auto}:host:last-child:after{content:none}:host::ng-deep .tab-router__item sd-badge .c-badge-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:184px}.flex-1{flex:1}.tab-router__icon{background-color:#5c6bc0;width:16px;height:16px;line-height:16px;text-align:center;font-size:10px;color:#fff;border-radius:2px;text-transform:uppercase}.tab-router__icon .mat-icon{height:10px;width:10px;font-size:10px}.tab-router__close{color:#757575;outline:none;border:0;background:none;border-radius:50%;height:16px;width:16px}.tab-router__close:hover{background-color:#0000001f}.tab-router__close .mat-icon{font-size:12px;height:12px;width:12px}.tab-router__item{background:#f2f3f4;padding:8px;color:inherit;text-decoration:none;font-size:12px;line-height:16px;overflow:hidden}.tab-router__item:hover{background-color:#fff}.tab-router__name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tab-router__item--active{border-radius:8px 8px 0 0;background-color:#fff}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: SdBadge, selector: "sd-badge", inputs: ["type", "color", "primary", "secondary", "success", "info", "warning", "error", "fontSet", "title", "description", "tooltip", "icon", "size"], outputs: ["click"] }, { kind: "pipe", type: SdTabInfoPipe, name: "sdTabInfo" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterItemComponent, decorators: [{
            type: Component,
            args: [{ selector: 'sd-tab-router-item', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, MatIconModule, SdBadge, SdTabInfoPipe], template: "<a\n  [href]=\"[tab.url]\"\n  class=\"tab-router__item d-flex align-items-center gap-8\"\n  [class.tab-router__item--active]=\"tab.isActive\"\n  (click)=\"onTabClick($event)\"\n  (mousedown)=\"onMousedown($event)\"\n  (mouseup)=\"onMouseup($event)\">\n  @let info = tabInfo | sdTabInfo: tab;\n  @if (info) {\n    <sd-badge\n      style=\"overflow: hidden;white-space: nowrap;\"\n      [icon]=\"info.icon\"\n      [title]=\"info.icon\"\n      [tooltip]=\"info.tooltip || info.name\"\n      [title]=\"info.name\"\n      [color]=\"info.color\"\n      (click)=\"onTabClick($event)\"></sd-badge>\n    <button\n      aria-hidden=\"true\"\n      class=\"tab-router__close d-flex align-items-center justify-content-center ml-auto p-0\"\n      (click)=\"close($event)\"\n      (mousedown)=\"$event.stopPropagation()\">\n      <mat-icon aria-hidden=\"true\" fontIcon=\"close\"></mat-icon>\n    </button>\n  }\n</a>\n", styles: [":host{display:block;overflow:hidden;position:relative;flex:1 1 64px;max-width:240px}:host:after{content:\"\";position:absolute;right:0;top:0;bottom:0;height:16px;width:1px;background:#dde0e5;margin:auto}:host:last-child:after{content:none}:host::ng-deep .tab-router__item sd-badge .c-badge-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:184px}.flex-1{flex:1}.tab-router__icon{background-color:#5c6bc0;width:16px;height:16px;line-height:16px;text-align:center;font-size:10px;color:#fff;border-radius:2px;text-transform:uppercase}.tab-router__icon .mat-icon{height:10px;width:10px;font-size:10px}.tab-router__close{color:#757575;outline:none;border:0;background:none;border-radius:50%;height:16px;width:16px}.tab-router__close:hover{background-color:#0000001f}.tab-router__close .mat-icon{font-size:12px;height:12px;width:12px}.tab-router__item{background:#f2f3f4;padding:8px;color:inherit;text-decoration:none;font-size:12px;line-height:16px;overflow:hidden}.tab-router__item:hover{background-color:#fff}.tab-router__name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tab-router__item--active{border-radius:8px 8px 0 0;background-color:#fff}\n"] }]
        }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: SdTabRouterService }, { type: i2.Router }], propDecorators: { tab: [{
                type: Input,
                args: [{ required: true }]
            }] } });

class SdTabRouterNavComponent {
    tabRouterNav = viewChild('tabRouterNav', ...(ngDevMode ? [{ debugName: "tabRouterNav" }] : /* istanbul ignore next */ []));
    tabs = input([], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
    mode = 'default';
    cdRef = inject(ChangeDetectorRef);
    elementRef = inject(ElementRef);
    onResize() {
        this.checkUI();
    }
    checkUI = () => {
        const width = this.tabRouterNav()?.nativeElement.clientWidth ?? 0;
        const tabs = this.tabs();
        if (tabs.length === 0) {
            this.mode = 'default';
            this.cdRef.markForCheck();
            return;
        }
        const nameWidth = (width - tabs.length * 68) / tabs.length;
        if (nameWidth <= 20) {
            this.mode = 'compact';
        }
        else {
            this.mode = 'default';
        }
        this.cdRef.markForCheck();
    };
    onDrop = (event) => {
        moveItemInArray(this.tabs(), event.previousIndex, event.currentIndex);
    };
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdTabRouterNavComponent, isStandalone: true, selector: "sd-tab-router-nav", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "onResize()" } }, viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n  #tabRouterNav\n  cdkDropList\n  cdkDropListLockAxis=\"x\"\n  cdkDropListOrientation=\"horizontal\"\n  (cdkDropListDropped)=\"onDrop($event)\"\n  class=\"tab-router__nav tab-router__nav--{{ mode }} d-flex align-items-center flex-nowrap\"\n  [class.d-none]=\"tabs().length > 1\">\n  @for (tab of tabs(); track tab.key) {\n    <sd-tab-router-item [tab]=\"tab\" cdkDrag [cdkDragBoundary]=\"elementRef.nativeElement\"></sd-tab-router-item>\n  }\n</div>\n", styles: [".tab-router__nav{background:#f9f9f9;overflow:hidden}.tab-router__nav--compact::ng-deep .tab-router__name{display:none}.tab-router__nav--compact::ng-deep .tab-router__icon{margin:0!important}.tab-router__nav--compact::ng-deep .tab-router__item--active{min-width:240px}.tab-router__nav--compact::ng-deep .tab-router__item--active .tab-router__icon{margin-right:8px!important}.tab-router__nav--compact::ng-deep .tab-router__item--active .tab-router__name{display:-webkit-box}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: SdTabRouterItemComponent, selector: "sd-tab-router-item", inputs: ["tab"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterNavComponent, decorators: [{
            type: Component,
            args: [{ selector: 'sd-tab-router-nav', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, DragDropModule, SdTabRouterItemComponent], template: "<div\n  #tabRouterNav\n  cdkDropList\n  cdkDropListLockAxis=\"x\"\n  cdkDropListOrientation=\"horizontal\"\n  (cdkDropListDropped)=\"onDrop($event)\"\n  class=\"tab-router__nav tab-router__nav--{{ mode }} d-flex align-items-center flex-nowrap\"\n  [class.d-none]=\"tabs().length > 1\">\n  @for (tab of tabs(); track tab.key) {\n    <sd-tab-router-item [tab]=\"tab\" cdkDrag [cdkDragBoundary]=\"elementRef.nativeElement\"></sd-tab-router-item>\n  }\n</div>\n", styles: [".tab-router__nav{background:#f9f9f9;overflow:hidden}.tab-router__nav--compact::ng-deep .tab-router__name{display:none}.tab-router__nav--compact::ng-deep .tab-router__icon{margin:0!important}.tab-router__nav--compact::ng-deep .tab-router__item--active{min-width:240px}.tab-router__nav--compact::ng-deep .tab-router__item--active .tab-router__icon{margin-right:8px!important}.tab-router__nav--compact::ng-deep .tab-router__item--active .tab-router__name{display:-webkit-box}\n"] }]
        }], propDecorators: { tabRouterNav: [{ type: i0.ViewChild, args: ['tabRouterNav', { isSignal: true }] }], tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: false }] }], onResize: [{
                type: HostListener,
                args: ['window:resize']
            }] } });

/* eslint-disable @typescript-eslint/no-explicit-any */
// eslint-disable-next-line @angular-eslint/no-unused-standalone-imports
class SdTabRouterOutletComponent {
    disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
    tabRouterNav = viewChild('tabRouterNav', ...(ngDevMode ? [{ debugName: "tabRouterNav" }] : /* istanbul ignore next */ []));
    tabs = signal([], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
    #router = inject(Router);
    #injector = inject(Injector);
    #tabRouterService = inject(SdTabRouterService);
    #sdNotifyService = inject(SdNotifyService);
    #i18n = inject(I18nService);
    // Inject để đảm bảo SdTabDecoratorService được khởi tạo (nó register BehaviorSubject
    // tĩnh để @SdTab decorator có thể truy cập SdTabRouterService). Không dùng trực tiếp ở đây.
    #tabDecoratorService = inject(SdTabDecoratorService);
    #rootRoute;
    #subscription = new Subscription();
    // State của navigation hiện tại (replaceTab, switchTab, ...) được capture ở RoutesRecognized
    // và dùng lại ở NavigationEnd. Lý do: tại NavigationEnd, getCurrentNavigation() đã trả về null,
    // còn lastSuccessfulNavigation và window.history.state không đáng tin cậy với mọi case.
    #pendingNavigationState = {};
    // Serialize mọi #activeRoute (router events + initial sync) để tránh race tạo duplicate tab.
    #activationQueue = Promise.resolve();
    constructor() {
        this.#subscription.add(this.#router.events
            .pipe(
        // KHÔNG unwrap event.routerEvent (vd Scroll wrap NavigationEnd):
        // Angular emit NavigationEnd RAW trước, RouterScroller emit Scroll(NavigationEnd) sau.
        // Nếu unwrap thêm Scroll → handler fire 2 lần cho 1 nav → race condition tạo duplicate tabs.
        // Hybrid: cần CẢ HAI event vì mỗi event chứa data khác nhau ở thời điểm khác nhau.
        // - RoutesRecognized: navigation đang in-flight → getCurrentNavigation().extras.state đọc được
        // - NavigationEnd: navigation hoàn tất → routerState.root đã update với route mới (cần cho lazy routes)
        filter((event) => event instanceof RoutesRecognized || event instanceof NavigationEnd), 
        // Serialize: #handleEvent async (await getBestInjector). 2 nav liên tiếp
        // không await xen kẽ → tránh race đọc this.tabs() = [] khi tab đầu chưa kịp set.
        concatMap(event => from(this.#handleEvent(event))))
            .subscribe());
        this.#subscription.add(this.#tabRouterService.actions.subscribe((event) => {
            if (this.disabled())
                return;
            if (event?.type === 'close') {
                this.#closeTab(event.tab);
            }
        }));
        // Initial navigation có thể hoàn tất trước khi outlet subscribe router.events
        // (blocking init hoặc outlet mount muộn). Catch-up sau first render.
        afterNextRender(() => {
            void this.#syncCurrentRoute();
        });
    }
    ngOnDestroy() {
        this.#subscription.unsubscribe();
    }
    #scheduleActivation = (task) => {
        const run = this.#activationQueue.then(task);
        this.#activationQueue = run.catch(() => undefined);
        return run;
    };
    #handleEvent = async (event) => {
        if (this.disabled()) {
            this.#pendingNavigationState = {};
            return;
        }
        if (event instanceof RoutesRecognized) {
            // Capture state ngay lúc nav còn in-flight. Đây là điểm duy nhất chắc chắn
            // getCurrentNavigation() trả về Navigation object với extras.state nguyên vẹn.
            this.#pendingNavigationState = this.#router.getCurrentNavigation()?.extras?.state ?? {};
            return;
        }
        // NavigationEnd: dùng routerState.snapshot.root (canonical state tại NavigationEnd),
        // KHÔNG dùng injected ActivatedRoute.snapshot — có thể chưa sync trên initial load.
        const navigationState = this.#pendingNavigationState;
        this.#pendingNavigationState = {};
        await this.#scheduleActivation(async () => {
            const route = this.#getActivatedRouteSnapshot(this.#router.routerState.snapshot.root);
            this.#rootRoute = this.#router.routerState.root;
            await this.#activeRoute(event.urlAfterRedirects || event.url, route, navigationState);
        });
    };
    // Bù initial navigation nếu NavigationEnd đã fire trước khi outlet subscribe.
    #syncCurrentRoute = () => {
        if (this.disabled() || !this.#router.navigated) {
            return Promise.resolve();
        }
        const route = this.#getActivatedRouteSnapshot(this.#router.routerState.snapshot.root);
        if (!route?.component) {
            return Promise.resolve();
        }
        const [urlPath] = this.#router.url.split('?');
        const key = Utilities.hash({
            url: urlPath,
            queryParams: { ...(route.queryParams || {}) },
        });
        if (this.tabs().some(tab => tab.key === key)) {
            return Promise.resolve();
        }
        return this.#scheduleActivation(async () => {
            if (this.tabs().some(tab => tab.key === key)) {
                return;
            }
            this.#rootRoute = this.#router.routerState.root;
            await this.#activeRoute(this.#router.url, route, {});
        });
    };
    tabTrackBy = (index, tab) => tab.key;
    #closeTab = (tab) => {
        if (this.disabled())
            return;
        const currentTabs = this.tabs();
        const { isActive, key: activeKey } = tab;
        if (isActive) {
            const activeIndex = currentTabs.findIndex(({ key }) => key === activeKey);
            const nextTab = currentTabs[activeIndex + 1] || currentTabs[activeIndex - 1];
            this.tabs.set(currentTabs.filter(({ key }) => key !== activeKey));
            if (nextTab) {
                this.#router.navigate([nextTab.url], {
                    queryParams: { ...(nextTab.queryParams || {}) },
                    state: { switchTab: true },
                });
            }
            else {
                this.#router.navigateByUrl('/', { state: { switchTab: true } });
            }
        }
        else {
            this.tabs.set(currentTabs.filter(({ key }) => key !== tab.key));
            this.tabRouterNav()?.checkUI();
        }
    };
    #activeRoute = async (fullUrl, route, state = {}) => {
        if (this.disabled())
            return;
        if (!route?.component)
            return;
        const component = route.component;
        const queryParams = { ...(route.queryParams || {}) };
        const params = { ...(route.params || {}) };
        const data = { ...(route.data || {}) };
        const [url] = fullUrl.split('?');
        // Tab identity = hash(url + queryParams). Cùng key = cùng tab, không tạo lại.
        const key = Utilities.hash({ url, queryParams });
        let existedIndex = -1;
        let activatedIndex = -1;
        const currentTabs = this.tabs();
        // QUAN TRỌNG: scan READ-ONLY, KHÔNG mutate tab.isActive trong loop này.
        //
        // Lý do: NavigationEnd có thể fire nhiều lần cho 1 user-action (do nested outlets,
        // redirect, hoặc Angular internal). Vì #activeRoute là async (await getBestInjector),
        // 2 invocations có thể chạy concurrent và interleave với nhau.
        //
        // Nếu mutate tab.isActive = false ở đây, call thứ 2 sẽ thấy isActive đã bị call 1
        // set false rồi → không tìm thấy active tab → activatedIndex stay -1 → splice bỏ qua
        // → tab cũ không bị remove → xuất hiện duplicate tabs.
        //
        // Cách fix: chỉ ĐỌC isActive, sau đó dùng .map() ở dưới để tạo tab objects mới qua spread.
        for (let i = 0; i < currentTabs.length; i++) {
            const tab = currentTabs[i];
            if (tab.key === key) {
                existedIndex = i;
            }
            else if (tab.isActive) {
                activatedIndex = i;
                this.#tabRouterService.pushEvent(tab, SdTabDeactivated);
            }
        }
        const replaceTab = state['replaceTab'];
        // Resolve injector phù hợp với route. Cần xử lý 3 trường hợp:
        // - Standalone route (Angular đã set _injector trên routeConfig sau khi activate)
        // - NgModule lazy load (cần createNgModule từ class)
        // - Fallback: root injector
        const getBestInjector = async (snapshot) => {
            // Standalone route: Angular tự lưu environment injector trên routeConfig._injector
            const routeInjector = snapshot._resolvedGui || snapshot.routeConfig?._injector;
            if (routeInjector)
                return routeInjector;
            // NgModule lazy: phải gọi lại loadChildren() để lấy module class rồi createNgModule
            const loadChildren = snapshot.parent?.routeConfig?.loadChildren;
            if (typeof loadChildren === 'function') {
                let loaded = await loadChildren();
                if (isObservable(loaded)) {
                    loaded = await lastValueFrom(loaded);
                }
                // ES module có thể export default
                if (loaded && typeof loaded === 'object' && 'default' in loaded) {
                    loaded = loaded.default;
                }
                // Angular cũ: NgModuleFactory
                if (loaded instanceof NgModuleFactory) {
                    return loaded.create(this.#injector).injector;
                }
                // Angular mới: NgModule class. Bọc try/catch vì createNgModule throw nếu không phải NgModule.
                if (typeof loaded === 'function' && !Array.isArray(loaded)) {
                    try {
                        return createNgModule(loaded, this.#injector).injector;
                    }
                    catch {
                        return this.#injector;
                    }
                }
            }
            return this.#injector;
        };
        const finalInjector = await getBestInjector(route);
        // Refresh root sau await: lazy route tree có thể hoàn tất trong lúc resolve injector.
        this.#rootRoute = this.#router.routerState.root;
        const activatedRoute = this.#findActivatedRouteForSnapshot(this.#rootRoute, route);
        const newTab = {
            key,
            component,
            injector: new SdOutletInjector(activatedRoute, finalInjector),
            isActive: true,
            url,
            params,
            queryParams,
            data,
            tabInfoChanges: new Subject(),
        };
        // Tạo updatedTabs qua spread thay vì mutate (xem lý do ở for loop phía trên).
        // Với tab có isActive không đổi: giữ nguyên reference (tránh trigger ngComponentOutlet
        // re-evaluate không cần thiết). Với tab cần đổi isActive: tạo object mới qua spread,
        // các nested fields (component, injector) vẫn giữ same reference nên component không bị recreate.
        let updatedTabs = currentTabs.map(tab => {
            if (tab.key === key)
                return tab.isActive ? tab : { ...tab, isActive: true };
            return tab.isActive ? { ...tab, isActive: false } : tab;
        });
        // replaceTab: thay vì mở tab mới song song, xoá tab đang active rồi mở tab mới ở cuối.
        // Use case: từ tab "chi tiết" bấm "chỉnh sửa" với replaceTab → tab chi tiết bị xoá,
        // tab chỉnh sửa thay thế (giữ số tab không tăng).
        if (replaceTab && activatedIndex >= 0) {
            updatedTabs = updatedTabs.filter((_, i) => i !== activatedIndex);
        }
        if (existedIndex >= 0) {
            // Tab đã tồn tại (cùng url + queryParams) → CHỈ activate, KHÔNG thay tab object.
            // Lý do: thay tab object = đổi reference của tab.injector → ngComponentOutlet recreate
            // component → tab bị "reload" mỗi khi click lại hoặc navigate cùng URL.
            //
            // splice phía trên có thể đã shift index nếu activatedIndex < existedIndex.
            const idx = replaceTab && activatedIndex >= 0 && activatedIndex < existedIndex
                ? existedIndex - 1
                : existedIndex;
            this.#tabRouterService.setCurrentTab(updatedTabs[idx]);
            this.#tabRouterService.pushEvent(updatedTabs[idx], SdTabActivated);
            this.tabs.set(updatedTabs);
        }
        else {
            // Tab chưa tồn tại → thêm mới ở cuối.
            this.#tabRouterService.setCurrentTab(newTab);
            this.tabs.set([...updatedTabs, newTab]);
            if (this.tabs().length > 30) {
                this.#sdNotifyService.warning(this.#i18n.t('core.component.tab-router.too-many-tabs'));
            }
        }
        this.tabRouterNav()?.checkUI();
    };
    // Lần xuống deepest firstChild để lấy snapshot của route lá (route thực sự render component).
    #getActivatedRouteSnapshot = (snapshot) => {
        let node = snapshot;
        while (node.firstChild)
            node = node.firstChild;
        return node;
    };
    // DFS match leaf snapshot → ActivatedRoute instance (ổn định hơn so với so sánh component class).
    #findActivatedRouteForSnapshot = (activatedRoute, targetSnapshot) => {
        if (activatedRoute.snapshot === targetSnapshot) {
            return activatedRoute;
        }
        for (const child of activatedRoute.children) {
            const result = this.#findActivatedRouteForSnapshot(child, targetSnapshot);
            if (result) {
                return result;
            }
        }
        return null;
    };
    static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
    static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: SdTabRouterOutletComponent, isStandalone: true, selector: "sd-tab-router-outlet", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "tabRouterNav", first: true, predicate: ["tabRouterNav"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (disabled()) {\n  <div class=\"tab-router__disabled-outlet\">\n    <router-outlet></router-outlet>\n  </div>\n} @else {\n  <sd-tab-router-nav [tabs]=\"tabs()\" #tabRouterNav></sd-tab-router-nav>\n  <div class=\"tab-router__list\">\n    @for (tab of tabs(); track tab.key) {\n      <div class=\"tab-router__pane\" [class.active]=\"tab.isActive\" [id]=\"tab.key\">\n        <div class=\"tab-router__content\">\n          <ng-container *ngComponentOutlet=\"tab.component; injector: tab.injector\"></ng-container>\n        </div>\n      </div>\n    } @empty {\n      <div class=\"tab-router__empty\">\n      </div>\n    }\n  </div>\n}", styles: [":host{display:flex;flex-direction:column;width:100%;min-height:0;height:100vh}:host ::ng-deep .sd-loading{max-width:100%;max-height:100%}.tab-router__list{flex:1;min-height:0}.tab-router__disabled-outlet{flex:1;width:100%;min-height:0;overflow:auto}.tab-router__pane{display:none;position:relative;height:100%;width:100%}.tab-router__pane.active{display:block}.tab-router__content{position:absolute;inset:0;overflow:auto;height:100%;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: SdTabRouterNavComponent, selector: "sd-tab-router-nav", inputs: ["tabs"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SdTabRouterOutletComponent, decorators: [{
            type: Component,
            args: [{ selector: 'sd-tab-router-outlet', standalone: true, imports: [CommonModule, MatIconModule, MatTooltipModule, RouterOutlet, SdTabRouterNavComponent], template: "@if (disabled()) {\n  <div class=\"tab-router__disabled-outlet\">\n    <router-outlet></router-outlet>\n  </div>\n} @else {\n  <sd-tab-router-nav [tabs]=\"tabs()\" #tabRouterNav></sd-tab-router-nav>\n  <div class=\"tab-router__list\">\n    @for (tab of tabs(); track tab.key) {\n      <div class=\"tab-router__pane\" [class.active]=\"tab.isActive\" [id]=\"tab.key\">\n        <div class=\"tab-router__content\">\n          <ng-container *ngComponentOutlet=\"tab.component; injector: tab.injector\"></ng-container>\n        </div>\n      </div>\n    } @empty {\n      <div class=\"tab-router__empty\">\n      </div>\n    }\n  </div>\n}", styles: [":host{display:flex;flex-direction:column;width:100%;min-height:0;height:100vh}:host ::ng-deep .sd-loading{max-width:100%;max-height:100%}.tab-router__list{flex:1;min-height:0}.tab-router__disabled-outlet{flex:1;width:100%;min-height:0;overflow:auto}.tab-router__pane{display:none;position:relative;height:100%;width:100%}.tab-router__pane.active{display:block}.tab-router__content{position:absolute;inset:0;overflow:auto;height:100%;width:100%}\n"] }]
        }], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], tabRouterNav: [{ type: i0.ViewChild, args: ['tabRouterNav', { isSignal: true }] }] } });
// Custom Injector cho từng tab: override ActivatedRoute thành route của TAB ĐÓ
// (không phải route hiện tại của router). Nếu không override, mọi tab sẽ inject ActivatedRoute
// của route đang active → component cũ trong tab inactive nhận data sai khi user navigate.
class SdOutletInjector {
    route;
    parentInjector;
    constructor(route, parentInjector) {
        this.route = route;
        this.parentInjector = parentInjector;
    }
    get(token, notFoundValue) {
        if (token === ActivatedRoute) {
            return this.route || notFoundValue;
        }
        return this.parentInjector.get(token, notFoundValue);
    }
}

function SdTabComponent(builder) {
    return (constructor) => {
        SdTabDecoratorService.tabRouterService
            .pipe(filter(service => service !== undefined && service !== null), take(1))
            .subscribe(service => {
            service.addBuilder(builder);
        });
    };
}

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

export { SdTabComponent, SdTabRouterOutletComponent, SdTabRouterService };
//# sourceMappingURL=sdcorejs-angular-components-tab-router.mjs.map
