export const TTY_COLORS = {
  reset: "\x1b[0m",
  bright: "\x1b[1m",
  dim: "\x1b[2m",
  undim: "\x1b[22m",
  underscore: "\x1b[4m",
  blink: "\x1b[5m",
  reverse: "\x1b[7m",
  hidden: "\x1b[8m",
  fg: {
    black: "\x1b[30m",
    red: "\x1b[31m",
    green: "\x1b[32m",
    yellow: "\x1b[33m",
    blue: "\x1b[34m",
    magenta: "\x1b[35m",
    cyan: "\x1b[36m",
    white: "\x1b[37m",
  },
  bg: {
    black: "\x1b[40m",
    red: "\x1b[41m",
    green: "\x1b[42m",
    yellow: "\x1b[43m",
    blue: "\x1b[44m",
    magenta: "\x1b[45m",
    cyan: "\x1b[46m",
    white: "\x1b[47m",
  },
} as const;

export type LogLevel = "debug" | "info" | "success" | "warn" | "error";

export const levels = ["debug", "info", "success", "warn", "error"] as const;

export function shouldPublishLog(currentLogLevel: LogLevel, logLevel: LogLevel): boolean {
  return levels.indexOf(logLevel) >= levels.indexOf(currentLogLevel);
}

export interface Logger {
  disabled?: boolean | undefined;
  disableColors?: boolean | undefined;
  level?: Exclude<LogLevel, "success"> | undefined;
  // biome-ignore lint/suspicious/noExplicitAny: Dynamic log arguments from user code
  log?: ((level: Exclude<LogLevel, "success">, message: string, ...args: any[]) => void) | undefined;
}

export type LogHandlerParams = Parameters<NonNullable<Logger["log"]>> extends [LogLevel, ...infer Rest] ? Rest : never;

const levelColors: Record<LogLevel, string> = {
  info: TTY_COLORS.fg.blue,
  success: TTY_COLORS.fg.green,
  warn: TTY_COLORS.fg.yellow,
  error: TTY_COLORS.fg.red,
  debug: TTY_COLORS.fg.magenta,
};

const formatMessage = (level: LogLevel, message: string, colorsEnabled: boolean): string => {
  const timestamp = new Date().toISOString();

  if (colorsEnabled) {
    return `${TTY_COLORS.dim}${timestamp}${TTY_COLORS.reset} ${
      levelColors[level]
    }${level.toUpperCase()}${TTY_COLORS.reset} ${TTY_COLORS.bright}[FMODATA]:${TTY_COLORS.reset} ${message}`;
  }

  return `${timestamp} ${level.toUpperCase()} [FMODATA]: ${message}`;
};

export type InternalLogger = {
  [K in LogLevel]: (...params: LogHandlerParams) => void;
} & {
  get level(): LogLevel;
};

export const createLogger = (options?: Logger | undefined): InternalLogger => {
  const enabled = options?.disabled !== true;
  const logLevel = options?.level ?? "error";

  const colorsEnabled = options?.disableColors !== true;

  // biome-ignore lint/suspicious/noExplicitAny: Dynamic log arguments from user code
  const LogFunc = (level: LogLevel, message: string, args: any[] = []): void => {
    if (!(enabled && shouldPublishLog(logLevel, level))) {
      return;
    }

    const formattedMessage = formatMessage(level, message, colorsEnabled);

    if (!options || typeof options.log !== "function") {
      if (level === "error") {
        console.error(formattedMessage, ...args);
      } else if (level === "warn") {
        console.warn(formattedMessage, ...args);
      } else {
        console.log(formattedMessage, ...args);
      }
      return;
    }

    options.log(level === "success" ? "info" : level, message, ...args);
  };

  const logger = Object.fromEntries(
    levels.map((level) => [level, (...[message, ...args]: LogHandlerParams) => LogFunc(level, message, args)]),
  ) as Record<LogLevel, (...params: LogHandlerParams) => void>;

  return {
    ...logger,
    get level() {
      return logLevel;
    },
  };
};

export const logger = createLogger();
