// ---------------------------------------------------------------------------
// PanelManager — central manager for panel lifecycle, navigation, and split
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// PanelManager
// ---------------------------------------------------------------------------
export class PanelManager {
    registry = [];
    retainedPanels = new Map();
    _visible = false;
    _splitRatio = 0.6;
    // Two panes for the top/bottom split within the panel area
    topPane = { panels: [], activeIndex: 0 };
    bottomPane = { panels: [], activeIndex: 0 };
    _focusedPane = 'top';
    _verticalSplitRatio = 0.5; // top gets 50% of panel height
    _bottomPaneVisible = false;
    // -------------------------------------------------------------------------
    // Registration
    // -------------------------------------------------------------------------
    registerType(registration) {
        const existing = this.registry.findIndex(r => r.id === registration.id);
        if (existing >= 0) {
            this.registry[existing] = registration;
        }
        else {
            this.registry.push(registration);
        }
    }
    getRegisteredTypes() {
        return [...this.registry];
    }
    getTypesByCategory() {
        const map = new Map();
        for (const reg of this.registry) {
            const list = map.get(reg.category) ?? [];
            list.push(reg);
            map.set(reg.category, list);
        }
        return map;
    }
    prewarmRegistered() {
        for (const registration of this.registry) {
            if (!registration.preload)
                continue;
            if (this.getPanel(registration.id) || this.retainedPanels.has(registration.id))
                continue;
            const panel = registration.factory();
            this.retainedPanels.set(registration.id, panel);
        }
    }
    // -------------------------------------------------------------------------
    // Panel lifecycle — operates on a specific pane (defaults to focused)
    // -------------------------------------------------------------------------
    open(panelId, pane) {
        const existingPane = this._findPaneOf(panelId);
        if (existingPane) {
            this._activateByIdInPane(panelId, existingPane);
            this._visible = true;
            this._focusedPane = existingPane;
            if (existingPane === 'bottom')
                this._bottomPaneVisible = true;
            return this._getPane(existingPane).panels[this._getPane(existingPane).activeIndex];
        }
        const targetPane = pane ?? this._focusedPane;
        const p = this._getPane(targetPane);
        const oldPanel = p.panels[p.activeIndex];
        if (oldPanel)
            oldPanel.onDeactivate();
        const panel = this._obtainPanel(panelId);
        p.panels.push(panel);
        p.activeIndex = p.panels.length - 1;
        this._visible = true;
        // If opening into bottom pane, also make it visible
        if (targetPane === 'bottom') {
            this._bottomPaneVisible = true;
            this._focusedPane = 'bottom';
        }
        else {
            this._focusedPane = 'top';
        }
        panel.onActivate();
        return panel;
    }
    close(panelId) {
        // Search both panes
        for (const which of ['top', 'bottom']) {
            const p = this._getPane(which);
            const index = p.panels.findIndex(panel => panel.id === panelId);
            if (index < 0)
                continue;
            const panel = p.panels[index];
            const wasActive = index === p.activeIndex;
            if (wasActive)
                panel.onDeactivate();
            if (this._shouldRetain(panelId)) {
                this.retainedPanels.set(panelId, panel);
            }
            else {
                panel.onDestroy();
            }
            p.panels.splice(index, 1);
            if (p.panels.length === 0) {
                p.activeIndex = 0;
                if (which === 'bottom') {
                    this._bottomPaneVisible = false;
                    // Move focus to top if we were focused on empty bottom
                    if (this._focusedPane === 'bottom')
                        this._focusedPane = 'top';
                }
            }
            else {
                p.activeIndex = Math.min(p.activeIndex, p.panels.length - 1);
                if (wasActive) {
                    const newActive = p.panels[p.activeIndex];
                    if (newActive)
                        newActive.onActivate();
                }
            }
            // Hide sidebar if no panels remain in either pane
            if (this.topPane.panels.length === 0 && this.bottomPane.panels.length === 0) {
                this._visible = false;
            }
            return;
        }
    }
    /**
     * Move a panel to a specific pane. If panelId is omitted, moves the active
     * panel from the currently focused pane.
     */
    moveToPane(dest, panelId) {
        const srcPaneName = panelId
            ? this._findPaneOf(panelId) ?? this._focusedPane
            : this._focusedPane;
        if (srcPaneName === dest)
            return; // already there
        const dstPaneName = dest;
        this._moveBetweenPanes(srcPaneName, dstPaneName, panelId);
    }
    /**
     * Move a panel to the other pane. If panelId is omitted, moves the active
     * panel from the currently focused pane.
     */
    moveToOtherPane(panelId) {
        const srcPaneName = panelId
            ? this._findPaneOf(panelId) ?? this._focusedPane
            : this._focusedPane;
        const dstPaneName = srcPaneName === 'top' ? 'bottom' : 'top';
        this._moveBetweenPanes(srcPaneName, dstPaneName, panelId);
    }
    // -------------------------------------------------------------------------
    // Navigation — operates on focused pane
    // -------------------------------------------------------------------------
    nextPanel() {
        const p = this._getFocusedPane();
        if (p.panels.length === 0)
            return;
        const oldPanel = p.panels[p.activeIndex];
        if (oldPanel)
            oldPanel.onDeactivate();
        p.activeIndex = (p.activeIndex + 1) % p.panels.length;
        const newPanel = p.panels[p.activeIndex];
        if (newPanel)
            newPanel.onActivate();
    }
    nextWorkspaceTab() {
        this._cycleWorkspaceTab(1);
    }
    prevWorkspaceTab() {
        this._cycleWorkspaceTab(-1);
    }
    prevPanel() {
        const p = this._getFocusedPane();
        if (p.panels.length === 0)
            return;
        const oldPanel = p.panels[p.activeIndex];
        if (oldPanel)
            oldPanel.onDeactivate();
        p.activeIndex = (p.activeIndex - 1 + p.panels.length) % p.panels.length;
        const newPanel = p.panels[p.activeIndex];
        if (newPanel)
            newPanel.onActivate();
    }
    activateByIndex(index) {
        const p = this._getFocusedPane();
        if (index < 0 || index >= p.panels.length)
            return;
        if (index === p.activeIndex)
            return;
        const oldPanel = p.panels[p.activeIndex];
        if (oldPanel)
            oldPanel.onDeactivate();
        p.activeIndex = index;
        const newPanel = p.panels[p.activeIndex];
        if (newPanel)
            newPanel.onActivate();
    }
    activateById(panelId) {
        const which = this._findPaneOf(panelId);
        if (!which)
            return;
        this._activateByIdInPane(panelId, which);
    }
    // -------------------------------------------------------------------------
    // Pane focus control
    // -------------------------------------------------------------------------
    focusPane(pane) {
        if (pane === 'bottom' && !this._bottomPaneVisible)
            return;
        this._focusedPane = pane;
    }
    getFocusedPane() {
        return this._focusedPane;
    }
    /** Get the currently active (focused) panel, or null if none. */
    getActivePanel() {
        const p = this._getFocusedPane();
        return p.panels[p.activeIndex] ?? null;
    }
    togglePaneFocus() {
        if (!this._bottomPaneVisible || this.bottomPane.panels.length === 0)
            return;
        this._focusedPane = this._focusedPane === 'top' ? 'bottom' : 'top';
    }
    // -------------------------------------------------------------------------
    // Pane visibility
    // -------------------------------------------------------------------------
    toggleBottomPane() {
        if (this._bottomPaneVisible) {
            this._bottomPaneVisible = false;
            if (this._focusedPane === 'bottom')
                this._focusedPane = 'top';
        }
        else {
            this._bottomPaneVisible = true;
            // If bottom pane is empty, populate it
            if (this.bottomPane.panels.length === 0) {
                if (this.topPane.panels.length > 1) {
                    // Move last panel from top to bottom
                    const panel = this.topPane.panels.pop();
                    if (this.topPane.activeIndex >= this.topPane.panels.length) {
                        this.topPane.activeIndex = Math.max(0, this.topPane.panels.length - 1);
                    }
                    this.bottomPane.panels.push(panel);
                    this.bottomPane.activeIndex = 0;
                }
                else {
                    // Open a default panel in bottom pane
                    const firstType = this.registry[0];
                    if (firstType) {
                        this.open(firstType.id, 'bottom');
                    }
                }
            }
            this._focusedPane = 'bottom';
        }
    }
    isBottomPaneVisible() {
        return this._bottomPaneVisible && this.bottomPane.panels.length > 0;
    }
    // -------------------------------------------------------------------------
    // Pane state accessors
    // -------------------------------------------------------------------------
    getTopPane() {
        return this.topPane;
    }
    getBottomPane() {
        return this.bottomPane;
    }
    // -------------------------------------------------------------------------
    // Backward-compatible accessors (operate on focused pane)
    // -------------------------------------------------------------------------
    getOpen() {
        const p = this._getFocusedPane();
        return [...p.panels];
    }
    /**
     * Returns all panels across both panes (top then bottom).
     * Use this when you need to know if any panels exist at all.
     */
    getAllOpen() {
        return [...this.topPane.panels, ...this.bottomPane.panels];
    }
    getActive() {
        const p = this._getFocusedPane();
        if (p.panels.length === 0)
            return null;
        return p.panels[p.activeIndex] ?? null;
    }
    getPanel(panelId) {
        return this.topPane.panels.find((panel) => panel.id === panelId)
            ?? this.bottomPane.panels.find((panel) => panel.id === panelId)
            ?? null;
    }
    getPaneOf(panelId) {
        return this._findPaneOf(panelId);
    }
    getWorkspaceTabs() {
        const focusedPanelId = this.getActivePanel()?.id;
        const topTabs = this.topPane.panels.map((panel) => ({
            id: panel.id,
            name: panel.name,
            icon: panel.icon,
            pane: 'top',
            active: panel.id === focusedPanelId,
            focused: panel.id === focusedPanelId,
        }));
        const bottomTabs = this.bottomPane.panels.map((panel) => ({
            id: panel.id,
            name: panel.name,
            icon: panel.icon,
            pane: 'bottom',
            active: panel.id === focusedPanelId,
            focused: panel.id === focusedPanelId,
        }));
        return [...topTabs, ...bottomTabs];
    }
    activateWorkspaceIndex(index) {
        const tabs = this.getWorkspaceTabs();
        if (index < 0 || index >= tabs.length)
            return;
        const tab = tabs[index];
        this._focusedPane = tab.pane;
        if (tab.pane === 'bottom')
            this._bottomPaneVisible = true;
        this._activateByIdInPane(tab.id, tab.pane);
    }
    // -------------------------------------------------------------------------
    // Visibility
    // -------------------------------------------------------------------------
    toggle() {
        this._visible = !this._visible;
        // Auto-open a default panel if toggling visible with nothing open
        if (this._visible && this.topPane.panels.length === 0 && this.bottomPane.panels.length === 0) {
            const defaultPanel = this._getRegistration('panel-list') ?? this.registry[0];
            if (defaultPanel)
                this.open(defaultPanel.id);
        }
    }
    show() {
        this._visible = true;
    }
    hide() {
        this._visible = false;
    }
    isVisible() {
        return this._visible;
    }
    // -------------------------------------------------------------------------
    // Horizontal split control (left/right)
    // -------------------------------------------------------------------------
    getSplitRatio() {
        return this._splitRatio;
    }
    setSplitRatio(ratio) {
        this._splitRatio = Math.max(0.3, Math.min(0.85, ratio));
    }
    widenLeft() {
        this.setSplitRatio(this._splitRatio + 0.05);
    }
    widenRight() {
        this.setSplitRatio(this._splitRatio - 0.05);
    }
    getLeftWidth(totalWidth) {
        return Math.floor(totalWidth * this._splitRatio);
    }
    getRightWidth(totalWidth) {
        return totalWidth - this.getLeftWidth(totalWidth);
    }
    // -------------------------------------------------------------------------
    // Vertical split control (top/bottom within panel area)
    // -------------------------------------------------------------------------
    getVerticalSplitRatio() {
        return this._verticalSplitRatio;
    }
    setVerticalSplitRatio(ratio) {
        this._verticalSplitRatio = Math.max(0.2, Math.min(0.8, ratio));
    }
    // -------------------------------------------------------------------------
    // Cleanup
    // -------------------------------------------------------------------------
    destroyAll() {
        for (const panel of [...this.topPane.panels, ...this.bottomPane.panels, ...this.retainedPanels.values()]) {
            panel.onDestroy();
        }
        this.topPane = { panels: [], activeIndex: 0 };
        this.bottomPane = { panels: [], activeIndex: 0 };
        this.retainedPanels.clear();
        this.registry = [];
        this._focusedPane = 'top';
        this._bottomPaneVisible = false;
        this._visible = false;
    }
    // -------------------------------------------------------------------------
    // Private helpers
    // -------------------------------------------------------------------------
    _getPane(which) {
        return which === 'top' ? this.topPane : this.bottomPane;
    }
    _getFocusedPane() {
        return this._getPane(this._focusedPane);
    }
    _findPaneOf(panelId) {
        if (this.topPane.panels.some(p => p.id === panelId))
            return 'top';
        if (this.bottomPane.panels.some(p => p.id === panelId))
            return 'bottom';
        return null;
    }
    _moveBetweenPanes(srcPaneName, dstPaneName, panelId) {
        const src = this._getPane(srcPaneName);
        const dst = this._getPane(dstPaneName);
        const id = panelId ?? src.panels[src.activeIndex]?.id;
        if (!id)
            return;
        const index = src.panels.findIndex(p => p.id === id);
        if (index < 0)
            return;
        const panel = src.panels[index];
        const wasActive = index === src.activeIndex;
        if (wasActive)
            panel.onDeactivate();
        src.panels.splice(index, 1);
        src.activeIndex = Math.min(src.activeIndex, Math.max(0, src.panels.length - 1));
        if (wasActive && src.panels.length > 0) {
            src.panels[src.activeIndex]?.onActivate();
        }
        // Deactivate current active in dest
        const oldDstActive = dst.panels[dst.activeIndex];
        if (oldDstActive)
            oldDstActive.onDeactivate();
        dst.panels.push(panel);
        dst.activeIndex = dst.panels.length - 1;
        panel.onActivate();
        if (dstPaneName === 'bottom') {
            this._bottomPaneVisible = true;
        }
        this._focusedPane = dstPaneName;
    }
    _cycleWorkspaceTab(direction) {
        const tabs = this.getWorkspaceTabs();
        if (tabs.length === 0)
            return;
        const currentIndex = tabs.findIndex((tab) => tab.focused);
        const nextIndex = currentIndex < 0
            ? 0
            : (currentIndex + direction + tabs.length) % tabs.length;
        this.activateWorkspaceIndex(nextIndex);
    }
    _obtainPanel(panelId) {
        const retained = this.retainedPanels.get(panelId);
        if (retained) {
            this.retainedPanels.delete(panelId);
            return retained;
        }
        const registration = this._getRegistration(panelId);
        if (!registration) {
            throw new Error(`No panel type registered with id: ${panelId}`);
        }
        return registration.factory();
    }
    _getRegistration(panelId) {
        return this.registry.find((registration) => registration.id === panelId);
    }
    _shouldRetain(panelId) {
        return this._getRegistration(panelId)?.preload === true;
    }
    _activateByIdInPane(panelId, which) {
        const p = this._getPane(which);
        const index = p.panels.findIndex(panel => panel.id === panelId);
        if (index >= 0 && index !== p.activeIndex) {
            const oldPanel = p.panels[p.activeIndex];
            if (oldPanel)
                oldPanel.onDeactivate();
            p.activeIndex = index;
            const newPanel = p.panels[p.activeIndex];
            if (newPanel)
                newPanel.onActivate();
        }
    }
}
