/*-----------------------------------------------------------------------------
 * @package:    node-kit wizard
 * @author:     Richard B Winters
 * @copyright:  2014-2020 Massively Modified, Inc.
 * @license:    Apache-2.0 <http://www.apache.org/licenses/LICENSE-2.0>
 * @version:    0.2.0
 *---------------------------------------------------------------------------*/
'use strict'


// INCLUDES
import * as rl from 'readline';
import * as Event from 'events';
import { FilesystemDescriptor } from '@kwaeri/filesystem';
import { BaseServiceProvider, ServiceProviderSubscriptions, ServiceProviderHelpText, ServiceEventBits } from '@kwaeri/service';
import debug from 'debug';


// DEFINES

/* Configure Debug module support */
let DEBUG = debug( 'nkm:wizard' );

/* Our UserPrompt Interface */
let readline = rl;

/* A type for async Wizard responses */
export type WizardResponsePromise =
{
    answers: Array<string>;
}


/**
 * BaseWizard defines the interface for any service provider which will
 * implement a wizard-type service
 *
 * @since 0.1.0
 */
export interface BaseWizardService
{
    run<T extends WizardResponsePromise>( options: FilesystemDescriptor ): Promise<T>
    question1( instance: rl.ReadLine, answers: Array<any>, options?: FilesystemDescriptor ): Promise<any>
    setServiceEventMetadata( data: ServiceEventBits ): void;
}



/**
 * WizardService Class
 *
 * The { WizardService } is the class from which all wizard services should inherit
 * from. A wizard service is one that prompts users for information, and will [or should]
 * always need the readline class, and have at least a pair of methods 'run' and `question1`.
 *
 * This class helps to sensibly build a derived service provider, it should not be
 * used directly. Instead, developers should extend from the WizardServiceProvider class.
 */
export abstract class WizardService extends Event.EventEmitter implements BaseWizardService
{
    constructor()
    {
        super();
    }

    abstract run<T extends WizardResponsePromise>( options: FilesystemDescriptor ): Promise<T>;
    abstract question1( instance: rl.ReadLine, answers: Array<any>, options: FilesystemDescriptor ): Promise<any>;

    setServiceEventMetadata( data: ServiceEventBits): void
    {
        this.emit( 'ServiceEvent', data );
    }
}


/**
 * WizardServiceProvider Class
 *
 * The { WizardServiceProvider } class implements a facility for presenting
 * a series of user promopts via CLI, in order to gather information
 * for a task, much in the same fashion as that of a wizard.
 */
export abstract class WizardServiceProvider extends WizardService implements BaseServiceProvider
{
    /**
     * An array of user prompt functions
     *
     * @var { Array<Function>}
     */
    prompts: Array<Function>;


    /**
     * Class constructor
     *
     * @param { void }
     *
     * @since 0.1.10
     */
    constructor()
    {
        super();
    }


    abstract getServiceProviderSubscriptions( options: any ): ServiceProviderSubscriptions;


    abstract getServiceProviderSubscriptionHelpText<T extends ServiceProviderHelpText>( options: any ): T;


    /**
     * A method which runs a sequence of user prompts through a CLI
     *
     * @param { FilesystemDescriptor } options  Any options that might have been provided preemptively
     *
     * @return { Promise<WizardResponsePromise> } An array of answers
     *
     * @since 0.1.10
     */
    async run<T extends WizardResponsePromise>( options: FilesystemDescriptor ): Promise<T>
    {
        // Any time a Wizard is run, we need a readline interface:
        let userPrompt = readline.createInterface
        (
            {
                input: process.stdin,
                output: process.stdout
            }
        );

        // As well as an answers array:
        let answerArray = [];

        DEBUG( `Call user prompts` );

        let answers = await this.question1( userPrompt, answerArray, options );

        // Start the wizard:
        return Promise.resolve( <T>answers );
    }


    /**
     * A method which implements the first user prompt for a setup wizard that is
     * run for assisting the automaton with generating file contents.
     *
     * @param { rl.ReadLine } instance An instance of readline.createInterface()
     * @param { Array<string> } answers An array filled with answers from the automaton project generator wizard.
     * @param { Array<Function> } prompts An array of user prompt functions
     * @param { FilesystemDescriptor } options An object with options for specifying
     *
     * @return { Promise<any> } Returns a promise for allowing asynchronous chaining
     *
     * @since 0.1.10
     */
    async question1( instance: rl.ReadLine, answers: Array<any>, options?: FilesystemDescriptor ): Promise<any>
    {
        instance.question
        (
            `This is a @kwaeri/node-kit wizard example user-prompt question. Use as many pro-\n` +
            `mpts as you'd like, simply assign the next prompts resolved promise as this pro-\n` +
            `mpts resolved promise. You can chain them in that fashion, passing along requir-\n` +
            `ed arguments.\n\n` +
            `Press [Enter] to continue...`,
            async ( answer ) =>
            {
                console.log( 'You continued...: ', answer );

                instance.close();

                return Promise.resolve( answer );
            }
        );
    }
}


// Example of a Wizard Service Provider
export class ExampleWizard extends WizardServiceProvider
{
    getServiceProviderSubscriptions( options?: any ): ServiceProviderSubscriptions
    {
        return { commands: {}, required: {}, optional: {}, subcommands: {} };
    }
    getServiceProviderSubscriptionHelpText<T extends ServiceProviderHelpText>( options?: any ): T
    {
        return { helpText: { "command": `To use this service, read this HelpText.` } } as T;
    }
    testEvents( handler: any ): void
    {
        this.on( 'ServiceEvent', ( data ) => { DEBUG( `TestServiceEvent Caught and Handled!` ); handler( data ); } );
    }
}