import * as path from 'path'
import { createStream } from "rotating-file-stream"
import { BaseLogger, ILogObjMeta, ISettingsParam, Logger } from "tslog"
import { CONFIG_DEFAULT, resolve } from '@plastichub/osr-commons'
import { LogLevelEx } from './zod_schema'
import { sync as read } from '@plastichub/fs/read'
import { sync as write } from '@plastichub/fs/write'
import { sync as exists } from '@plastichub/fs/exists'
export * from './zod_schema'

import * as TransportStream from 'winston-transport'
const { SeqTransport } = require('@datalust/winston-seq')
import * as winston from 'winston'

export enum ELogTargets {
    Console = 1 << 0,
    FileText = 1 << 1,
    FileJson = 1 << 2,
    Seq = 1 << 3
}

export function createLogger(name: string, options?: ISettingsParam<any>) {
    return new Logger<unknown>({
        name,
        type: 'pretty',
        ...options,
    })
}
export const defaultLogger = createLogger('DefaultLogger', {
    minLevel: LogLevelEx.info
})
export class CustomLogger<LogObj> extends BaseLogger<LogObj> {
    constructor(settings?: ISettingsParam<LogObj>, logObj?: LogObj) {
        super(settings, logObj, 5)
    }
    public custom(loggerName?: string, ...args: unknown[]): LogObj & ILogObjMeta {
        return super.log(8, loggerName || 'Custom', ...args)
    }
}

class JsonArrayFileTransport extends TransportStream {
    filename: string;
    constructor(opts) {
        super(opts);
        opts.filename = opts.filename
        this.filename = opts.filename || 'logs.json';
        setImmediate(() => this.emit('opened'))
    }
    log(info: any, next: () => void): void {
        setImmediate(() => this.emit('logged', info))
        const { level, message, exception, stack, ...props } = info;
        const fileExists = exists(this.filename)
        const existingLogs = fileExists
            ? read(this.filename, 'json') as []
            : [];

        const entry = {
            level: info.level,
            message: info.message,
            timestamp: new Date().toISOString(),
            ...info
        };
        existingLogs.push(entry)
        write(this.filename, existingLogs)
        next()
    }
    close(): void {
        setImmediate(() => this.emit('closed'))
    }
    flush(): Promise<any> {
        return new Promise((resolve, reject) => {
            resolve(true)
        })
    }
}

class TSLogTransport extends TransportStream {
    constructor(opts) {
        super(opts);
        setImmediate(() => this.emit('opened'))
    }
    log(info: any, next: () => void): void {
        setImmediate(() => this.emit('logged', info))
        const { level, message, exception, stack, ...props } = info;
        defaultLogger.info(info)
        next()
    }
}

export const winstonLogger = (name: string, file: string, targets: ELogTargets = ELogTargets.Console | ELogTargets.FileJson)  => {
    const logger = winston.createLogger({
        defaultMeta: { service: name },
        level: 'debug',
        transports: []
    })
    if (targets & ELogTargets.Console) {
        //logger.add(new TSLogTransport({}))
        logger.add(new winston.transports.Console({
            format: winston.format.combine(
                winston.format.timestamp({ format: 'MM/DD/YYYY hh:mm:ss.SSS' }),
                ///winston.format.json(),
                winston.format.colorize(),
                winston.format.printf(info => {
                    let message = null
                    try {
                        message = JSON.stringify(info.message)
                    }   catch (e) { 
                        
                    }
                    return `[${info.level}] [${name}] | message: ${message.substring(0, 200)}`
                }))
        }))
    }

    if (targets & ELogTargets.FileText) {
        logger.add(new winston.transports.File({
            format: winston.format.combine(
                winston.format.timestamp(),
                winston.format.timestamp({ format: 'MM/DD/YYYY hh:mm:ss.SSS' }),
                winston.format.json(),
                winston.format.printf(info => {
                    return JSON.stringify(info, null, 2);
                })),
            dirname: path.parse(file).dir,
            filename: path.parse(file).base
        }))
    }

    if (targets & ELogTargets.FileJson) {
        logger.add(new JsonArrayFileTransport({
            filename: file
        }))
    }

    if (targets & ELogTargets.Seq) {
        const config = CONFIG_DEFAULT() as any
        if (config.seq) {
            logger.add(new SeqTransport({
                ...config.seq,
                //serverUrl: "https://localhost:5341",
                //apiKey: "Qu6XPThyfnXo7ugXoRZS",
                onError: (e => { })
            }))
        }
    }
    return logger
}

export const createFileLogger = (logger: Logger<unknown>, level: number, file: string): Logger<unknown> => {
    const rfs = createStream(file,
        {
            size: "10M", // rotate every 10 MegaBytes written
            interval: "1d", // rotate daily
            compress: "gzip", // compress rotated files
        });

    const log = new Logger({
        type: "json",
        attachedTransports: [
            (logObj) => {
                rfs.write(JSON.stringify(logObj) + "\n");
            },
        ],
    });
    return log
}
