/*
 * 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 = (this && this.__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());
    });
};
import React from 'react';
import { v4 } from 'uuid';
import { i18n } from '@kui-shell/core';
import { Choices, Graph, Memoizer, Wizard as Wiz } from 'madwizard';
import { Grid, GridItem } from '@patternfly/react-core/dist/esm/layouts/Grid';
import { Chip } from '@patternfly/react-core/dist/esm/components/Chip';
import { Tile } from '@patternfly/react-core/dist/esm/components/Tile';
import { Progress } from '@patternfly/react-core/dist/esm/components/Progress';
import { ChipGroup } from '@patternfly/react-core/dist/esm/components/ChipGroup';
import Wizard from '../Wizard/KWizard';
import Card from '../../../../spi/Card';
import Icons from '../../../../spi/Icons';
// import { CodeBlockResponseFn } from '../../components'
import Markdown from '../../../Markdown';
import { statusToClassName, statusToIcon } from '../../../ProgressStepper';
import '../../../../../../web/scss/components/Wizard/Guide.scss';
const strings = i18n('plugin-client-common', 'code');
export default class Guide extends React.PureComponent {
    constructor(props) {
        super(props);
        this.choiceIcon1 = (React.createElement(Icons, { className: "yellow-text", icon: "Warning" }));
        this.choiceIcon2 = (React.createElement(Icons, { icon: "PlusSquare" }));
        this.memos = new 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 || Choices.newChoiceState('default');
            if (!useTheseChoices && !props.choices) {
                choices.onChoice(this.onChoiceFromAbove);
            }
            const newGraph = yield Graph.compile(props.blocks, choices, this.memos, undefined, 'sequence', props.title, props.description);
            this.setState(state => {
                const noChangeToGraph = state && Graph.sameGraph(state.graph, newGraph);
                const graph = noChangeToGraph ? state.graph : Graph.order(newGraph);
                const frontier = noChangeToGraph && state && state.frontier ? state.frontier : Graph.findChoiceFrontier(graph);
                const startAtStep = state ? state.startAtStep : 1;
                const wizard = Wiz.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.createElement(Card, { 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.createElement(Markdown, { 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.createElement("div", { className: "kui--wizard-nav-item-description" }, description);
    }
    /** @return UI that offers the user a choice */
    tilesForChoice(choice) {
        return this.stepContent(React.createElement(Grid, { hasGutter: true, span: 4 }, choice.choices.map(_ => {
            return (React.createElement(GridItem, { key: _.title },
                React.createElement(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.createElement(Markdown, { nested: true, source: _.description }))));
        })));
    }
    withStatus(name, status) {
        const icon = status && statusToIcon(status);
        if (icon) {
            return (React.createElement(React.Fragment, null,
                name,
                " ",
                React.createElement("span", { className: 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.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.createElement("span", { className: "red-text" }, step.name) : step.name,
                component: this.renderContent(step.content),
                stepNavItemProps: {
                    children: this.wizardStepDescription(Graph.extractDescription(graph))
                }
            }
        };
    }
    /** @return the `WizardStep` models for this Guide */
    wizardSteps() {
        let isFirstChoice = true;
        return this.state.wizard
            .map(_ => {
            if (Wiz.isChoiceStep(_)) {
                const ui = this.choiceUI(_, isFirstChoice);
                isFirstChoice = false;
                return ui;
            }
            else if (Wiz.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 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.createElement(Chip, { key: key, ouiaId: key, onClick: this.removeChip }, value)));
        // categoryName={strings('Your Choices')}
        return (chips.length > 0 && (React.createElement(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.createElement(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 = Graph.extractDescription(this.state.graph);
        return descriptionContent && React.createElement(Markdown, { nested: true, source: descriptionContent });
    }
    wizardDescriptionFooter(steps) {
        return React.createElement("div", { className: "kui--markdown-major-paragraph" }, this.progress(steps));
    }
    wizardTitle() {
        return 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.createElement("div", { className: "kui--guide" },
            React.createElement("div", { className: "kui--wizard" },
                React.createElement("div", { className: "kui--wizard-main-content" },
                    React.createElement(Wizard, { key: this.state.isRunning && 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.createElement(React.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';
        }
    }
}
//# sourceMappingURL=Guide.js.map