import { createColors } from 'colorette'
import dayjs from 'dayjs'
import pino, { type BaseLogger } from 'pino'
import { isProxy, toRaw } from 'vue'

const { blue, cyan, green, red } = createColors({ useColor: true })

export class LoggingService {
  private _logger: BaseLogger

  constructor() {
    this._logger = pino({
      browser: {
        asObject: true,
        transmit: {
          level: import.meta.env.VITE_LOG_LEVEL || 'error',
          send: (level, logEvent) => {
            const { ts, messages } = logEvent
            const logStr: string[] = [dayjs(ts).format('HH:mm:ss.SSS')]
            logStr.push(this.colors[level](level.toUpperCase()))
            const firstMessage = messages.shift()
            if (firstMessage.type === 'Error') {
              logStr.push(red(firstMessage.message))
              logStr.push(red(firstMessage.stack || ''))
            } else if (typeof firstMessage === 'string') {
              logStr.push(cyan(firstMessage))
            } else {
              messages.unshift(firstMessage)
            }
            console.log(`${logStr.join(' ')}:`, ...messages)
          }
        }
      }
    })
  }

  private get colors(): any {
    return {
      info: green,
      debug: blue,
      error: red
    }
  }

  debug(message: string, data?: any) {
    this._logger.debug(message, data)
  }

  info(message: string, data?: any) {
    this._logger.info(this._getMessageWidthObject(message, data))
  }

  warn(message: string, data?: any) {
    this._logger.warn(this._getMessageWidthObject(message, data))
  }

  error(error: any, message?: string) {
    this._logger.error(error, message)
  }

  fatal(message: string, data?: any) {
    this._logger.fatal(this._getMessageWidthObject(message, data))
  }

  trace(message: string, data?: any) {
    this._logger.trace(this._getMessageWidthObject(message, data))
  }

  object(message: any) {
    this._logger.info(this._getStringObject(message))
  }

  _getMessageWidthObject(message: string, data?: any) {
    if (!data) {
      return message
    }
    return `${message}\n${this._getStringObject(data)}`
  }

  _getStringObject(data: any): any {
    if (isProxy(data)) {
      return this._getStringObject(toRaw(data))
    }
    return JSON.stringify(data, null, 2)
  }
}
