import {LogInfo, LogLevel} from '@interopio/gateway-server/gateway-ent';
import {IOGateway} from '@interopio/gateway';
import {format} from 'node:util';

class LogInfoAdapter implements LogInfo {
    private _parsed?: { err?: Error, msg: string };
    private _timestamp?: string;
    private _output?: string;

    constructor(private readonly event: IOGateway.Logging.LogEvent) {
    }

    private parsed(): { err?: Error, msg: string } {
        if (this._parsed === undefined) {
            let err: Error | undefined = undefined;
            let vargs = this.event.data;
            if (this.event.data[0] instanceof Error) {
                err = this.event.data[0];
                vargs = vargs.slice(1);
            }
            const msg = format(this.event.message, ...vargs);
            this._parsed = {err, msg};
        }
        return this._parsed;
    }

    get time(): Date {
        return this.event.time;
    }

    get level(): LogLevel {
        return this.event.level;
    }

    get namespace() {
        return this.event.name;
    }

    get file(): string {
        return undefined as unknown as string;
    }

    get line(): number {
        return undefined as unknown as number;
    }

    get message(): string {
        return this.parsed().msg;
    }

    get stacktrace(): Error | undefined {
        return this.parsed().err;
    }

    private get timestamp(): string {
        if (this._timestamp === undefined) {
            this._timestamp = this.time.toISOString();
        }
        return this._timestamp;
    }

    get output(): string {
        if (this._output === undefined) {
            const err = this.parsed().err;
            const stacktrace = err ? `\n${err.stack ?? err}` : '';
            this._output = `${this.timestamp} ${this.level.toUpperCase()} [${this.namespace}] - ${this.message}${stacktrace}`;
        }
        return this._output;
    }
}

export function toLogConfig(config?: { level?: LogLevel; appender?: (info: LogInfo) => void }): IOGateway.Logging.LogConfig {
    let level: Exclude<LogLevel, 'report' | 'fatal'> = 'info';
    if (config?.level) {
        if (config.level === 'fatal') {
            level = 'error';
        } else if (config.level !== 'report') {
            level = config.level;
        }
    }
    const result: IOGateway.Logging.LogConfig = {level};
    const appenderFn = config?.appender;
    if (appenderFn) {
        result.appender = (event: IOGateway.Logging.LogEvent) => {
            appenderFn(new LogInfoAdapter(event));
        };
    }
    return result;
}
