import { SET_STORIES, STORY_RENDERED } from '@storybook/core-events';
import { denormalizeStoryParameters } from '../../shared';
import { isDefined } from '../../types';
import { initCreeveyClientApi } from '../shared/creeveyClientApi';
import { calcStatus } from '../shared/helpers';
import { ADDON_ID } from './register';
import { getEmojiByTestStatus } from './utils';
export class CreeveyManager {
  storyId = '';
  activeBrowser = '';
  selectedTestId = '';
  status = {
    isRunning: false,
    tests: {},
    browsers: []
  };
  creeveyApi = null;
  stories = {};
  updateStatusListeners = [];
  changeTestListeners = [];
  constructor(storybookApi) {
    this.storybookApi = storybookApi;
    this.storybookApi = storybookApi;
  }
  initAll = async () => {
    this.storybookApi.on(STORY_RENDERED, this.onStoryRendered);
    this.storybookApi.on(SET_STORIES, this.onSetStories);
    this.creeveyApi = await initCreeveyClientApi();
    this.creeveyApi.onUpdate(this.handleCreeveyUpdate);
    this.status = await this.creeveyApi.status;
  };
  onUpdateStatus(listener) {
    this.updateStatusListeners.push(listener);
    return () => void (this.updateStatusListeners = this.updateStatusListeners.filter(x => x != listener));
  }
  onChangeTest(listener) {
    this.changeTestListeners.push(listener);
    return () => void (this.changeTestListeners = this.changeTestListeners.filter(x => x != listener));
  }
  handleCreeveyUpdate = update => {
    const {
      tests,
      removedTests = [],
      isRunning
    } = update;
    if (isDefined(isRunning)) {
      this.status.isRunning = isRunning;
    }
    if (isDefined(tests)) {
      const prevTests = this.status.tests;
      const prevStories = this.stories || {};
      Object.values(tests).filter(isDefined).forEach(update => {
        const {
          id,
          skip,
          status,
          results,
          approved,
          storyId
        } = update;
        const test = prevTests[id];
        if (!test) {
          return prevTests[id] = update;
        }
        if (isDefined(skip)) test.skip = skip;
        if (isDefined(status)) {
          test.status = status;
          if (isDefined(storyId) && isDefined(prevStories[storyId])) {
            const story = prevStories[storyId];
            const storyStatus = this.getStoryTests(storyId);
            const oldStatus = storyStatus.map(x => x.id === id ? status : x.status).reduce((oldStatus, newStatus) => calcStatus(oldStatus, newStatus), undefined);
            story.name = this.addStatusToStoryName(story.name, calcStatus(oldStatus, status), skip || false);
          }
        }
        if (isDefined(results)) test.results ? test.results.push(...results) : test.results = results;
        if (isDefined(approved)) {
          Object.entries(approved).forEach(_ref => {
            let [image, retry] = _ref;
            return retry !== undefined && test && ((test.approved = test?.approved || {})[image] = retry);
          });
        }
      });
      removedTests.forEach(_ref2 => {
        let {
          id
        } = _ref2;
        return delete prevTests[id];
      });
      this.status.tests = prevTests;
      this.stories = prevStories;
      this.setPanelsTitle();
      // TODO Check setStories method in 6.x and migrate properly
      this.storybookApi.emit(SET_STORIES, this.stories);
    }
    this.updateStatusListeners.forEach(x => x(update));
  };
  getCurrentTest = () => {
    return this.status.tests[this.selectedTestId];
  };
  onStoryRendered = storyId => {
    if (this.storyId === '') this.addStatusesToSideBar();
    if (this.storyId !== storyId) {
      this.storyId = storyId;
      this.selectedTestId = this.getTestsByStoryIdAndBrowser(this.activeBrowser)[0]?.id ?? '';
      this.setPanelsTitle();
      this.changeTestListeners.forEach(x => x(this.selectedTestId));
    }
  };
  onStart = () => {
    this.creeveyApi?.start([this.selectedTestId]);
  };
  onStop = () => {
    this.creeveyApi?.stop();
  };
  onImageApprove = (id, retry, image) => this.creeveyApi?.approve(id, retry, image);
  onStartAllStoryTests = () => {
    const ids = Object.values(this.status.tests).filter(isDefined).filter(x => x.storyId === this.storyId).map(x => x.id);
    this.creeveyApi?.start(ids);
  };
  onStartAllTests = () => {
    const ids = Object.values(this.status.tests).filter(isDefined).map(x => x.id);
    this.creeveyApi?.start(ids);
  };
  onSetStories = data => {
    const stories = data.v ? denormalizeStoryParameters(data) : data.stories;
    this.stories = stories;
  };
  setActiveBrowser = browser => {
    this.activeBrowser = browser;
    this.selectedTestId = this.getTestsByStoryIdAndBrowser(this.activeBrowser)[0]?.id ?? '';
    this.changeTestListeners.forEach(x => x(this.selectedTestId));
  };
  setSelectedTestId = testId => {
    this.selectedTestId = testId;
    this.changeTestListeners.forEach(x => x(this.selectedTestId));
  };
  getStoryTests = storyId => {
    if (!this.status || !this.status.tests) return [];
    return Object.values(this.status.tests).filter(result => result?.storyId === storyId).filter(isDefined);
  };
  getBrowsers = () => {
    return this.status.browsers;
  };
  getTestsByStoryIdAndBrowser = browser => {
    return Object.values(this.status.tests).filter(x => x?.browser === browser && x.storyId === this.storyId).filter(isDefined);
  };
  getTabTitle = browser => {
    const tests = this.getTestsByStoryIdAndBrowser(browser);
    const browserStatus = tests.map(x => x && x.status).reduce((oldStatus, newStatus) => calcStatus(oldStatus, newStatus), undefined);
    const browserSkip = tests.length > 0 ? tests.every(x => x && x.skip) : false;
    const emojiStatus = getEmojiByTestStatus(browserStatus, browserSkip);
    return `${emojiStatus ? `${emojiStatus} ` : ''}Creevey/${browser}`;
  };
  setPanelsTitle = () => {
    const panels = this.storybookApi?.getPanels();
    if (!panels) return;
    let firstPanelBrowser = this.activeBrowser;
    for (const p in panels) {
      const panel = panels[p];
      if (panel.id?.indexOf(ADDON_ID) === 0 && panel.paramKey) {
        panel.title = this.getTabTitle(panel.paramKey);
        if (!firstPanelBrowser) firstPanelBrowser = panel.paramKey;
      }
    }
    this.storybookApi.setSelectedPanel(`${ADDON_ID}/panel/${firstPanelBrowser}`);
  };
  addStatusesToSideBar() {
    if (!Object.keys(this.stories).length) return;
    const stories = this.stories;
    Object.keys(this.stories).forEach(storyId => {
      const storyStatus = this.getStoryTests(storyId);
      const status = storyStatus.map(x => x.status).reduce((oldStatus, newStatus) => calcStatus(oldStatus, newStatus), undefined);
      const skip = storyStatus.length > 0 ? storyStatus.every(x => x.skip) : false;
      this.stories[storyId].name = this.addStatusToStoryName(stories[storyId].name, status, skip);
    });

    // TODO Check setStories method in 6.x and migrate properly
    this.storybookApi.emit(SET_STORIES, this.stories);
  }
  addStatusToStoryName(name, status, skip) {
    name = name.replace(/^(❌|✔|🟡|🕗|⏸) /, '');
    const emojiStatus = getEmojiByTestStatus(status, skip);
    return `${emojiStatus ? `${emojiStatus} ` : ''} ${name}`;
  }
}