"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _uuid = require("uuid");
var _i18n = require("@kui-shell/core/mdist/api/i18n");
var _madwizard = require("madwizard");
var _Grid = require("@patternfly/react-core/dist/esm/layouts/Grid");
var _Chip = require("@patternfly/react-core/dist/esm/components/Chip");
var _Tile = require("@patternfly/react-core/dist/esm/components/Tile");
var _Progress = require("@patternfly/react-core/dist/esm/components/Progress");
var _ChipGroup = require("@patternfly/react-core/dist/esm/components/ChipGroup");
var _KWizard = _interopRequireDefault(require("../Wizard/KWizard"));
var _Card = _interopRequireDefault(require("../../../../spi/Card"));
var _Icons = _interopRequireDefault(require("../../../../spi/Icons"));
var _Markdown = _interopRequireDefault(require("../../../Markdown"));
var _ProgressStepper = require("../../../ProgressStepper");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
 * Copyright 2022 The Kubernetes Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function (resolve) {
      resolve(value);
    });
  }
  return new (P || (P = Promise))(function (resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }
    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }
    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }
    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
};
const strings = (0, _i18n.i18n)('plugin-client-common', 'code');
class Guide extends _react.default.PureComponent {
  constructor(props) {
    super(props);
    this.choiceIcon1 = _react.default.createElement(_Icons.default, {
      className: "yellow-text",
      icon: "Warning"
    });
    this.choiceIcon2 = _react.default.createElement(_Icons.default, {
      icon: "PlusSquare"
    });
    this.memos = new _madwizard.Memoizer();
    /** A choice was made somewhere in the UI */
    this.onChoiceFromAbove = ({
      choices
    }) => this.init(this.props, choices.clone());
    /**
     * A choice was made in *this* UI. The `this.props.choices.set()`
     * will eventually find its way back to a call to
     * `this.onChoiceFromAbove()`
     */
    this.onChoice = evt => {
      const group = evt.currentTarget.getAttribute('data-choice-group');
      const title = evt.currentTarget.getAttribute('data-choice-title');
      this.props.choices.setKey(group, title);
    };
    /**
     * Modifiy the given `step` to include any UI bits that all wizard
     * steps should have.
     */
    this.addCommonWizardStepProperties = (step, idx) => {
      const status = this.state.wizardStepStatus[idx];
      const name = this.withStatus(step.name, status);
      return Object.assign(step, {
        name,
        hideCancelButton: true
      });
    };
    /** User clicked to remove a chip */
    this.removeChip = evt => {
      const node = evt.currentTarget.parentElement;
      if (node) {
        const key = node.getAttribute('data-ouia-component-id');
        if (key) {
          this.state.choices.removeKey(key);
        }
      }
    };
    /** Commence automated execution of code blocks */
    this.startRun = () => {
      this.setState(curState => {
        const firstNotDoneStepIdx = curState.wizardStepStatus.findIndex(_ => _ !== 'success');
        return {
          isRunning: true,
          // remember that startAtStep is 1-indexed
          startAtStep: firstNotDoneStepIdx < 0 ? curState.startAtStep : firstNotDoneStepIdx + 1
        };
      });
    };
    /** Terminate automated execution of code blocks */
    this.stopRun = () => {
      this.setState({
        isRunning: false
      });
    };
    this.state = {
      startAtStep: 0,
      isRunning: false,
      graph: undefined,
      frontier: undefined,
      wizard: undefined,
      wizardStepStatus: undefined,
      choices: props.choices
    };
  }
  componentDidMount() {
    this.init(this.props);
  }
  /**
   * TODO move to a more common location?
   */
  static isValidFrontier(frontier) {
    return frontier.length > 0 && frontier.every(_ => _.prereqs.length > 0 || !!_.choice);
  }
  init(props, useTheseChoices) {
    return __awaiter(this, void 0, void 0, function* () {
      const choices = useTheseChoices || props.choices || _madwizard.Choices.newChoiceState('default');
      if (!useTheseChoices && !props.choices) {
        choices.onChoice(this.onChoiceFromAbove);
      }
      const newGraph = yield _madwizard.Graph.compile(props.blocks, choices, this.memos, undefined, 'sequence', props.title, props.description);
      this.setState(state => {
        const noChangeToGraph = state && _madwizard.Graph.sameGraph(state.graph, newGraph);
        const graph = noChangeToGraph ? state.graph : _madwizard.Graph.order(newGraph);
        const frontier = noChangeToGraph && state && state.frontier ? state.frontier : _madwizard.Graph.findChoiceFrontier(graph);
        const startAtStep = state ? state.startAtStep : 1;
        const wizard = _madwizard.Wizard.wizardify(graph, this.memos, {
          previous: state.wizard
        });
        const wizardStepStatus = noChangeToGraph && state ? state.wizardStepStatus : [];
        const isRunning = state ? state.isRunning : false;
        return {
          graph,
          frontier,
          choices,
          wizard,
          wizardStepStatus,
          startAtStep,
          isRunning
        };
      });
    });
  }
  componentWillUnmount() {
    if (this.state.choices) {
      this.state.choices.offChoice(this.onChoiceFromAbove);
    }
  }
  /** @return a wrapper UI for the content of a wizard step */
  stepContent(actualContent) {
    return _react.default.createElement(_Card.default, {
      className: "kui--markdown-tab-card"
    }, actualContent);
  }
  static getDerivedStateFromError(error) {
    console.error(error);
    return {
      error
    };
  }
  /** @return a UI component to visualize the given markdown source */
  renderContent(source) {
    return source && this.stepContent(_react.default.createElement(_Markdown.default, {
      tab: this.props.tab,
      nested: true,
      source: typeof source === 'string' ? source : source(),
      choices: this.state.choices,
      executeImmediately: this.state.isRunning
    }));
  }
  /** @return text to place below the wizard step title */
  wizardStepDescription(description) {
    return description && _react.default.createElement("div", {
      className: "kui--wizard-nav-item-description"
    }, description);
  }
  /** @return UI that offers the user a choice */
  tilesForChoice(choice) {
    return this.stepContent(_react.default.createElement(_Grid.Grid, {
      hasGutter: true,
      span: 4
    }, choice.choices.map(_ => {
      return _react.default.createElement(_Grid.GridItem, {
        key: _.title
      }, _react.default.createElement(_Tile.Tile, {
        isStacked: true,
        title: _.title,
        className: "kui--tile",
        icon: this.choiceIcon2,
        isSelected: this.state.choices && this.state.choices.getKey(choice.group) === _.title,
        onClick: this.onChoice,
        "data-choice-group": choice.group,
        "data-choice-title": _.title
      }, _.description && _react.default.createElement(_Markdown.default, {
        nested: true,
        source: _.description
      })));
    })));
  }
  withStatus(name, status) {
    const icon = status && (0, _ProgressStepper.statusToIcon)(status);
    if (icon) {
      return _react.default.createElement(_react.default.Fragment, null, name, " ", _react.default.createElement("span", {
        className: (0, _ProgressStepper.statusToClassName)(status).join(' ') + ' kui--validator'
      }, icon));
    } else {
      return name;
    }
  }
  /** Add React `component` to the given choice step */
  choiceUI({
    status,
    step,
    graph
  }, isFirstChoice) {
    return {
      status,
      graph,
      step: Object.assign({}, step, {
        name: graph.title,
        component: this.tilesForChoice(graph),
        stepNavItemProps: isFirstChoice && {
          children: this.wizardStepDescription(_react.default.createElement("span", {
            className: "sub-text"
          }, this.choiceIcon1, " This step requires you to choose how to proceed"))
        }
      })
    };
  }
  /** Add React `component` to the given task execution step */
  taskUI({
    status,
    step,
    graph
  }) {
    return {
      status,
      graph,
      step: {
        name: step.name === 'Missing title' ? _react.default.createElement("span", {
          className: "red-text"
        }, step.name) : step.name,
        component: this.renderContent(step.content),
        stepNavItemProps: {
          children: this.wizardStepDescription(_madwizard.Graph.extractDescription(graph))
        }
      }
    };
  }
  /** @return the `WizardStep` models for this Guide */
  wizardSteps() {
    let isFirstChoice = true;
    return this.state.wizard.map(_ => {
      if (_madwizard.Wizard.isChoiceStep(_)) {
        const ui = this.choiceUI(_, isFirstChoice);
        isFirstChoice = false;
        return ui;
      } else if (_madwizard.Wizard.isTaskStep(_)) {
        return this.taskUI(_);
      } else {
        return undefined;
      }
    }).filter(Boolean);
  }
  validateStepsIfNeeded(steps) {
    Promise.all(steps.map((_, idx) => __awaiter(this, void 0, void 0, function* () {
      if (!this.state.wizardStepStatus[idx] || this.state.wizardStepStatus[idx] === 'blank') {
        const status = yield _madwizard.Graph.validate(_.graph, this.memos, {
          validator: cmdline => this.props.tab.REPL.qexec(cmdline)
        });
        if (status !== this.state.wizardStepStatus[idx]) {
          this.setState(curState => ({
            wizardStepStatus: [...curState.wizardStepStatus.slice(0, idx), status, ...curState.wizardStepStatus.slice(idx + 1)]
          }));
        }
      }
    })));
    return steps.map(_ => _.step);
  }
  /** UI to indicate what choices the user has already made */
  chips() {
    // if you want key=value, this would be they key: key.split(/\/([^/]+)$/)[1]}=
    const chips = this.state.choices.entries().map(([key, value]) => _react.default.createElement(_Chip.Chip, {
      key: key,
      ouiaId: key,
      onClick: this.removeChip
    }, value));
    // categoryName={strings('Your Choices')}
    return chips.length > 0 && _react.default.createElement(_ChipGroup.ChipGroup, {
      className: "kui--chip-group kui--inverted-color-context",
      numChips: 8
    }, chips);
  }
  /**
   * @return a progress bar UI that indicates how far along we are
   * towards completion of the given `steps`
   */
  progress(steps) {
    const nTotal = steps.length;
    const nError = this.state.wizardStepStatus.filter(_ => _ === 'error').length;
    const nDone = this.state.wizardStepStatus.filter(_ => _ === 'success').length;
    const label = nError > 0 ? strings(nError === 1 ? 'xOfyFailingz' : 'xOfyFailingsz', nDone, nError, nTotal) : strings('xOfy', nDone, nTotal);
    const variant = nDone === nTotal ? 'success' : nError > 0 ? 'danger' : undefined;
    return _react.default.createElement(_Progress.Progress, {
      "aria-label": "wizard progress",
      className: "kui--wizard-progress",
      min: 0,
      max: nTotal,
      value: nDone,
      title: strings('Completed Tasks'),
      label: label,
      valueText: label,
      size: "sm",
      variant: variant,
      measureLocation: "outside"
    });
  }
  wizardDescription() {
    const descriptionContent = _madwizard.Graph.extractDescription(this.state.graph);
    return descriptionContent && _react.default.createElement(_Markdown.default, {
      nested: true,
      source: descriptionContent
    });
  }
  wizardDescriptionFooter(steps) {
    return _react.default.createElement("div", {
      className: "kui--markdown-major-paragraph"
    }, this.progress(steps));
  }
  wizardTitle() {
    return _madwizard.Graph.extractTitle(this.state.graph);
    // {this.state.isRunning && <span className="small-left-pad">{statusToIcon('in-progress')}</span>}
  }

  hasRemainingChoices() {
    return !!this.state.frontier.find(_ => _.choice !== undefined);
  }
  runAction() {
    return !this.hasRemainingChoices() && {
      className: 'kui--guidebook-run',
      onClick: this.state.isRunning ? this.stopRun : this.startRun,
      children: strings(this.state.isRunning ? 'Stop' : 'Run'),
      isDisabled: this.hasRemainingChoices() // ??? this does not seem to take...
    };
  }

  actions() {
    // return [this.runAction()].filter(Boolean)
    return undefined;
  }
  presentChoices() {
    const steps = this.validateStepsIfNeeded(this.wizardSteps()).map(this.addCommonWizardStepProperties);
    return steps.length === 0 ? 'Nothing to do!' : _react.default.createElement("div", {
      className: "kui--guide"
    }, _react.default.createElement("div", {
      className: "kui--wizard"
    }, _react.default.createElement("div", {
      className: "kui--wizard-main-content"
    }, _react.default.createElement(_KWizard.default, {
      key: this.state.isRunning && (0, _uuid.v4)(),
      boxShadow: true,
      hideClose: true,
      steps: steps,
      title: this.wizardTitle(),
      startAtStep: this.state.startAtStep,
      description: this.wizardDescription(),
      descriptionFooter: this.wizardDescriptionFooter(steps),
      rightButtons: this.actions(),
      topContent: this.chips()
    }))));
  }
  allDoneWithChoices() {
    return 'all done with choices';
  }
  render() {
    try {
      if (!this.state || !this.state.frontier) {
        return _react.default.createElement(_react.default.Fragment, null);
      } else if (this.state.error) {
        return 'Internal error';
      } else if (this.state.frontier.length === 0) {
        return this.allDoneWithChoices();
      } else {
        return this.presentChoices();
      }
    } catch (error) {
      console.error(error);
      setTimeout(() => this.setState({
        error
      }));
      return 'Internal Error';
    }
  }
}
exports.default = Guide;