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;
};
import { createDynamicSlimmer, InjectVegaSlimmer, MapToComponentField, MapToComponentMethod, } from 'vega-slimmer/core';
import { ElementAppenderSlimmer } from '../../../../helpers/slimmers/element-appender/element-appender-slimmer';
import { FeatureFlag } from '../../../../helpers/feature-control/feature-flag-controller';
import { SynchronizeTaskQueue } from '../../../../helpers/queue/synchronize-task-queue';
import { ClickOutsideOnlyToDismissStrategy } from '../../../../helpers/slimmers/element-appender/trigger-strategies/click-outside-only-to-dismiss-strategy';
import { CalendarDate } from '../../../../helpers/calendar';
import { buildDatePickerAnnouncement } from '../../../../helpers/calendar/utils';
import { getDeepActiveElementInShadow } from '../../../../utils/ui';
import { VegaInternalTranslation } from '../../../../helpers/translation/internal-translation-controller';
import { AriaDialogFocusableTrapSlimmer } from '../../../../helpers/slimmers/aria-dialog-focusable-trap-slimmer';
/** The date picker element appender controller slimmer */
export class VegaDatePickerCalendarOpenStateController extends ElementAppenderSlimmer {
  /**
   * Sets up the configuration the element appender of date picker.
   */
  constructor() {
    super(() => ({
      target: this.vegaDatePickerRenderer.getDatePickerContainer(),
      appendableElement: this.vegaDatePickerCalendarRenderer.getCalendarContentBoxRef(),
      positionRelativeTo: this.dropdownConfig.positionRelativeTo,
      translocation: { X: 0, Y: 8 },
      placement: ['bottom', 'top'],
      alignment: ['start', 'end'],
      isScreenPosition: FeatureFlag.isEnabled('VEGA_DATE_PICKER.ENABLE_CALCULATION_POSITION_BY_SCREEN'),
    }), () => new ClickOutsideOnlyToDismissStrategy(), 'isCalendarShow');
    this.ariaDialogFocusableTrapSlimmer = createDynamicSlimmer(VegaDatePickerCalendarOpenStateController, AriaDialogFocusableTrapSlimmer, [], [{ mapFrom: 'show', mapTo: 'show' }], [
      {
        // eslint-disable-next-line jsdoc/require-jsdoc
        triggerEl: () => this.vegaDatePickerRenderer.getDatePickerContainer(),
        // eslint-disable-next-line jsdoc/require-jsdoc
        dialogEl: () => this.vegaDatePickerCalendarRenderer.getCalendarContentBoxRef(),
      },
    ]);
    this.clearCalendarStateTaskQueue = new SynchronizeTaskQueue();
  }
  /**
   * Since connectedCallback will init element appender again, and it will add `vega-force-hidden` class to calendar,
   *   so we should skip this in mobile side.
   */
  connectedCallback() {
    if (!this.isMobileDevice) {
      super.connectedCallback();
    }
  }
  /**
   * Since it not init the element appender in mobile side, we also not need destroy element appender in disconnectedCallback.
   */
  disconnectedCallback() {
    if (!this.isMobileDevice) {
      super.disconnectedCallback();
    }
  }
  /**
   * Since componentDidLoad will init element appender, and it will add `vega-force-hidden` class to calendar,
   *   so we should skip this in mobile side.
   */
  componentDidLoad() {
    if (!this.isMobileDevice) {
      super.componentDidLoad();
    }
  }
  /**
   * We need to clear the calendar state when the property or state of the vega-date-picker is completed,
   *   such as when the user inputs a range value. we can refill the selecting state in calendar according the new value.
   */
  async componentDidRender() {
    await this.clearCalendarStateTaskQueue.runTask();
  }
  /** doOpen */
  async doOpen() {
    await this.showCalendar();
  }
  /** doClose */
  async doClose() {
    await this.hideCalendar();
  }
  /** watchDisabled */
  async watchDisabled() {
    await this.hideCalendar();
  }
  /** watch calendar show state */
  async watchIsCalendarShow() {
    await this.clearCalendarSelectingState();
  }
  /**
   * Watch isCalendarShow state change to clear selecting state in calendar.
   */
  async showCalendar() {
    if (this.isMobileDevice) {
      await this.vegaDatePickerCalendarRenderer.getMobileModalRef().modal('show');
    }
    else {
      this.show();
    }
  }
  /**
   * Hide calendar in both mobile and desktop mode.
   * After hiding, focuses the input container with a temporary
   * aria-label so the screen reader announces the selection.
   */
  async hideCalendar() {
    if (this.isMobileDevice) {
      await this.vegaDatePickerCalendarRenderer.getMobileModalRef().modal('hide');
    }
    else {
      this.hide();
    }
    this.focusContainerWithAnnouncement();
  }
  /**
   * Update isCalendarShow state.
   *
   * @param {boolean} isShow - is calendar show
   */
  updateIsCalendarShowState(isShow) {
    this.isCalendarShow = isShow;
  }
  /**
   * Focus the input container div with a temporary aria-label.
   * VoiceOver always reads aria-label on programmatic .focus().
   *
   * The role, aria-label, and tabIndex attributes are set imperatively
   * only during the announcement and removed on blur, so they don't
   * interfere with VoiceOver navigation while the calendar is open.
   */
  focusContainerWithAnnouncement() {
    const activeEl = getDeepActiveElementInShadow();
    if ((activeEl === null || activeEl === void 0 ? void 0 : activeEl.matches) && activeEl.matches(':focus-visible')) {
      const container = this.vegaDatePickerRenderer.getDatePickerContainer();
      if (container) {
        const announcement = this.buildAriaAnnouncement(this.value);
        container.setAttribute('role', 'group');
        container.setAttribute('aria-label', announcement);
        container.setAttribute('tabindex', '-1');
        container.addEventListener('blur', () => {
          container.removeAttribute('role');
          container.removeAttribute('aria-label');
          container.removeAttribute('tabindex');
        }, { once: true });
        // Use setTimeout to ensure that the focus happens after the current call stack, allowing the screen reader to process the attribute changes.
        setTimeout(() => container.focus(), 0);
      }
    }
  }
  /**
   * Builds an aria announcement string based on the selected date value.
   *
   * @param {DatePickerValueType<DatePickerModeType>} value The date picker value to build announcement for
   * @returns {string} The aria announcement string
   */
  buildAriaAnnouncement(value) {
    let startDate = null;
    let endDate = null;
    if (this.mode === 'single' && typeof value === 'string') {
      startDate = value ? CalendarDate.fromString(value, this.format) : null;
    }
    else if (this.mode === 'range' && Array.isArray(value)) {
      startDate = value[0] ? CalendarDate.fromString(value[0], this.format) : null;
      endDate = value[1] ? CalendarDate.fromString(value[1], this.format) : null;
    }
    return `
			${buildDatePickerAnnouncement({ startDate, endDate, mode: this.mode })},
			${VegaInternalTranslation.t('date picker')}
		`;
  }
  /**
   * Clear selecting state in calendar.
   */
  async clearCalendarSelectingState() {
    if (!this.isCalendarShow) {
      const calendar = this.vegaDatePickerCalendarRenderer.getCalendarRef();
      await this.clearCalendarStateTaskQueue.enqueue({
        /** Clear the calendar selecting state */
        run: async () => await calendar.reset(),
      }, false);
    }
  }
}
__decorate([
  MapToComponentField()
], VegaDatePickerCalendarOpenStateController.prototype, "dropdownConfig", void 0);
__decorate([
  MapToComponentField()
], VegaDatePickerCalendarOpenStateController.prototype, "vegaDatePickerRenderer", void 0);
__decorate([
  MapToComponentField()
], VegaDatePickerCalendarOpenStateController.prototype, "vegaDatePickerCalendarRenderer", void 0);
__decorate([
  MapToComponentField()
], VegaDatePickerCalendarOpenStateController.prototype, "value", void 0);
__decorate([
  MapToComponentField()
], VegaDatePickerCalendarOpenStateController.prototype, "format", void 0);
__decorate([
  MapToComponentField()
], VegaDatePickerCalendarOpenStateController.prototype, "mode", void 0);
__decorate([
  MapToComponentField({ writable: true })
], VegaDatePickerCalendarOpenStateController.prototype, "isCalendarShow", void 0);
__decorate([
  MapToComponentField()
], VegaDatePickerCalendarOpenStateController.prototype, "isMobileDevice", void 0);
__decorate([
  InjectVegaSlimmer()
], VegaDatePickerCalendarOpenStateController.prototype, "ariaDialogFocusableTrapSlimmer", void 0);
__decorate([
  MapToComponentMethod('componentDidRender')
], VegaDatePickerCalendarOpenStateController.prototype, "componentDidRender", null);
__decorate([
  MapToComponentMethod('doOpen')
], VegaDatePickerCalendarOpenStateController.prototype, "doOpen", null);
__decorate([
  MapToComponentMethod('doClose')
], VegaDatePickerCalendarOpenStateController.prototype, "doClose", null);
__decorate([
  MapToComponentMethod('watchDisabled')
], VegaDatePickerCalendarOpenStateController.prototype, "watchDisabled", null);
__decorate([
  MapToComponentMethod('watchIsCalendarShow')
], VegaDatePickerCalendarOpenStateController.prototype, "watchIsCalendarShow", null);
