import { isDevelopmentMode } from "@racletteCore/helpers/devMode"

/**
 * Custom logger function
 *
 * We use this mainly to format the output in the dev console, e.g.
 * in order to color code log messages and events coming from
 * different components, such as:
 *   - redux store
 *   - websocket client
 *   - http API client
 *
 * Examples:
 *
 * 1. Use generic log function
 *    log.log('my message', 'store', 'my title')
 *
 * 2. Use shortcuts for types with message/data and title as parameters
 *    log.api('GET ticket', data)
 *    log.store('My custom log event', data)
 *    log.socket('subject line', { msg: 'some message', data: {} })
 *    log.info('Simple message')
 *
 * NOTE: When using the error method, the console will point to this file.
 * For better stack traces, use console.error directly in your code.
 */

/**
 * Log levels enum
 */
export enum LogLevel {
  NoLogging = 0,
  Development = 1,
  Verbose = 2,
}

/**
 * Available log types
 */
export type LogType =
  | "orchestrator"
  | "plugin"
  | "pluginRegistrar"
  | "api"
  | "queryApi"
  | "default"
  | "error"
  | "event"
  | "info"
  | "socket"
  | "store"
  | "warning"
  | "widgets"

/**
 * Color codes for each log type
 */
const LOG_TYPE_COLORS: Record<LogType, string> = {
  orchestrator: "#359369",
  plugin: "#00f050",
  pluginRegistrar: "#00f000",
  api: "#c666ff",
  queryApi: "#f464af",
  default: "#148dbc",
  error: "#fc4b4b",
  event: "#008000",
  info: "#148dbc",
  socket: "#eee600",
  store: "#0DBC79",
  warning: "#CC8800",
  widgets: "#ad449d",
} as const

/**
 * Message can be any serializable value
 */
export type LogMessage = unknown

/**
 * Formatted log entry with timestamp
 */
interface LogEntry {
  message: LogMessage
  dateTime: string
}

/**
 * Plugin logger function type
 */
export type PluginLogger = (
  title: string,
  message?: LogMessage,
  level?: LogLevel,
) => void

/**
 * Creates a formatted log entry with timestamp
 */
function createLogEntry(message: LogMessage): LogEntry {
  const date = new Date()
  const day = date.getDate().toString().padStart(2, "0")
  const month = (date.getMonth() + 1).toString().padStart(2, "0") // Month is 0-indexed
  const year = date.getFullYear()
  const hour = date.getHours().toString().padStart(2, "0")
  const minutes = date.getMinutes().toString().padStart(2, "0")
  const seconds = date.getSeconds().toString().padStart(2, "0")
  const milliseconds = date.getMilliseconds().toString().padStart(3, "0")

  const dateTime = `${day}.${month}.${year} - ${hour}:${minutes}:${seconds}:${milliseconds}`

  return {
    message,
    dateTime,
  }
}

/**
 * Logger Class
 */
class Logger {
  private level: LogLevel

  constructor(level: LogLevel = LogLevel.Development) {
    this.level = level
  }

  /**
   * Set the log level
   */
  setLevel(level: LogLevel): void {
    this.level = level
  }

  /**
   * Get the current log level
   */
  getLevel(): LogLevel {
    return this.level
  }

  /**
   * Check if debug mode is enabled
   */
  private isDebugEnabled(): boolean {
    return isDevelopmentMode()
  }

  /**
   * Core log method
   */
  log(
    message: LogMessage,
    type: LogType = "default",
    title: string = "",
    level: LogLevel = LogLevel.Development,
  ): boolean {
    // Return early if the message level is higher than the current log level
    if (level > this.level) {
      return false
    }

    const logEntry = createLogEntry(message)
    const color = LOG_TYPE_COLORS[type]
    const label = title ? `${type}/${title}` : type

    // Handle simple log with just message
    if (arguments.length === 1) {
      console.debug(logEntry)
      return true
    }

    // Handle error logs
    if (type === "error") {
      console.error(`%c${label}`, `color: ${color}`, "\n", logEntry)
      return true
    }

    // Handle regular logs (only if debug is enabled)
    if (this.isDebugEnabled()) {
      console.debug(`%c${label}`, `color: ${color}`, "\n", logEntry)
    }

    return true
  }

  /**
   * Convenience methods for different log types
   */

  socket(title: string, message?: LogMessage, level?: LogLevel): void {
    this.log(message, "socket", title, level)
  }

  store(title: string, message?: LogMessage, level?: LogLevel): void {
    this.log(message, "store", title, level)
  }

  queryApi(title: string, message?: LogMessage, level?: LogLevel): void {
    const actionId =
      message &&
      typeof message === "object" &&
      message !== null &&
      "actionId" in message
        ? message.actionId
        : ""
    this.log(message, "queryApi", `${title} ${actionId}`.trim(), level)
  }

  api(title: string, message?: LogMessage, level?: LogLevel): void {
    this.log(message, "api", title, level)
  }

  event(title: string, message?: LogMessage, level?: LogLevel): void {
    this.log(message, "event", title, level)
  }

  error(
    title: string,
    message?: LogMessage | any,
    level: LogLevel = LogLevel.NoLogging,
  ): void {
    this.log(message, "error", title, level)
  }

  warning(title: string, message?: LogMessage, level?: LogLevel): void {
    this.log(message, "warning", title, level)
  }

  info(title: string, message?: LogMessage, level?: LogLevel): void {
    this.log(message, "info", title, level)
  }

  orchestrator(title: string, message?: LogMessage, level?: LogLevel): void {
    this.log(message, "orchestrator", title, level)
  }

  pluginRegistrar(title: string, message?: LogMessage, level?: LogLevel): void {
    this.log(message, "pluginRegistrar", title, level)
  }

  widgets(title: string, message?: LogMessage, level?: LogLevel) {
    this.log(message, "widgets", title, level)
  }

  /**
   * Create a plugin-specific logger
   */
  plugin(pluginKey: string): PluginLogger {
    return (
      title: string,
      message?: LogMessage | any,
      level?: LogLevel,
    ): void => {
      if (title === "error") {
        this.error(`plugin ${pluginKey} - ${message.error}`, message)
      } else {
        this.log(message, "plugin", `${pluginKey} - ${title}`, level)
      }
    }
  }
}

// Export both the class and singleton instance
export { Logger }
export default new Logger()

export type LoggerInstance = typeof Logger
