import { Deque } from "../../../core/collection/queue/Deque.js";
import { LoggerBackend } from "../LoggerBackend.js";

/**
 Transformer function to transform log data as provided by winston into
 a message structure which is more appropriate for indexing in ES.

 @param {Object} logData
 @param {Object} logData.message - the log message
 @param {Object} logData.level - the log level
 @param {Object} logData.meta - the log meta data (JSON object)
 @returns {Object} transformed message
 */
function defaultTransformer(logData) {
    const transformed = {};
    transformed['@timestamp'] = logData.timestamp ? logData.timestamp : new Date().toISOString();
    transformed.message = logData.message;
    transformed.severity = logData.level;
    transformed.fields = logData.meta;

    if (logData.meta['transaction.id']) transformed.transaction = { id: logData.meta['transaction.id'] };
    if (logData.meta['trace.id']) transformed.trace = { id: logData.meta['trace.id'] };
    if (logData.meta['span.id']) transformed.span = { id: logData.meta['span.id'] };

    return transformed;
}

class Record {
    constructor() {
        this.time = 0;
        this.message = 0;
        this.level = 0;
    }
}

export class ElasticSearchLogger extends LoggerBackend {
    constructor({
                    url = 'http://localhost:9200',
                    target = `log-${new Date().toISOString()}`
                } = {}) {
        super();

        /**
         *
         * @type {Deque<Record>}
         * @private
         */
        this.__buffer = new Deque();

        /**
         * Number of records
         * @type {number}
         * @private
         */
        this.__flush_size = 500;
        /**
         * In milliseconds
         * @type {number}
         * @private
         */
        this.__flush_timeout = 2000;

        /**
         *
         * @type {string}
         * @private
         */
        this.__url = url;

        /**
         *
         * @type {string}
         * @private
         */
        this.__target = target;

        /**
         * setTimeout handle
         * @type {number}
         * @private
         */
        this.__timeout_ms = -1;

        /**
         *
         * @type {any}
         * @private
         */
        this.__bound_flush = this.flush.bind(this);
    }

    flush() {

        // clear timeout
        if (this.__timeout !== -1) {
            clearTimeout(this.__timeout);
            this.__timeout = -1;
        }

        const buffer = this.__buffer;

        if (buffer.isEmpty()) {
            // do nothing
            return;
        }

        const data = [];

        while (!buffer.isEmpty()) {
            const record = buffer.removeFirst();

            data.push({
                create: {
                    '@timestamp': record.time,
                    'level': record.level,
                    'message': record.message
                }
            });
        }

        const xhr = new XMLHttpRequest();

        xhr.onreadystatechange = (evt) => {
            console.warn(evt);
        };

        xhr.open('POST', `${this.__url}/${this.__target}/_bulk`, true);
        xhr.overrideMimeType('text/plain');

        xhr.send(JSON.stringify(data));
    }

    __prod() {
        const buffer_size = this.__buffer.size();
        if (buffer_size >= this.__flush_size) {
            this.flush();
        } else if (this.__timeout === -1) {
            this.__timeout = setTimeout(this.__bound_flush, this.__flush_timeout);
        }
    }

    log(level, message) {
        const record = new Record();

        record.message = message;
        record.level = level;
        record.time = performance.now();

        this.__buffer.addLast(record);

        this.__prod();
    }
}
