Source: interface/CLI/PoolForm/QuestionModel.js

const Prompt = require('@src/services/Prompt');
const PoolForm = require('./index');

/**
 * Class representing a question model.
 * @class
 */
class QuestionModel {
    /**
     * Create a question model.
     * @constructor
     * @param {Object} setup - The setup object.
     * @param {string} setup.id - The question ID.
     * @param {string} setup.type - The question type (default, form-control).
     * @param {string} setup.text - The question text.
     * @param {boolean} setup.required - Whether the question is required.
     * @param {string} setup.next - The ID of the next question.
     * @param {EventsHandlers} setup.events - The events handlers object.
     * @param {PoolForm} setup.formCtrl - The pool form object.
     * @param {Object} ctrl - The control object.
     */
    constructor(setup, ctrl) {
        const PoolFormModel = require('./index');
        const EventsHandlers = require('./EventsHandlers');
        const {id, type, text, required, events, next, formCtrl} = new Object(setup || {});

        this.id = id;
        this.type = type || 'default';
        this.text = text;
        this.required = required;
        this.next = next;
        this.ctrl = () => ctrl;

        if (formCtrl) {
            this.type = 'form-control';
            this.formCtrl = new PoolFormModel(formCtrl, this);
        }

        this.prompt = new Prompt();
        this.events = new EventsHandlers({
            ...(ctrl && ctrl.questions && ctrl.questions.events),
            ...events
        });
    }

    /**
     * The tools object.
     * @type {ToolsCLI}
     */
    get tools() {
        return toolsCLI || {};
    }

    /**
     * The command-line interface control object.
     * @type {ViewCLI}
     */
    get parentPool() {
        return this.ctrl() || {};
    }

    get poolData() {
        return this.getSafe('parentPool.values');
    }

    async redirectTo(path, timeout) {
        try {
            return await new Promise((resolve, reject) => {
                this.tools.print(`Redirecting to the "${path}" view...`, 'REDIRECTING');

                setTimeout(async () => {
                    try {
                        const redirect = await this.parentPool.goToView(path || 'home');
                        resolve(redirect);
                    } catch (err) {
                        reject(err);
                    }
                }, timeout || 2000);
            });
        } catch (err) {
            throw new Error.Log(err);
        }
    }

    /**
     * Sets the value of a key.
     * @param {string} key - The key to set the value for.
     * @param {*} value - The value to set.
     */
    setValue(key, value) {
        return this.parentPool.setValue(key, value);
    }

    /**
     * Gets the value of a key.
     * @param {string} key - The key to get the value for.
     * @returns {*} The value of the key.
     */
    getValue(key) {
        return this.parentPool.getValue(key);
    }

    /**
     * Gets a question by ID.
     * @param {string} id - The ID of the question to get.
     * @returns {QuestionModel} The question model object.
     */
    getQuestion(id) {
        return this.parentPool.getQuestion(id);
    }

    /**
     * 
     * @param {PoolForm} setup - New PoolForm setup.
     * @returns {PoolForm}
     */
    createPool(setup) {
        const newPool = new PoolForm(setup);
        return newPool;
    }

    /**
     * Goes to the next question.
     * @returns {*} The next question.
     */
    async goNext(stepName) {
        const nextQ = this.parentPool.getQuestion(stepName || this.next);
        
        if (nextQ) {
            return await nextQ.trigger();
        } else {
            return await this.endParentPool();
        }
    }

    async endParentPool() {
        return await this.parentPool.end();
    }

    /**
     * Triggers the question.
     * @returns {*} The result of the question trigger.
     */
    async trigger() {
        try {
            await this.events.trigger(this);

            if (this.text) {
                const answer = await this.prompt.question(this.text);
                
                this.answer = answer;
                const answerRes = await this.events.triggerEvent('answer', this, answer);
                if (answerRes instanceof Error.Log) {
                    answerRes.consolePrint();
                    return answerRes;
                }
            }

            if (this.formCtrl) {
                return await this.formCtrl.startPool();
            }

            return await this.goNext();
        } catch(err) {
            this.events.triggerEvent('error', this, err);
        }
    }

    /**
     * Sets a listener for the specified event.
     * @param {string} eventName - The name of the event to set the listener for.
     * @param {function} action - The action to perform when the event is triggered.
     */
    setListener(eventName, action) {
        if (!this.events[eventName]) {
            this.events[eventName] = action;
        }
    }

    /**
     * Overrides the listener for the specified event.
     * @param {string} eventName - The name of the event to override the listener for.
     * @param {function} action - The new action to perform when the event is triggered.
     */
    overrideListener(eventName, action) {
        this.events[eventName] = action;
    }
}

/**
 * A class for Question used on PoolForm of CLI Interface
 * @module QuestionModel
 */
module.exports = QuestionModel;