// Code originally derived from https://github.com/hadfieldn/node-bunyan-prettystream but since heavily edited
// Neither fork nor original repo appear to be maintained

import { Stream } from 'node:stream';
import * as util from 'node:util';
import chalk from 'chalk';
import stringify from 'json-stringify-pretty-compact';
import { regEx } from '../util/regex';
import type { BunyanRecord } from './types';

const bunyanFields = [
  'name',
  'hostname',
  'pid',
  'level',
  'v',
  'time',
  'msg',
  'start_time',
];
const metaFields = [
  'repository',
  'baseBranch',
  'packageFile',
  'depType',
  'dependency',
  'dependencies',
  'branch',
];

const levels: Record<number, string> = {
  10: chalk.gray('TRACE'),
  20: chalk.blue('DEBUG'),
  30: chalk.green(' INFO'),
  40: chalk.magenta(' WARN'),
  50: chalk.red('ERROR'),
  60: chalk.bgRed('FATAL'),
};

export function indent(str: string, leading = false): string {
  const prefix = leading ? '       ' : '';
  return prefix + str.split(regEx(/\r?\n/)).join('\n       ');
}

export function getMeta(rec: BunyanRecord): string {
  if (!rec) {
    return '';
  }
  let res = rec.module ? ` [${rec.module}]` : ``;
  const filteredMeta = metaFields.filter((elem) => rec[elem]);
  if (!filteredMeta.length) {
    return res;
  }
  const metaStr = filteredMeta
    .map((field) => `${field}=${String(rec[field])}`)
    .join(', ');
  res = ` (${metaStr})${res}`;
  return chalk.gray(res);
}

export function getDetails(rec: BunyanRecord): string {
  if (!rec) {
    return '';
  }
  const recFiltered = { ...rec };
  delete recFiltered.module;
  Object.keys(recFiltered).forEach((key) => {
    if (
      key === 'logContext' ||
      bunyanFields.includes(key) ||
      metaFields.includes(key)
    ) {
      delete recFiltered[key];
    }
  });
  const remainingKeys = Object.keys(recFiltered);
  if (remainingKeys.length === 0) {
    return '';
  }
  return `${remainingKeys
    .map((key) => `${indent(`"${key}": ${stringify(recFiltered[key])}`, true)}`)
    .join(',\n')}\n`;
}

export function formatRecord(rec: BunyanRecord): string {
  const level = levels[rec.level];
  const msg = `${indent(rec.msg)}`;
  const meta = getMeta(rec);
  const details = getDetails(rec);
  return util.format('%s: %s%s\n%s', level, msg, meta, details);
}

export class RenovateStream extends Stream {
  readable: boolean;

  writable: boolean;

  constructor() {
    super();
    this.readable = true;
    this.writable = true;
  }

  write(data: BunyanRecord): boolean {
    this.emit('data', formatRecord(data));
    return true;
  }
}
