/**
 * @license
 * Copyright (c) 2016 - 2026 Vaadin Ltd.
 * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
 */
import { addListener } from '@vaadin/component-base/src/gestures.js';

/**
 * @polymerMixin
 */
export const ColumnResizingMixin = (superClass) =>
  class ColumnResizingMixin extends superClass {
    /** @protected */
    ready() {
      super.ready();
      const scroller = this.$.scroller;
      addListener(scroller, 'track', this._onHeaderTrack.bind(this));

      // Cancel resizing on multi-touch (e.g. pinch-zoom)
      scroller.addEventListener('touchstart', (e) => {
        if (e.touches.length > 1) {
          scroller.removeAttribute('column-resizing');
        }
      });

      // Disallow scrolling while resizing, but allow multi-touch gestures
      scroller.addEventListener('touchmove', (e) => {
        if (e.touches.length > 1) {
          scroller.removeAttribute('column-resizing');
          return;
        }
        if (scroller.hasAttribute('column-resizing')) {
          e.preventDefault();
        }
      });

      // Disable contextmenu on any resize separator.
      scroller.addEventListener('contextmenu', (e) => e.target.part.contains('resize-handle') && e.preventDefault());

      // Disable native cell focus when resizing
      scroller.addEventListener('mousedown', (e) => e.target.part.contains('resize-handle') && e.preventDefault());
    }

    /** @private */
    _onHeaderTrack(e) {
      const handle = e.target;
      if (handle.part.contains('resize-handle')) {
        // Ignore track events after multi-touch cancelled resizing
        if (e.detail.state !== 'start' && !this.$.scroller.hasAttribute('column-resizing')) {
          return;
        }

        const cell = handle.parentElement;
        let column = cell._column;

        this.$.scroller.toggleAttribute('column-resizing', true);

        // Get the target column to resize
        while (column.localName === 'vaadin-grid-column-group') {
          column = column._childColumns
            .slice(0)
            .sort((a, b) => a._order - b._order)
            .filter((column) => !column.hidden)
            .pop();
        }

        const isRTL = this.__isRTL;
        const eventX = e.detail.x;
        const columnRowCells = Array.from(this.$.header.querySelectorAll('[part~="row"]:last-child [part~="cell"]'));
        const targetCell = columnRowCells.find((cell) => cell._column === column);
        // Resize the target column
        if (targetCell.offsetWidth) {
          const style = getComputedStyle(targetCell._content);
          const minWidth =
            10 +
            parseInt(style.paddingLeft) +
            parseInt(style.paddingRight) +
            parseInt(style.borderLeftWidth) +
            parseInt(style.borderRightWidth) +
            parseInt(style.marginLeft) +
            parseInt(style.marginRight);

          let maxWidth;

          const cellWidth = targetCell.offsetWidth;
          const cellRect = targetCell.getBoundingClientRect();

          // For cells frozen to end, resize handle is flipped horizontally.
          if (targetCell.hasAttribute('frozen-to-end')) {
            maxWidth = cellWidth + (isRTL ? eventX - cellRect.right : cellRect.left - eventX);
          } else {
            maxWidth = cellWidth + (isRTL ? cellRect.left - eventX : eventX - cellRect.right);
          }

          column.width = `${Math.max(minWidth, maxWidth)}px`;
          column.flexGrow = 0;
        }
        // Fix width and flex-grow for all preceding columns
        columnRowCells.slice(0, columnRowCells.indexOf(targetCell)).forEach((cell) => {
          cell._column.width = `${cell.offsetWidth}px`;
          cell._column.flexGrow = 0;
        });

        const cellFrozenToEnd = this._frozenToEndCells[0];

        // When handle moves below the cell frozen to end, scroll into view.
        if (cellFrozenToEnd && this.$.table.scrollWidth > this.$.table.offsetWidth) {
          const frozenRect = cellFrozenToEnd.getBoundingClientRect();
          const offset = eventX - (isRTL ? frozenRect.right : frozenRect.left);

          if ((isRTL && offset <= 0) || (!isRTL && offset >= 0)) {
            this.$.table.scrollLeft += offset;
          }
        }

        if (e.detail.state === 'end') {
          this.$.scroller.toggleAttribute('column-resizing', false);
          this.dispatchEvent(
            new CustomEvent('column-resize', {
              detail: { resizedColumn: column },
            }),
          );
        }

        // Notify resize
        this._resizeHandler();
      }
    }

    /**
     * Fired when a column in the grid is resized by the user.
     *
     * @event column-resize
     * @param {Object} detail
     * @param {Object} detail.resizedColumn the column that was resized
     */
  };
