import { Condition, WebDriver, error } from 'selenium-webdriver';
import { executeWithModules, TEST_OBJ_NAME } from './oj-module-proxy';

/**
 * Get open busy state descriptions from the page context
 * @private
 * @param driver
 * @returns an array of strings of busy state descriptions
 */
export const getBusyStates = async (driver: WebDriver): Promise<string> => {
  const hasProxy = await driver.executeScript<boolean>(`return '${TEST_OBJ_NAME}' in window`);
  if (hasProxy) {
    return executeWithModules<string>(
      driver,
      ['BusyContext'],
      [],
      '(BusyContext) => BusyContext.getBusyStates().map((state) => state.description)'
    ).catch((ex) => ex.message);
  }
  return '';
};

/**
 * Returns a Promise that is resolved when the application's busy
 * state reports ready or the page is not a JET page (RequireJS
 * or ojs/ojcontext module not available).
 * @private
 * @param driver WebDriver instance
 * @return A Promise which is resolved to true when the
 * page's busy state is ready or to false when it is not a JET page.
 */
export const whenBusyContextReady = async (driver: WebDriver): Promise<boolean> => {
  return executeWithModules<boolean>(
    driver,
    ['BusyContext'],
    [],
    '(BusyContext) => BusyContext.whenReady()'
  ).catch(async (ex) => {
    if (ex instanceof error.TimeoutError || ex instanceof error.ScriptTimeoutError) {
      const openStates = await getBusyStates(driver);
      throw Error(`BusyContext.whenReady() timed out: ${ex.message}\nBusy states: ${openStates}`);
    } else {
      // unexpected error which is just reported as a warning below
      return false;
    }
  });
};

/**
 * Create a Condition for WebDriver to wait on the page's BusyContext to be ready
 */
export function pageReady(): Condition<Promise<boolean>> {
  return new Condition<Promise<boolean>>('for JET page to indicate ready', async (driver) => {
    // Check that <html> element has a size after page ready
    // Call thru executeScript to bypass WebElement overrides
    const size = await driver.executeScript<{ width: number; height: number }>(`
      const html = document.querySelector('html');
      return { width: html.clientWidth, height: html.clientHeight }
    `);
    if (!(size.width && size.height)) {
      return Promise.resolve(false);
    }

    return whenBusyContextReady(driver);
  });
}
