import { OjSelectManyBase } from './OjSelectManyBase';
import { executeWithModules } from '../../lib/oj-module-proxy';

/**
 * The component WebElement for [oj-select-many](../../jsdocs/oj.ojSelectMany.html).
 * Do not instantiate this class directly, instead, use
 * [ojSelectMany](../functions/elements.ojSelectMany-1.html).
 */
export class OjSelectMany extends OjSelectManyBase {
  /**
   * Sets the value of "value" property.
   * The value of the element.
   * @param value The value to set for "value"
   * @override
   */
  async changeValue(value: Array<any> | null) {
    // Call focus() on the root element
    await this.getDriver().executeScript('arguments[0].focus()', this);
    // Only mutate if not readonly/disabled
    const readonly = await this.getReadonly();
    const disabled = await this.getDisabled();
    if (!(readonly || disabled)) {
      await this.whenBusyContextReady();

      if (value == null) {
        // it is not possible to set the value to null through user interaction
        await this._changeValueFallback(null);
        await this.whenBusyContextReady();
        return;
      }

      const isValueSet = await executeWithModules<boolean>(
        this.getDriver(),
        ['CustomElementUtils'],
        [this, value],
        `
        (({ getElementBridge }, element, value) => {
          // Retrieve the widget instance from the element
          var bridge = getElementBridge(element);
          var widget = bridge._WIDGET_INSTANCE.select;
          return widget._selectItemByValue(value)
            .then(() => true)
            .catch(() => false);
        })
        `
      );

      // If failed to set value, fallback to setProperty
      if (isValueSet === false) {
        console.warn('Failed to update value by interaction. Falling back to setProperty.');
        await this._changeValueFallback(value);
      }
    }
  }

  /**
   * Clears the value
   * @override
   */
  clear(): Promise<void> {
    return this.changeValue([]);
  }

  /**
   * This is a fallback mechanism for setting a value which
   * uses the setProperty method to update the value.
   *
   * @param value The value to be set
   */
  private _changeValueFallback(value: Array<any> | null): Promise<void> {
    return this.setProperty<Array<any> | null>('value', value);
  }
}
