"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.loadTestsFromStories = loadTestsFromStories;
exports.saveStoriesJson = saveStoriesJson;
exports.saveTestsJson = saveTestsJson;
var _path = _interopRequireDefault(require("path"));
var _fs = require("fs");
var _crypto = require("crypto");
var _lodash = require("lodash");
var _types = require("../types");
var _utils = require("./utils");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function storyTestFabric(delay, testFn) {
  return async function storyTest() {
    delay ? await new Promise(resolve => setTimeout(resolve, delay)) : void 0;
    await (testFn ? testFn.call(this) : this.screenshots.length > 0 ? this.expect(this.screenshots.reduce((screenshots, {
      imageName,
      screenshot
    }, index) => ({
      ...screenshots,
      [imageName ?? `screenshot_${index}`]: screenshot
    }), {})).to.matchImages() : this.expect(await this.takeScreenshot()).to.matchImage());
  };
}
function createCreeveyTest(browser, storyMeta, skipOptions, testName) {
  const {
    kind,
    name: story,
    id: storyId
  } = storyMeta;
  const path = [kind, story, testName, browser].filter(_types.isDefined);
  const skip = skipOptions ? (0, _utils.shouldSkip)(browser, {
    kind,
    story
  }, skipOptions, testName) : false;
  const id = (0, _crypto.createHash)('sha1').update(path.join('/')).digest('hex');
  return {
    id,
    skip,
    browser,
    testName,
    storyPath: [...kind.split('/').map(x => x.trim()), story],
    storyId
  };
}
function convertStories(browserName, stories) {
  const tests = {};
  (Array.isArray(stories) ? stories : Object.values(stories)).forEach(storyMeta => {
    // TODO Skip docsOnly stories for now
    if (storyMeta.parameters.docsOnly) return;
    const {
      delay: delayParam,
      tests: storyTests,
      skip
    } = storyMeta.parameters.creevey ?? {};
    const delay = typeof delayParam == 'number' ? delayParam : delayParam !== null && delayParam !== void 0 && delayParam.for.includes(browserName) ? delayParam.ms : 0;

    // typeof tests === "undefined" => rootSuite -> kindSuite -> storyTest -> [browsers.png]
    // typeof tests === "function"  => rootSuite -> kindSuite -> storyTest -> browser -> [images.png]
    // typeof tests === "object"    => rootSuite -> kindSuite -> storySuite -> test -> [browsers.png]
    // typeof tests === "object"    => rootSuite -> kindSuite -> storySuite -> test -> browser -> [images.png]

    if (!storyTests) {
      const test = createCreeveyTest(browserName, storyMeta, skip);
      tests[test.id] = {
        ...test,
        storyId: storyMeta.id,
        story: storyMeta,
        fn: storyTestFabric(delay)
      };
      return;
    }
    Object.entries(storyTests).forEach(([testName, testFn]) => {
      const test = createCreeveyTest(browserName, storyMeta, skip, testName);
      tests[test.id] = {
        ...test,
        storyId: storyMeta.id,
        story: storyMeta,
        fn: storyTestFabric(delay, testFn)
      };
    });
  });
  return tests;
}
async function loadTestsFromStories(browsers, provider, update) {
  const testIdsByFiles = new Map();
  const stories = await provider(storiesByFiles => {
    const testsDiff = {};
    const tests = {};
    browsers.forEach(browser => {
      Array.from(storiesByFiles.entries()).forEach(([filename, stories]) => {
        var _testIdsByFiles$get;
        Object.assign(tests, convertStories(browser, stories));
        const changed = Object.keys(tests);
        const removed = ((_testIdsByFiles$get = testIdsByFiles.get(filename)) === null || _testIdsByFiles$get === void 0 ? void 0 : _testIdsByFiles$get.filter(testId => !tests[testId])) ?? [];
        if (changed.length == 0) testIdsByFiles.delete(filename);else testIdsByFiles.set(filename, changed);
        Object.assign(testsDiff, tests);
        removed.forEach(testId => testsDiff[testId] = undefined);
      });
    });
    update === null || update === void 0 || update(testsDiff);
  });
  const tests = browsers.reduce((tests, browser) => Object.assign(tests, convertStories(browser, stories)), {});
  Object.values(tests).filter(_types.isDefined).forEach(({
    id,
    story: {
      parameters: {
        fileName
      }
    }
  }) =>
  // TODO Don't use filename as a key, due possible collisions if two require.context with same structure of modules are defined
  testIdsByFiles.set(fileName, [...(testIdsByFiles.get(fileName) ?? []), id]));
  return tests;
}
function saveStoriesJson(storiesData, extract) {
  const outputDir = typeof extract == 'boolean' ? 'storybook-static' : extract;

  // NOTE Copy-pasted from Storybook's `getStoriesJsonData` method
  const allowed = ['fileName', 'docsOnly', 'framework', '__id', '__isArgsStory'];
  storiesData.globalParameters = (0, _lodash.pick)(storiesData.globalParameters, allowed);
  // @ts-expect-error ignore error
  storiesData.kindParameters = (0, _lodash.mapValues)(storiesData.kindParameters, v => (0, _lodash.pick)(v, allowed));
  // @ts-expect-error ignore error
  storiesData.stories = (0, _lodash.mapValues)(storiesData.stories, v => ({
    ...(0, _lodash.pick)(v, ['id', 'name', 'kind', 'story']),
    parameters: (0, _lodash.pick)(v.parameters, allowed)
  }));

  // TODO Fix args stories
  (0, _utils.removeProps)(storiesData ?? {}, ['stories', () => true, 'parameters', '__isArgsStory']);
  Object.values((storiesData === null || storiesData === void 0 ? void 0 : storiesData.stories) ?? {}).forEach(story => (0, _types.isObject)(story) && 'parameters' in story && (0, _types.isObject)(story.parameters) && delete story.parameters.__isArgsStory);
  (0, _fs.mkdirSync)(outputDir, {
    recursive: true
  });
  (0, _fs.writeFileSync)(_path.default.join(outputDir, 'stories.json'), JSON.stringify(storiesData, null, 2));
}
function saveTestsJson(tests, dstPath = process.cwd()) {
  (0, _fs.mkdirSync)(dstPath, {
    recursive: true
  });
  (0, _fs.writeFileSync)(_path.default.join(dstPath, 'tests.json'),
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
  JSON.stringify(tests, (_, value) => (0, _types.isFunction)(value) ? value.toString() : value, 2));
}