/**
 * @class SimpleLogger
 *
 * @author: darryl.west@raincitysoftware.com
 * @created: 2014-07-06
 */
const dash = require( 'lodash' );
const Logger = require('./Logger' );
const ConsoleAppender = require('./ConsoleAppender' );
const FileAppender = require( './FileAppender' );
const RollingFileAppender = require( './RollingFileAppender' );

const SimpleLogger = function(opts) {
    'use strict';

    const options = Object.assign({}, opts);

    const manager = this;
    const domain = options.domain;
    const appenders = options.appenders || [];
    const loggers = options.loggers || [];

    let dfltLevel = options.level || Logger.DEFAULT_LEVEL,
        loggerConfigFile = options.loggerConfigFile,
        refresh = options.refresh,
        fs = options.fs || require('fs' ),
        createInterval = options.createInterval || setInterval,
        minRefresh = options.minRefresh || 10 * 1000,
        errorEventName = options.errorEventName;

    /**
     * create a logger with optional category and level
     *
     * @param category
     * @param level
     */
    this.createLogger = function(category, level) {
        const opts = Object.prototype.toString.call(category) === '[object String]' ? options : dash.merge({}, options, category);
        
        opts.category  = dash.isString(category) ? category : opts.category;
        opts.level     = level ? level : opts.level || dfltLevel;
        opts.appenders = appenders;

        if (errorEventName) {
            opts.errorEventName = errorEventName;
        }
        
        const logger = new Logger( opts );
        loggers.push( logger );
        
        return logger;
    };

    /**
     * create the console appender and add it to the appenders list
     *
     * @param opts - appender settings
     * @returns ConsoleAppender -
     */
    this.createConsoleAppender = function(opts) {
        return manager.addAppender( new ConsoleAppender( Object.assign({}, opts ) ));
    };

    /**
     * create a file appender and add it to the appenders list
     *
     * @param opts
     * @returns a FileAppender object
     */
    this.createFileAppender = function(opts) {
        if (!opts) {
            throw new Error('file appender must be created with log file path set in options');
        }

        return manager.addAppender( new FileAppender( opts ) );
    };

    /**
     * create a rolling file appender and add it to the appender list
     *
     * @param opts
     * @returns the appender
     */
    this.createRollingFileAppender = function( opts ) {
        return manager.addAppender( new RollingFileAppender( opts ) );
    };

    /**
     * add the appender to list
     *
     * @param appender
     * @returns the new appender
     */
    this.addAppender = function(appender) {
        appenders.push( appender );

        return appender;
    };

    this.getAppenders = function() {
        return appenders;
    };

    this.getLoggers = function() {
        return loggers;
    };

    /**
     * start the refresh thread; minimum cycle time = 10 seconds...
     */
    this.startRefreshThread = function() {
        // TODO replace with watcher thread
        if (fs.existsSync( loggerConfigFile ) && dash.isNumber( refresh )) {
            const t = Math.max( minRefresh, refresh );
            createInterval( manager.readConfig, t);
        }
    };

    /**
     * set the level of all loggers to the specified level
     *
     * @param level - one of the know levels
     */
    this.setAllLoggerLevels = function(level) {
        loggers.forEach(function(logger) {
            logger.setLevel( level );
        });
    };

    /**
     * read and parse the config file; change settings if required
     */
    this.readConfig = function(completeCallback) {
        // TODO refactor into configuration delegate to read stats and then process file only if stats change
        const callback = (err, buf) => {
            if (err) {
                /*eslint no-console: "off"*/
                console.log( err );
            } else {
                const conf = JSON.parse( buf.toString() );
                if (conf.appenders && conf.appenders.length > 0) {
                    // find each appender and set the level
                    conf.appenders.forEach(function(app) {
                        const level = app.level;

                        const appender = dash.find( appenders, (item) => {
                            if (item.getTypeName() === app.typeName && app.level) {
                                return item;
                            }
                        });

                        if (appender && typeof appender.setLevel === 'function') {
                            appender.setLevel( level );
                        }
                    });
                }

                if (conf.loggers && conf.loggers.length > 0) {
                    conf.loggers.forEach(item => {
                        if (item.category === 'all') {
                            manager.setAllLoggerLevels( item.level );
                        }
                    });
                }
            }

            if (completeCallback) {
                return completeCallback( err );
            }
        };

        fs.readFile( loggerConfigFile, callback );
    };

    this.__protected = function() {
        return {
            domain:domain,
            dfltLevel:dfltLevel,
            refresh:refresh,
            loggerConfigFile:loggerConfigFile
        };
    };
};

module.exports = SimpleLogger;

/**
 * static convenience method to create a simple console logger; see options for details
 *
 * @param options - optional, if present then it could be 1) a string or 2) and object.  if it's a string it's assumed
 * to be the logFilePath; if it's a string or an object with logFilePath property, then a file appender is created.
 *
 * Valid options:
 *  - logFilePath : a path to the file appender
 *  - domain : the logger domain, e.g., machine or site id
 *  - dfltLevel : the default log level (overrides info level)
 *  - timestampFormat : the format used for log entries (see moment date formats for all possibilities)
 *
 * @returns logger
 */
SimpleLogger.createSimpleLogger = function(options) {
    'use strict';

    let opts;

    // if options is a string then it must be the
    if (typeof options === 'string') {
        opts = {
            logFilePath: options
        };
    } else {
        opts = Object.assign( {}, options );
    }

    const manager = new SimpleLogger(opts);

    // pass options in to change date formats, etc
    manager.createConsoleAppender( opts );

    if (opts.logFilePath) {
        manager.createFileAppender( opts );
    }

    return manager.createLogger();
};

/**
 * static convenience method to create a file logger (no console logging);
 *
 * @param options - if string then it's the logFilePath, else options with the logFilePath
 * @returns logger
 */
SimpleLogger.createSimpleFileLogger = function(options) {
    'use strict';

    if (!options) {
        throw new Error('must create file logger with a logFilePath');
    }

    let opts;

    // if options is a string then it must be the
    if (typeof options === 'string') {
        opts = {
            logFilePath: options
        };
    } else {
        opts = Object.assign({}, options );
    }

    const manager = new SimpleLogger( opts );

    manager.createFileAppender( opts );

    return manager.createLogger();
};

/**
 * create a rolling file logger by passing options to SimpleLogger and Logger.  this enables setting
 * of domain, category, etc.
 *
 * @param options
 * @returns rolling logger
 */
SimpleLogger.createRollingFileLogger = function(options) {
    'use strict';

    if (!options) {
        throw new Error('createRollingFileLogger requires configuration options for this constructor');
    }

    var opts;

    // read a dynamic config file if available
    if (typeof options.readLoggerConfig === 'function') {
        opts = options.readLoggerConfig();

        opts.readLoggerConfig = options.readLoggerConfig;
    } else {
        opts = options;
    }

    var manager = new SimpleLogger( opts );

    manager.createRollingFileAppender( opts );

    if (opts.refresh && opts.loggerConfigFile) {
        process.nextTick( manager.startRefreshThread );
    }

    return manager.createLogger( opts );
};

/**
 * create a log manager
 *
 * @param options - file or rolling file specs;
 */
SimpleLogger.createLogManager = function(options) {
    'use strict';

    let opts; 

    // read a dynamic config file if available
    if (options && typeof options.readLoggerConfig === 'function') {
        opts = options.readLoggerConfig();

        opts.readLoggerConfig = options.readLoggerConfig;
    } else {
        opts = Object.assign({}, options);
    }

    var manager = new SimpleLogger( opts );

    if (opts.logDirectory && opts.fileNamePattern) {
        manager.createRollingFileAppender( opts );
    }

    // create at least one appender
    if (manager.getAppenders().length === 0) {
        manager.createConsoleAppender( opts );
    }

    return manager;
};