import { linterContextProps, linterContextDefault } from "../gorgon/proptypes.js";
import { StyleSheet, css } from "aphrodite";
import { gray76, phoneMargin, negativePhoneMargin, tableBackgroundAccent, kaGreen } from "../styles/constants.js";
import _gradedGroupAnswerBarJsx from "./graded-group-answer-bar.jsx";
import _rendererJsx from "../renderer.jsx";
import _componentsInlineIconJsx from "../components/inline-icon.jsx";
import { iconOk, iconRemove } from "../icon-paths.js";
import _mixinsChangeableJsx from "../mixins/changeable.jsx";
import _perseusApiJsx from "../perseus-api.jsx";
import _underscore from "underscore";
import _react from "react";
import _classnames from "classnames";

var _module_ = {
    exports: {}
};

var exports = _module_.exports;
/* eslint-disable react/forbid-prop-types */
/* TODO(csilvers): fix these lint errors (http://eslint.org/docs/rules): */
/* To fix, remove an entry above, run ka-lint, and fix errors. */
/* globals i18n */
const classNames = _classnames;
const React = _react;
const _ = _underscore;

const ApiOptions = _perseusApiJsx.Options;
const Changeable = _mixinsChangeableJsx;
const InlineIcon = _componentsInlineIconJsx;
const Renderer = _rendererJsx;
const GradedGroupAnswerBar = _gradedGroupAnswerBarJsx;

// A Graded Group is more or less a Group widget that displays a check
// answer button below the rendered content. When clicked, the widget grades
// the stuff inside and displays feedback about whether the inputted answer was
// correct or not.

const GRADING_STATUSES = {
    ungraded: "ungraded",
    correct: "correct",
    incorrect: "incorrect",
    invalid: "invalid",
};

const ANSWER_BAR_STATES = GradedGroupAnswerBar.ANSWER_BAR_STATES;

// Update answer bar state based on current state and whether the question is
// answerable (all parts have been filled out) or not.
const getNextState = (currentState, answerable) => {
    switch (currentState) {
        case ANSWER_BAR_STATES.HIDDEN:
            return answerable ? ANSWER_BAR_STATES.ACTIVE : currentState;
        case ANSWER_BAR_STATES.ACTIVE:
            return !answerable ? ANSWER_BAR_STATES.INACTIVE : currentState;
        case ANSWER_BAR_STATES.INACTIVE:
            return answerable ? ANSWER_BAR_STATES.ACTIVE : currentState;
        case ANSWER_BAR_STATES.INCORRECT:
            return answerable
                ? ANSWER_BAR_STATES.ACTIVE
                : ANSWER_BAR_STATES.INACTIVE;
        default:
            return currentState;
    }
};

// Prepended to all invalid messages to make the widget messages a bit clearer
const INVALID_MESSAGE_PREFIX = "We couldn't grade your answer.";
const DEFAULT_INVALID_MESSAGE =
    "It looks like you left something blank or " +
    "entered in an invalid answer.";

const GradedGroup = createReactClass({
    propTypes: {
        ...Changeable.propTypes,
        apiOptions: ApiOptions.propTypes,
        content: PropTypes.string,
        hasHint: PropTypes.bool,
        hint: PropTypes.object,
        images: PropTypes.object,
        inGradedGroupSet: PropTypes.bool,
        onBlur: PropTypes.func,
        onFocus: PropTypes.func,

        // The function to call when clicking "Next question" after correctly
        // answering one graded group out of a set. If this is null, the
        // "Next question" button will not appear.
        onNextQuestion: PropTypes.func,

        title: PropTypes.string,
        trackInteraction: PropTypes.func.isRequired,
        widgets: PropTypes.object,
        linterContext: linterContextProps,
    },

    getDefaultProps: function() {
        return {
            title: "",
            content: "",
            widgets: {},
            images: {},
            hint: null,
            hasHint: false,
            linterContext: linterContextDefault,
        };
    },

    getInitialState: function() {
        return {
            status: GRADING_STATUSES.ungraded,
            showHint: false,
            message: "",
            answerBarState: ANSWER_BAR_STATES.HIDDEN,
        };
    },

    shouldComponentUpdate: function(nextProps, nextState) {
        return nextProps !== this.props || nextState !== this.state;
    },

    change(...args) {
        return Changeable.change.apply(this, args);
    },

    // This is a little strange because the id of the widget that actually
    // changed is going to be lost in favor of the group widget's id. The
    // widgets prop also wasn't actually changed, and this only serves to
    // alert our renderer (our parent) of the fact that some interaction
    // has occurred.
    _onInteractWithWidget: function(id) {
        // Reset grading display when user changes answer
        this.setState({
            status: GRADING_STATUSES.ungraded,
            message: "",
        });

        if (this.refs.renderer) {
            this.change("widgets", this.props.widgets);
            const emptyWidgets = this.refs.renderer.emptyWidgets();
            const answerable = emptyWidgets.length === 0;
            const answerBarState = this.state.answerBarState;
            this.setState({
                answerBarState: getNextState(answerBarState, answerable),
            });
        }
    },

    _checkAnswer: function() {
        this.refs.renderer.showRationalesForCurrentlySelectedChoices();
        const score = this.refs.renderer.score();

        let status;
        let message;
        if (score.type === "points") {
            status =
                score.total === score.earned
                    ? GRADING_STATUSES.correct
                    : GRADING_STATUSES.incorrect;
            message = score.message || "";
        } else {
            // score.type is "invalid"
            status = GRADING_STATUSES.invalid;
            message = score.message
                ? `${INVALID_MESSAGE_PREFIX} ${score.message}`
                : `${INVALID_MESSAGE_PREFIX} ${DEFAULT_INVALID_MESSAGE}`;
        }

        this.setState({
            status: status,
            message: message,
            // TODO(kevinb) handle 'invalid' status
            answerBarState:
                status === "correct"
                    ? ANSWER_BAR_STATES.CORRECT
                    : ANSWER_BAR_STATES.INCORRECT,
        });

        this.props.trackInteraction({
            status: status,
        });
    },

    // Mobile API
    getInputPaths: function() {
        return this.refs.renderer.getInputPaths();
    },

    setInputValue: function(path, newValue, cb) {
        return this.refs.renderer.setInputValue(path, newValue, cb);
    },

    getAcceptableFormatsForInputPath: function(path) {
        return this.refs.renderer.getAcceptableFormatsForInputPath(path);
    },

    focus: function() {
        return this.refs.renderer.focus();
    },

    focusInputPath: function(path) {
        this.refs.renderer.focusPath(path);
    },

    blurInputPath: function(path) {
        this.refs.renderer.blurPath(path);
    },

    render: function() {
        const apiOptions = _.extend(
            {},
            ApiOptions.defaults,
            this.props.apiOptions,
            {
                // Api Rewriting to support correct onFocus/onBlur
                // events for the mobile API
                onFocusChange: (newFocus, oldFocus) => {
                    if (oldFocus) {
                        this.props.onBlur(oldFocus);
                    }
                    if (newFocus) {
                        this.props.onFocus(newFocus);
                    }
                },
            }
        );

        let icon = null;
        // Colors are 10% darker than the colors in graded-group.less
        if (this.state.status === GRADING_STATUSES.correct) {
            icon = <InlineIcon {...iconOk} style={{color: "#526f03"}} />;
        } else if (this.state.status === GRADING_STATUSES.incorrect) {
            icon = <InlineIcon {...iconRemove} style={{color: "#ff5454"}} />;
        }

        const mobileClass = this.props.inGradedGroupSet
            ? css(styles.gradedGroupInSet)
            : css(styles.gradedGroup);

        const classes = classNames({
            [mobileClass]: apiOptions.isMobile,
            "perseus-graded-group": true,
            "answer-correct": apiOptions.isMobile
                ? false
                : this.state.status === GRADING_STATUSES.correct,
            "answer-incorrect": apiOptions.isMobile
                ? false
                : this.state.status === GRADING_STATUSES.incorrect,
        });

        const {answerBarState} = this.state;

        // Disabled widgets after the answer has been answered correctly to
        // prevent a situation where the answer has been marked correct but
        // looks incorrect because a user has modified it afterwards.
        const isCorrect = answerBarState === ANSWER_BAR_STATES.CORRECT;
        const readOnly =
            apiOptions.readOnly || (apiOptions.isMobile && isCorrect);

        return (
            <div className={classes}>
                {!!this.props.title &&
                    <div className={css(styles.title)}>
                        {this.props.title}
                    </div>}
                <Renderer
                    {...this.props}
                    ref="renderer"
                    apiOptions={{...apiOptions, readOnly}}
                    onInteractWithWidget={this._onInteractWithWidget}
                    linterContext={this.props.linterContext}
                />
                {!apiOptions.isMobile &&
                    icon &&
                    <div className="group-icon">
                        {icon}
                    </div>}
                {!apiOptions.isMobile &&
                    <p>
                        {this.state.message}
                    </p>}
                {!apiOptions.isMobile &&
                    <input
                        type="button"
                        value={i18n._("Check")}
                        className="simple-button"
                        disabled={this.props.apiOptions.readOnly}
                        onClick={this._checkAnswer}
                    />}
                {!apiOptions.isMobile &&
                    isCorrect &&
                    this.props.onNextQuestion &&
                    <input
                        type="button"
                        value={i18n._("Next question")}
                        className="simple-button"
                        disabled={this.props.apiOptions.readOnly}
                        onClick={this.props.onNextQuestion}
                        style={{marginLeft: 5}}
                    />}

                {this.props.hint &&
                    this.props.hint.content &&
                    (this.state.showHint
                        ? <div>
                              <div
                                  className={css(styles.explanationTitle)}
                                  onClick={() => this.setState({showHint: false})}
                              >
                                  {i18n._("Hide explanation")}
                              </div>
                              <Renderer
                                  {...this.props.hint}
                                  ref="hints-renderer"
                                  apiOptions={apiOptions}
                                  linterContext={this.props.linterContext}
                              />
                          </div>
                        : <div
                            onClick={() => this.setState({showHint: true})}
                            className={css(styles.showHintLink)}
                        >
                            {i18n._("Explain")}
                        </div>)}
                {apiOptions.isMobile &&
                    answerBarState !== ANSWER_BAR_STATES.HIDDEN &&
                    <GradedGroupAnswerBar
                        apiOptions={apiOptions}
                        answerBarState={answerBarState}
                        onCheckAnswer={this._checkAnswer}
                        onNextQuestion={this.props.onNextQuestion}
                    />}
            </div>
        );
    },
});

const traverseChildWidgets = function(props, traverseRenderer) {
    return _.extend({}, props, traverseRenderer(props));
};

_module_.exports = {
    name: "graded-group",
    displayName: "Graded group (articles only)",
    widget: GradedGroup,
    traverseChildWidgets: traverseChildWidgets,
    // TODO(aasmund): This widget should be available for articles only
    hidden: false,
    tracking: "all",
    isLintable: true,
};

const styles = StyleSheet.create({
    gradedGroupInSet: {
        // Reset a few desktop-only styles that come from graded-group.less
        marginLeft: 0,
        paddingLeft: 0,
    },

    gradedGroup: {
        borderTop: `1px solid ${gray76}`,
        borderBottom: `1px solid ${gray76}`,
        backgroundColor: tableBackgroundAccent,
        marginLeft: negativePhoneMargin,
        marginRight: negativePhoneMargin,
        paddingBottom: phoneMargin,
        paddingLeft: phoneMargin,
        paddingRight: phoneMargin,
        paddingTop: 10,
        width: "auto",
    },

    showHintLink: {
        marginTop: 20,
        color: kaGreen,
        cursor: "pointer",
    },

    explanationTitle: {
        marginTop: 20,
        color: kaGreen,
        marginBottom: 10,
        cursor: "pointer",
    },

    title: {
        fontSize: 12,
        color: gray76,
        textTransform: "uppercase",
        marginBottom: 11,
        letterSpacing: 0.8,
    },
});
export default _module_.exports;
