"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.capture = capture;
exports.withCreevey = withCreevey;
var Events = _interopRequireWildcard(require("@storybook/core-events"));
var _previewApi = require("@storybook/preview-api");
var _types = require("../../types");
var _shared = require("../../shared");
var _helpers = require("../shared/helpers");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const disableAnimationsStyles = `
*,
*:hover,
*::before,
*::after {
  animation-delay: -0.0001ms !important;
  animation-duration: 0s !important;
  animation-play-state: paused !important;
  cursor: none !important;
  caret-color: transparent !important;
  transition: 0s !important;
}
`;
async function resetCurrentStory(channel) {
  setTimeout(() => channel.emit(Events.SET_CURRENT_STORY, {
    storyId: true,
    name: '',
    kind: ''
  }), 0);
  return new Promise(resolve => channel.once(Events.STORY_MISSING, resolve));
}
function catchRenderError(channel) {
  let rejectCallback;
  const promise = new Promise((_resolve, reject) => rejectCallback = reject);
  function errorHandler(_ref) {
    let {
      title,
      description
    } = _ref;
    rejectCallback({
      message: title,
      stack: description
    });
  }
  function exceptionHandler(exception) {
    rejectCallback(exception);
  }
  function removeHandlers() {
    channel.off(Events.STORY_ERRORED, errorHandler);
    channel.off(Events.STORY_THREW_EXCEPTION, errorHandler);
  }
  channel.once(Events.STORY_ERRORED, errorHandler);
  channel.once(Events.STORY_THREW_EXCEPTION, exceptionHandler);
  return Object.assign(promise, {
    cancel: removeHandlers
  });
}
function waitForStoryRendered(channel) {
  let resolveCallback;
  const promise = new Promise(resolve => resolveCallback = resolve);
  function renderHandler() {
    resolveCallback();
  }
  function removeHandlers() {
    channel.off(Events.STORY_RENDERED, renderHandler);
  }
  channel.once(Events.STORY_RENDERED, renderHandler);
  return Object.assign(promise, {
    cancel: removeHandlers
  });
}
function waitForFontsLoaded() {
  if (!document.fonts) return;
  const areFontsLoading = Array.from(document.fonts).some(font => font.status == 'loading');
  if (areFontsLoading) {
    return new Promise(resolve => {
      const fontsLoadedHandler = () => {
        document.fonts.removeEventListener('loadingdone', fontsLoadedHandler);
        resolve();
      };
      document.fonts.addEventListener('loadingdone', fontsLoadedHandler);
    });
  }
}
function waitForCaptureCall() {
  return new Promise(resolve => captureResolver = resolve);
}
function initCreeveyState() {
  const prevState = JSON.parse(window.localStorage.getItem('Creevey_Tests') ?? '{}');
  if (prevState.creeveyHost) window.__CREEVEY_SERVER_HOST__ = prevState.creeveyHost;
  if (prevState.creeveyPort) window.__CREEVEY_SERVER_PORT__ = prevState.creeveyPort;
  if (prevState.setStoriesCounter) setStoriesCounter = prevState.setStoriesCounter;
  if (prevState.isTestBrowser) isTestBrowser = prevState.isTestBrowser;
  window.addEventListener('beforeunload', () => {
    window.localStorage.setItem('Creevey_Tests', JSON.stringify({
      creeveyHost: window.__CREEVEY_SERVER_HOST__,
      creeveyPort: window.__CREEVEY_SERVER_PORT__,
      setStoriesCounter,
      isTestBrowser
    }));
  });
}
let isTestBrowser = false;
let captureResolver;
let waitForCreevey;
let creeveyReady;
let setStoriesCounter = 0;
function withCreevey() {
  const addonsChannel = () => window.__STORYBOOK_ADDONS_CHANNEL__;
  let currentStory = '';
  let isAnimationDisabled = false;
  initCreeveyState();
  function disableAnimation() {
    isAnimationDisabled = true;
    const style = document.createElement('style');
    const textNode = document.createTextNode(disableAnimationsStyles);
    style.setAttribute('type', 'text/css');
    style.appendChild(textNode);
    document.head.appendChild(style);
  }
  async function getStories() {
    const stories = (0, _shared.serializeRawStories)(await window.__STORYBOOK_PREVIEW__.extract());
    const storiesByFiles = new Map();
    Object.values(stories).forEach(story => {
      const fileName = story.parameters.fileName;
      const storiesFromFile = storiesByFiles.get(fileName);
      if (storiesFromFile) storiesFromFile.push(story);else storiesByFiles.set(fileName, [story]);
    });
    void fetch(`http://${(0, _helpers.getConnectionUrl)()}/stories`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        setStoriesCounter,
        stories: [...storiesByFiles.entries()]
      })
    });
    return stories;
  }
  async function selectStory(storyId, kind, name, shouldWaitForReady, callback) {
    if (!isAnimationDisabled) disableAnimation();
    isTestBrowser = true;
    const channel = addonsChannel();
    const waitForReady = shouldWaitForReady ? new Promise(resolve => window.__CREEVEY_SET_READY_FOR_CAPTURE__ = resolve) : Promise.resolve();
    if (storyId == currentStory) await resetCurrentStory(channel);else currentStory = storyId;
    let isCaptureCalled = false;
    const renderPromise = waitForStoryRendered(channel);
    const errorPromise = catchRenderError(channel);
    const capturePromise = waitForCaptureCall().then(() => isCaptureCalled = true);
    setTimeout(() => channel.emit(Events.SET_CURRENT_STORY, {
      storyId,
      name,
      kind
    }), 0);
    try {
      await Promise.race([(async () => {
        await Promise.race([renderPromise, capturePromise]);
        await waitForFontsLoaded();
        await waitForReady;
      })(), errorPromise]);
      callback([null, isCaptureCalled]);
    } catch (reason) {
      // NOTE Event `STORY_THREW_EXCEPTION` triggered only in react and vue frameworks and return Error instance
      // NOTE Event `STORY_ERRORED` return error-like object without `name` field
      const errorMessage = reason instanceof Error ? reason.stack ?? reason.message : (0, _types.isObject)(reason) ? `${reason.message}\n    ${reason.stack}` : reason;
      callback([errorMessage]);
    } finally {
      renderPromise.cancel();
      errorPromise.cancel();
    }
  }
  function updateGlobals(globals) {
    addonsChannel().emit(Events.UPDATE_GLOBALS, {
      globals
    });
  }
  function insertIgnoreStyles(ignoreSelectors) {
    const stylesElement = document.createElement('style');
    stylesElement.setAttribute('type', 'text/css');
    document.head.appendChild(stylesElement);
    ignoreSelectors.forEach(selector => {
      stylesElement.innerHTML += `
        ${selector} {
          background: #000 !important;
          box-shadow: none !important;
          text-shadow: none !important;
          outline: 0 !important;
          color: rgba(0,0,0,0) !important;
        }
        ${selector} *, ${selector}::before, ${selector}::after {
          visibility: hidden !important;
        }
      `;
    });
    return stylesElement;
  }
  function removeIgnoreStyles(ignoreStyles) {
    ignoreStyles.parentNode?.removeChild(ignoreStyles);
  }
  function hasPlayCompletedYet(callback) {
    creeveyReady();
    let isCaptureCalled = false;
    let isPlayCompleted = false;
    const channel = addonsChannel();
    void waitForStoryRendered(channel).then(() => {
      if (isCaptureCalled) return;
      isPlayCompleted = true;
      callback(true);
    });
    void waitForCaptureCall().then(() => {
      if (isPlayCompleted) return;
      isCaptureCalled = true;
      callback(false);
    });
  }
  window.__CREEVEY_GET_STORIES__ = getStories;
  window.__CREEVEY_SELECT_STORY__ = selectStory;
  window.__CREEVEY_UPDATE_GLOBALS__ = updateGlobals;
  window.__CREEVEY_INSERT_IGNORE_STYLES__ = insertIgnoreStyles;
  window.__CREEVEY_REMOVE_IGNORE_STYLES__ = removeIgnoreStyles;
  window.__CREEVEY_HAS_PLAY_COMPLETED_YET__ = hasPlayCompletedYet;
  window.__CREEVEY_SET_READY_FOR_CAPTURE__ = _types.noop;
  return (0, _previewApi.makeDecorator)({
    name: 'withCreevey',
    parameterName: 'creevey',
    wrapper: (getStory, context) => {
      // TODO Define proper types, like captureElement is a promise
      const {
        captureElement
      } = context.parameters.creevey = context.parameters.creevey ?? {};
      Object.defineProperty(context.parameters.creevey, 'captureElement', {
        get() {
          switch (true) {
            case captureElement === undefined:
              return Promise.resolve(context.canvasElement);
            case captureElement === null:
              return Promise.resolve(document.documentElement);
            case typeof captureElement == 'string':
              return Promise.resolve(context.canvasElement.querySelector(captureElement));
            case typeof captureElement == 'function':
              // TODO Define type for it
              return Promise.resolve(captureElement(context));
          }
        },
        enumerable: true,
        configurable: true
      });
      return getStory(context);
    }
  });
}
async function capture(options) {
  if (!isTestBrowser) return;
  captureResolver();
  waitForCreevey = new Promise(resolve => creeveyReady = resolve);
  await fetch(`http://${(0, _helpers.getConnectionUrl)()}/capture`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      workerId: window.__CREEVEY_WORKER_ID__,
      options
    })
  });
  await waitForCreevey;
}