import { Directive, ChangeDetectorRef, ViewChild, ViewContainerRef, inject } from '@angular/core';
import { BaseResourceComponent, BaseDashboard } from '@memberjunction/ng-shared';
import { MJGlobal } from '@memberjunction/global';
import { DashboardEngine } from '@memberjunction/core-entities';
import * as i0 from "@angular/core";
const _c0 = ["contentHost"];
/**
 * Shared base for all Admin app container resources. Subclasses declare:
 *   - ContainerTitle / ContainerIcon / ContainerSubtitle
 *   - Sections[]
 *   - @RegisterClass(BaseResourceComponent, '...')
 *
 * The base handles: left-nav state, URL deep-linking via NavigationService,
 * dynamic component instantiation for both resource and dashboard sections,
 * teardown on switch.
 */
export class BaseAdminContainerComponent extends BaseResourceComponent {
    contentHost;
    ActiveSection = '';
    LoadError = null;
    IsLoading = false;
    /**
     * Cache of section.id → ComponentRef. Once a sub-section is rendered we
     * keep its component alive across switches by detaching + reattaching the
     * view (instead of destroying + recreating). This preserves state — Event
     * Monitor's captured events, GraphQL Console's history, query inputs,
     * scroll position, etc. — and avoids expensive re-init.
     */
    cache = new Map();
    currentSectionId = null;
    cdr = inject(ChangeDetectorRef);
    async ngOnInit() {
        super.ngOnInit();
        const params = this.GetQueryParams();
        const requested = params['section'];
        const initial = this.findSection(requested)?.id ?? this.Sections[0]?.id ?? '';
        const target = this.findSection(initial);
        if (target) {
            await this.selectSection(target, /*syncUrl*/ false);
        }
        if (!requested && this.ActiveSection) {
            this.UpdateQueryParams({ section: this.ActiveSection });
        }
        this.NotifyLoadComplete();
    }
    ngOnDestroy() {
        for (const ref of this.cache.values()) {
            try {
                ref.destroy();
            }
            catch { /* ignore */ }
        }
        this.cache.clear();
        this.currentSectionId = null;
        super.ngOnDestroy();
    }
    async GetResourceDisplayName() { return this.ContainerTitle; }
    async GetResourceIconClass() { return this.ContainerIcon; }
    /**
     * Single-section view of `Sections` shaped for `<mj-left-nav>`. AdminSection
     * already has the required id / label / icon / description fields, so it's
     * structurally compatible with MJLeftNavItem.
     */
    get NavSections() {
        return [{ items: this.Sections }];
    }
    /** Adapter from `<mj-left-nav>`'s ItemClicked event back to `selectSection`. */
    OnNavItemClicked(item) {
        const section = this.Sections.find(s => s.id === item.id);
        if (section) {
            void this.OnSectionClick(section);
        }
    }
    /** Called by the framework on browser back/forward + deep-link entry. */
    OnQueryParamsChanged(params, _source) {
        const section = params['section'];
        if (!section || section === this.ActiveSection)
            return;
        const target = this.findSection(section);
        if (target) {
            void this.selectSection(target, /*syncUrl*/ false);
        }
    }
    async OnSectionClick(section) {
        if (section.id === this.ActiveSection)
            return;
        await this.selectSection(section, /*syncUrl*/ true);
    }
    // ---------- protected ----------
    async selectSection(section, syncUrl) {
        // Guard against duplicate calls for the same section. Without this,
        // ngOnInit's own GetQueryParams read races with the workspace stream's
        // synchronous replay through OnQueryParamsChanged on first mount —
        // both call selectSection with the same target, both reach
        // createComponent, and two instances of the sub-page end up mounted
        // in the contentHost. OnSectionClick + OnQueryParamsChanged each
        // guard their own call site, but defense-in-depth here covers any
        // future caller (and the ngOnInit ↔ stream-replay race specifically).
        if (section.id === this.ActiveSection) {
            return;
        }
        this.ActiveSection = section.id;
        this.IsLoading = true;
        this.LoadError = null;
        this.cdr.markForCheck();
        if (syncUrl) {
            this.UpdateQueryParams({ section: section.id });
        }
        try {
            await this.renderSection(section);
        }
        catch (err) {
            this.LoadError = err instanceof Error ? err.message : String(err);
        }
        finally {
            this.IsLoading = false;
            this.cdr.markForCheck();
        }
    }
    async renderSection(section) {
        // Detach (don't destroy) the currently visible sub-component so it
        // stays alive in memory with its state intact.
        this.detachCurrent();
        // Reattach if cached
        const cached = this.cache.get(section.id);
        if (cached) {
            this.contentHost.insert(cached.hostView);
            this.currentSectionId = section.id;
            return;
        }
        // Otherwise, create fresh
        const ref = section.source.kind === 'resource'
            ? await this.createResourceRef(section.source.driverClass)
            : await this.createDashboardRef(section.source.dashboardName);
        if (ref) {
            this.cache.set(section.id, ref);
            this.currentSectionId = section.id;
        }
    }
    detachCurrent() {
        if (!this.currentSectionId)
            return;
        const cur = this.cache.get(this.currentSectionId);
        if (!cur)
            return;
        const idx = this.contentHost.indexOf(cur.hostView);
        if (idx >= 0)
            this.contentHost.detach(idx);
    }
    async createResourceRef(driverClass) {
        const reg = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseResourceComponent, driverClass);
        if (!reg) {
            this.LoadError = `Component "${driverClass}" is not registered. Make sure its module is imported.`;
            return null;
        }
        const ref = this.contentHost.createComponent(reg.SubClass);
        const instance = ref.instance;
        if (this.Data)
            instance.Data = this.Data;
        return ref;
    }
    async createDashboardRef(dashboardName) {
        await DashboardEngine.Instance.Config(false);
        const dashboard = DashboardEngine.Instance.Dashboards.find(d => d.Name === dashboardName);
        if (!dashboard) {
            this.LoadError = `Dashboard "${dashboardName}" was not found in metadata.`;
            return null;
        }
        if (dashboard.Type !== 'Code') {
            this.LoadError = `Dashboard "${dashboardName}" has type "${dashboard.Type}" — only Code dashboards can be embedded here.`;
            return null;
        }
        if (!dashboard.DriverClass) {
            this.LoadError = `Dashboard "${dashboardName}" has no DriverClass set.`;
            return null;
        }
        const reg = await MJGlobal.Instance.ClassFactory.GetRegistrationAsync(BaseDashboard, dashboard.DriverClass);
        if (!reg) {
            this.LoadError = `Driver class "${dashboard.DriverClass}" is not registered against BaseDashboard.`;
            return null;
        }
        const ref = this.contentHost.createComponent(reg.SubClass);
        const instance = ref.instance;
        instance.Config = { dashboard, userState: undefined };
        instance.Refresh();
        return ref;
    }
    findSection(id) {
        if (!id)
            return undefined;
        return this.Sections.find(s => s.id === id);
    }
    static ɵfac = /*@__PURE__*/ (() => { let ɵBaseAdminContainerComponent_BaseFactory; return function BaseAdminContainerComponent_Factory(__ngFactoryType__) { return (ɵBaseAdminContainerComponent_BaseFactory || (ɵBaseAdminContainerComponent_BaseFactory = i0.ɵɵgetInheritedFactory(BaseAdminContainerComponent)))(__ngFactoryType__ || BaseAdminContainerComponent); }; })();
    static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: BaseAdminContainerComponent, viewQuery: function BaseAdminContainerComponent_Query(rf, ctx) { if (rf & 1) {
            i0.ɵɵviewQuery(_c0, 7, ViewContainerRef);
        } if (rf & 2) {
            let _t;
            i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.contentHost = _t.first);
        } }, features: [i0.ɵɵInheritDefinitionFeature] });
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(BaseAdminContainerComponent, [{
        type: Directive
    }], null, { contentHost: [{
            type: ViewChild,
            args: ['contentHost', { read: ViewContainerRef, static: true }]
        }] }); })();
//# sourceMappingURL=base-admin-container.component.js.map