// #region Imports
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { HTMLElementEventEmitter } from '@breadstone/mosaik-elements';
import { DropListController } from '../../../../DragDrop';
import { Focusable } from '../../../Behaviors/Focusable';
import { Gapable } from '../../../Behaviors/Gapable';
import { Sizeable } from '../../../Behaviors/Sizeable';
import { Slottable } from '../../../Behaviors/Slottable';
import { Themeable } from '../../../Behaviors/Themeable';
import { addInheritanceProviderController } from '../../../Controllers/InheritanceProviderController';
import { InlineStyleController } from '../../../Controllers/InlineStyleController';
import { addSlotController } from '../../../Controllers/SlotController';
import { Attribute } from '../../../Decorators/AttributeDecorator';
import { Component } from '../../../Decorators/ComponentDecorator';
import { Watch } from '../../../Decorators/WatchDecorator';
import { FlowBoardDisplayMode } from '../../../Types/FlowBoardDisplayMode';
import { FlowBoardScrollMode } from '../../../Types/FlowBoardScrollMode';
import { CustomElement } from '../../Abstracts/CustomElement';
import { FlowBoardColumnComposerElement } from './FlowBoardColumnComposerElement';
import { FlowBoardColumnElement } from './FlowBoardColumnElement';
import { flowBoardElementTemplate } from './FlowBoardElementTemplate';
import { FLOW_BOARD_INHERITANCE_CONTEXT } from './FlowBoardInheritanceContext';
import { IFlowBoardElementProps } from './IFlowBoardElementProps';
import { flowBoardElementCosmopolitanStyle } from './Themes/FlowBoardElement.Cosmopolitan';
import { flowBoardElementJoyStyle } from './Themes/FlowBoardElement.Joy';
import { flowBoardElementMemphisStyle } from './Themes/FlowBoardElement.Memphis';
// #endregion
/**
 * FlowBoard - A Kanban-style board container for organizing columns and items.
 *
 * @description
 * The FlowBoard component provides a horizontal container for organizing content
 * into columns. It supports drag-and-drop reordering of columns and items between
 * columns. The board can operate in different display modes (tasks, pipeline) that
 * affect the density and layout of items.
 *
 * @name FlowBoard
 * @element mosaik-flow-board
 * @category Layouts
 *
 * @slot - Default slot for mosaik-flow-board-column elements
 * @slot append - Slot for add column action (mosaik-flow-board-column-composer)
 *
 * @csspart viewport - The scrollable viewport container
 * @csspart columns - The columns container
 * @csspart append - The append slot container
 *
 * @cssprop {Color} --flow-board-background-color - Background color of the board
 * @cssprop {String} --flow-board-gap - Gap between columns
 * @cssprop {String} --flow-board-padding - Padding around the viewport
 *
 * @dependency {FlowBoardColumnElement} - Column component
 * @dependency {FlowBoardColumnComposerElement} - Column composer component for adding new columns
 *
 * @fires flowBoardBeforeColumnReorder {FlowBoardBeforeColumnReorderEvent} - Before column reorder (cancelable)
 * @fires flowBoardColumnReorder {FlowBoardColumnReorderEvent} - After column reorder
 * @fires flowBoardBeforeItemReorder {FlowBoardBeforeItemReorderEvent} - Before item reorder within column (cancelable)
 * @fires flowBoardItemReorder {FlowBoardItemReorderEvent} - After item reorder within column
 * @fires flowBoardBeforeItemMove {FlowBoardBeforeItemMoveEvent} - Before item move between columns (cancelable)
 * @fires flowBoardItemMove {FlowBoardItemMoveEvent} - After item move between columns
 * @fires flowBoardDragStart {FlowBoardDragStartEvent} - When a drag operation starts (column or item)
 *
 * @example
 * Basic board with columns:
 * ```html
 * <mosaik-flow-board dragdrop display-mode="tasks">
 *   <mosaik-flow-board-column column-key="todo">
 *     <span slot="title">To Do</span>
 *     <mosaik-flow-board-column-item item-key="item-1">
 *       <span slot="title">Task 1</span>
 *     </mosaik-flow-board-column-item>
 *   </mosaik-flow-board-column>
 *   <mosaik-flow-board-column-composer slot="append"></mosaik-flow-board-column-composer>
 * </mosaik-flow-board>
 * ```
 *
 * @public
 */
let FlowBoardElement = class FlowBoardElement extends Themeable(Sizeable(Gapable(Slottable(Focusable(CustomElement))))) {
    // #region Fields
    _displayMode;
    _dragdrop;
    _scrollMode;
    _flowBoardBeforeColumnReorder;
    _flowBoardColumnReorder;
    _flowBoardBeforeItemReorder;
    _flowBoardItemReorder;
    _flowBoardBeforeItemMove;
    _flowBoardItemMove;
    _flowBoardDragStart;
    _provider;
    _slotController;
    _inlineStyleController;
    _columnDropList;
    // #endregion
    // #region Ctor
    constructor() {
        super();
        this._displayMode = IFlowBoardElementProps.DEFAULTS.displayMode;
        this._dragdrop = IFlowBoardElementProps.DEFAULTS.dragdrop;
        this._scrollMode = IFlowBoardElementProps.DEFAULTS.scrollMode;
        this._flowBoardBeforeColumnReorder = new HTMLElementEventEmitter(this, 'flowBoardBeforeColumnReorder');
        this._flowBoardColumnReorder = new HTMLElementEventEmitter(this, 'flowBoardColumnReorder');
        this._flowBoardBeforeItemReorder = new HTMLElementEventEmitter(this, 'flowBoardBeforeItemReorder');
        this._flowBoardItemReorder = new HTMLElementEventEmitter(this, 'flowBoardItemReorder');
        this._flowBoardBeforeItemMove = new HTMLElementEventEmitter(this, 'flowBoardBeforeItemMove');
        this._flowBoardItemMove = new HTMLElementEventEmitter(this, 'flowBoardItemMove');
        this._flowBoardDragStart = new HTMLElementEventEmitter(this, 'flowBoardDragStart');
        this._provider = addInheritanceProviderController(this, {
            context: FLOW_BOARD_INHERITANCE_CONTEXT,
            values: () => ({
                dragdrop: this.dragdrop,
                displayMode: this.displayMode,
                scrollMode: this.scrollMode
            })
        });
        this._slotController = addSlotController(this);
        this._inlineStyleController = new InlineStyleController(this);
        this._columnDropList = new DropListController(this, {
            orientation: 'horizontal',
            disabled: !this._dragdrop
        });
    }
    // #endregion
    // #region Properties
    /**
     * Returns the `is` property.
     *
     * @public
     * @static
     * @readonly
     */
    static get is() {
        return 'mosaik-flow-board';
    }
    /**
     * Gets or sets the display mode affecting density and layout.
     *
     * @public
     * @attr display-mode
     */
    get displayMode() {
        return this._displayMode;
    }
    set displayMode(value) {
        if (this._displayMode !== value) {
            this._displayMode = value;
            this.requestUpdate('displayMode');
        }
    }
    /**
     * Gets or sets whether drag and drop is enabled.
     *
     * @public
     * @attr dragdrop
     */
    get dragdrop() {
        return this._dragdrop;
    }
    set dragdrop(value) {
        if (this._dragdrop !== value) {
            this._dragdrop = value;
            this.requestUpdate('dragdrop');
        }
    }
    /**
     * Gets or sets the scroll mode for the board.
     *
     * @public
     * @attr scroll-mode
     */
    get scrollMode() {
        return this._scrollMode;
    }
    set scrollMode(value) {
        if (this._scrollMode !== value) {
            this._scrollMode = value;
            this.requestUpdate('scrollMode');
        }
    }
    // #endregion
    // #region Methods
    /**
     * @public
     * @override
     */
    connectedCallback() {
        super.connectedCallback();
        this.addEventListener('drop', this.handleDrop);
        this.on('flowBoardBeforeColumnReorder', (e) => {
            if (e.detail.cancel) {
                e.preventDefault();
            }
        });
        this.on('flowBoardBeforeItemReorder', (e) => {
            if (e.detail.cancel) {
                e.preventDefault();
            }
        });
        this.on('flowBoardBeforeItemMove', (e) => {
            if (e.detail.cancel) {
                e.preventDefault();
            }
        });
    }
    /**
     * @public
     * @override
     */
    disconnectedCallback() {
        super.disconnectedCallback();
        this.removeEventListener('drop', this.handleDrop);
    }
    /**
     * Gets all column elements in the board.
     *
     * @public
     * @returns Array of FlowBoardColumnElement instances.
     */
    getColumns() {
        const slot = this.shadowRoot?.querySelector('slot:not([name])');
        if (!slot) {
            return [];
        }
        return slot.assignedElements({ flatten: true })
            .filter((el) => el instanceof FlowBoardColumnElement);
    }
    /**
     * Gets a column by its key.
     *
     * @public
     * @param columnKey The column key to find.
     * @returns The column element or undefined if not found.
     */
    getColumnByKey(columnKey) {
        return this.getColumns().find((col) => col.columnKey === columnKey);
    }
    /**
     * Handles default slot changes to update column references.
     *
     * @internal
     */
    onDefaultSlotChange(_event) {
        // Update provider when columns change
        this._provider.update();
        if (this._dragdrop) {
            this.syncDragDrop();
        }
    }
    /**
     * Moves an item between columns programmatically.
     *
     * @public
     * @param itemKey The key of the item to move.
     * @param fromColumnKey The source column key.
     * @param toColumnKey The target column key.
     * @param toIndex Optional target index in the target column.
     * @returns True if the move was successful.
     */
    moveItem(itemKey, fromColumnKey, toColumnKey, toIndex) {
        const sourceColumn = this.getColumnByKey(fromColumnKey);
        const targetColumn = this.getColumnByKey(toColumnKey);
        if (!sourceColumn || !targetColumn) {
            return false;
        }
        const item = sourceColumn.getItems().find((i) => i.itemKey === itemKey);
        if (!item) {
            return false;
        }
        const fromIndex = Array.from(sourceColumn.getItems()).indexOf(item);
        const finalToIndex = toIndex ?? targetColumn.getItems().length;
        const beforeDetail = {
            itemKey: itemKey,
            fromColumnKey: fromColumnKey,
            toColumnKey: toColumnKey,
            fromIndex: fromIndex,
            toIndex: finalToIndex,
            pinnedOffset: 0,
            toRegion: 'normal',
            cancel: false
        };
        this._flowBoardBeforeItemMove.emit(beforeDetail);
        if (beforeDetail.cancel) {
            return false;
        }
        const items = targetColumn.getItems();
        if (finalToIndex >= items.length) {
            targetColumn.appendChild(item);
        }
        else {
            targetColumn.insertBefore(item, items[finalToIndex]);
        }
        this._flowBoardItemMove.emit({
            itemKey: itemKey,
            fromColumnKey: fromColumnKey,
            toColumnKey: toColumnKey,
            fromIndex: fromIndex,
            toIndex: finalToIndex
        });
        return true;
    }
    /**
     * Reorders a column to a new index.
     *
     * @public
     * @param columnKey The key of the column to reorder.
     * @param newIndex The target index.
     * @returns True if the reorder was successful.
     */
    reorderColumn(columnKey, newIndex) {
        const columns = this.getColumns();
        const column = columns.find((c) => c.columnKey === columnKey);
        if (!column || column.pinned) {
            return false;
        }
        const currentIndex = columns.indexOf(column);
        if (currentIndex === newIndex) {
            return true;
        }
        const beforeDetail = {
            columnKey: columnKey,
            fromIndex: currentIndex,
            toIndex: newIndex,
            cancel: false
        };
        this._flowBoardBeforeColumnReorder.emit(beforeDetail);
        if (beforeDetail.cancel) {
            return false;
        }
        const targetColumn = columns[newIndex];
        if (newIndex > currentIndex) {
            targetColumn.after(column);
        }
        else {
            targetColumn.before(column);
        }
        this._flowBoardColumnReorder.emit({
            columnKey: columnKey,
            fromIndex: currentIndex,
            toIndex: newIndex
        });
        return true;
    }
    onDragdropChanged(_prev, next) {
        this._columnDropList.disabled = !next;
        if (next) {
            this.syncDragDrop();
        }
    }
    onDisplayModeChanged() {
        this._provider.update();
        this._inlineStyleController.set('flow-board-display-mode', this.displayMode);
    }
    onScrollModeChanged() {
        this._provider.update();
        this._inlineStyleController.set('flow-board-scroll-mode', this.scrollMode);
    }
    /**
     * Discovers column DragRefs and registers them with the column DropList.
     * Also connects column-level item DropLists for cross-column item moves.
     *
     * @private
     */
    syncDragDrop() {
        const columns = this.getColumns();
        // 1. Register column DragRefs with the board-level DropListController
        const columnDragRefs = columns
            .map((col) => col.getDragRef())
            .filter((ref) => ref !== null);
        this._columnDropList.withItems(columnDragRefs);
        // 2. Connect all column-level item DropListRefs so items can be moved between columns
        const columnDropListRefs = columns
            .map((col) => col.getDropListRef())
            .filter((ref) => ref !== null);
        for (const col of columns) {
            const ref = col.getDropListRef();
            if (ref) {
                ref.connectTo(columnDropListRefs.filter((r) => r !== ref));
            }
        }
    }
    /**
     * Handles `drop` events from the DnD system for both column reorder and item drops.
     *
     * @private
     */
    handleDrop = (event) => {
        const detail = event.detail;
        if (detail.container === this._columnDropList.dropListRef) {
            this.handleColumnDrop(detail);
        }
        else {
            this.handleItemDrop(detail);
        }
    };
    /**
     * Handles column reorder from a drop event.
     *
     * @private
     */
    handleColumnDrop(detail) {
        const { previousIndex, currentIndex } = detail;
        if (previousIndex === currentIndex) {
            return;
        }
        const columns = this.getColumns();
        const column = columns[previousIndex];
        if (!column) {
            return;
        }
        this._flowBoardDragStart.emit({
            type: 'column',
            columnKey: column.columnKey
        });
        const beforeDetail = {
            columnKey: column.columnKey,
            fromIndex: previousIndex,
            toIndex: currentIndex,
            cancel: false
        };
        this._flowBoardBeforeColumnReorder.emit(beforeDetail);
        if (beforeDetail.cancel) {
            return;
        }
        // Move the column DOM element
        const targetColumn = columns[currentIndex];
        if (targetColumn) {
            if (currentIndex > previousIndex) {
                targetColumn.after(column);
            }
            else {
                targetColumn.before(column);
            }
        }
        this._flowBoardColumnReorder.emit({
            columnKey: column.columnKey,
            fromIndex: previousIndex,
            toIndex: currentIndex
        });
    }
    /**
     * Handles item reorder or cross-column move from a drop event.
     *
     * @private
     */
    handleItemDrop(detail) {
        const { previousIndex, currentIndex, previousContainer, container } = detail;
        const item = detail.item;
        if (!item) {
            return;
        }
        const itemElement = item.element;
        const itemKey = itemElement.itemKey;
        // Determine source and target columns from the DropListRef elements
        const sourceColumnElement = previousContainer?.element;
        const targetColumnElement = container?.element;
        if (!sourceColumnElement || !targetColumnElement) {
            return;
        }
        const fromColumnKey = sourceColumnElement.columnKey;
        const toColumnKey = targetColumnElement.columnKey;
        this._flowBoardDragStart.emit({
            type: 'item',
            itemKey: itemKey,
            fromColumnKey: fromColumnKey
        });
        if (previousContainer === container) {
            // Same column → item reorder
            if (previousIndex === currentIndex) {
                return;
            }
            const beforeDetail = {
                itemKey: itemKey,
                columnKey: fromColumnKey,
                fromIndex: previousIndex,
                toIndex: currentIndex,
                pinnedOffset: 0,
                region: 'normal',
                cancel: false
            };
            this._flowBoardBeforeItemReorder.emit(beforeDetail);
            if (beforeDetail.cancel) {
                return;
            }
            // Move item DOM element within the column
            const items = targetColumnElement.getItems();
            const targetItem = items[currentIndex];
            if (targetItem) {
                if (currentIndex > previousIndex) {
                    targetItem.after(itemElement);
                }
                else {
                    targetItem.before(itemElement);
                }
            }
            this._flowBoardItemReorder.emit({
                itemKey: itemKey,
                columnKey: fromColumnKey,
                fromIndex: previousIndex,
                toIndex: currentIndex
            });
        }
        else {
            // Cross-column → item move
            const beforeDetail = {
                itemKey: itemKey,
                fromColumnKey: fromColumnKey,
                toColumnKey: toColumnKey,
                fromIndex: previousIndex,
                toIndex: currentIndex,
                pinnedOffset: 0,
                toRegion: 'normal',
                cancel: false
            };
            this._flowBoardBeforeItemMove.emit(beforeDetail);
            if (beforeDetail.cancel) {
                return;
            }
            // Move item DOM element to the target column
            const targetItems = targetColumnElement.getItems();
            if (currentIndex >= targetItems.length) {
                targetColumnElement.appendChild(itemElement);
            }
            else {
                targetColumnElement.insertBefore(itemElement, targetItems[currentIndex]);
            }
            this._flowBoardItemMove.emit({
                itemKey: itemKey,
                fromColumnKey: fromColumnKey,
                toColumnKey: toColumnKey,
                fromIndex: previousIndex,
                toIndex: currentIndex
            });
        }
    }
};
__decorate([
    Attribute({ type: FlowBoardDisplayMode }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], FlowBoardElement.prototype, "displayMode", null);
__decorate([
    Attribute({ type: Boolean }),
    __metadata("design:type", Boolean),
    __metadata("design:paramtypes", [Boolean])
], FlowBoardElement.prototype, "dragdrop", null);
__decorate([
    Attribute({ type: String }),
    __metadata("design:type", String),
    __metadata("design:paramtypes", [String])
], FlowBoardElement.prototype, "scrollMode", null);
__decorate([
    Watch('dragdrop'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Boolean, Boolean]),
    __metadata("design:returntype", void 0)
], FlowBoardElement.prototype, "onDragdropChanged", null);
__decorate([
    Watch('displayMode'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], FlowBoardElement.prototype, "onDisplayModeChanged", null);
__decorate([
    Watch('scrollMode'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], FlowBoardElement.prototype, "onScrollModeChanged", null);
FlowBoardElement = __decorate([
    Component({
        selector: 'mosaik-flow-board',
        template: flowBoardElementTemplate,
        themes: {
            joy: flowBoardElementJoyStyle,
            cosmopolitan: flowBoardElementCosmopolitanStyle,
            memphis: flowBoardElementMemphisStyle
        },
        options: {
            mode: 'open',
            delegatesFocus: true
        },
        imports: [
            FlowBoardColumnElement,
            FlowBoardColumnComposerElement
        ],
        host: {
            'attr.role': 'region',
            'attr.aria-label': 'Flow board'
        }
    }),
    __metadata("design:paramtypes", [])
], FlowBoardElement);
export { FlowBoardElement };
//# sourceMappingURL=FlowBoardElement.js.map