import winston from "winston";
import 'winston-daily-rotate-file';

export default class Logger {
    #internLogger = null;

    /*
        error: 0,
        warn: 1,
        info: 2,
        http: 3,
        verbose: 4,
        debug: 5,
        silly: 6
    */

    /**
     * @param {string} logDir 
     */
    constructor(logDir) {
        this.#internLogger = winston.createLogger({
            level: 'debug',
            format: winston.format.combine(
                winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
                this.#getLogFormat()
            ),
            transports: [
                this.#getCombinedLogTransport(logDir),
                this.#getErrorLogTransport(logDir)
            ],
        });
    }

    #getLogFormat(){
        return winston.format.printf(({ level, message, label, timestamp, data }) => {
            return `${timestamp} [${level.toUpperCase()}]: ${message}`;
        });
    }

    /**
     * @param {string} logPath 
     * @returns {winston.transport}
     */
    #getCombinedLogTransport(logPath){
        return new winston.transports.DailyRotateFile({
            filename: 'application-%DATE%.log',
            dirname: logPath,
            datePattern: 'YYYY-MM-DD-HH',
            zippedArchive: true,
            maxSize: '10m',
            maxFiles: '7d'
        });
    }

    /**
     * @param {string} logPath 
     * @returns {winston.transport}
     */
    #getErrorLogTransport(logPath){
        return new winston.transports.DailyRotateFile({
            filename: 'error-%DATE%.log',
            dirname: logPath,
            datePattern: 'YYYY-MM-DD-HH',
            zippedArchive: true,
            maxSize: '20m',
            maxFiles: '7d',
            level: 'error'
        });
    }

    /**
     * Logs to file
     * @param  {...any} obj objects to log 
     */
    info(...obj) {
        this.#writeLog('info', obj);
    }

    /**
     * Logs to file
     * @param  {...any} obj objects to log 
     */
    debug(...obj) {
        this.#writeLog('debug', obj);
    }

    /**
     * Logs to file
     * @param  {...any} obj objects to log 
     */
    error(...obj) {
        this.#writeLog('error', obj);
    }

    /**
     * Logs to file
     * @param  {...any} obj objects to log 
     */
    warn(...obj) {
        this.#writeLog('warn', obj);
    }

    /**
     * Logs to file
     * @param  {...any} obj objects to log 
     */
    log(...obj) {
        this.#writeLog('verbose', obj);
    }

    /**
     * Logs to separate file
     * @param  {...any} obj objects to log 
     */
    remark(...obj) {
        this.#writeLog('verbose', obj);
    }

    #writeLog(level, params) {
        let msgParts = [];

        for (let param of params) {
            console.log(param);
            msgParts.push(this.#stringifyParam(param));
        }

        this.#internLogger.log(level, msgParts.join(",  "));
    }

    #stringifyParam(param) {
        if (typeof param === 'string' || param instanceof String) {
            return param;
        } else if (Array.isArray(param)) {
            return '[' + param.join(", ") + ']';
        } else if (param instanceof Error) {
            return param.stack;
        } else if (param !== null && typeof param === 'object') {
            return "\n    " + JSON.stringify(param);
        }

        return param;
    }
}