import { logLevels } from '../constants';
import {
  type Logger,
  type MessageContext,
  type MessageEventHandler,
} from '../types';

const noopLevelMethod = () => {
  return undefined;
};

const mockLoggerPrototype: any = Object.create(Function.prototype);

mockLoggerPrototype.adopt = async function (_routine: any) {
  return _routine();
};

// A mock logger has no observable state: every log method is a noop,
// `getContext` always returns an empty object and the child context is
// discarded. Returning `this` therefore produces a logger indistinguishable
// from a freshly constructed one, while avoiding a function allocation and an
// `Object.setPrototypeOf` call (~75ns) per child.
mockLoggerPrototype.child = function (this: any) {
  return this;
};

mockLoggerPrototype.getContext = function () {
  return {};
};

for (const logLevelName of Object.keys(logLevels) as Array<
  keyof typeof logLevels
>) {
  mockLoggerPrototype[logLevelName] = noopLevelMethod;
  mockLoggerPrototype[logLevelName + 'Once'] = noopLevelMethod;
}

export const createMockLogger = (
  onMessage: MessageEventHandler,
  parentContext?: MessageContext,
): Logger => {
  const log: any = () => {
    return undefined;
  };

  log.onMessage = onMessage;
  log.parentMessageContext = parentContext;

  Object.setPrototypeOf(log, mockLoggerPrototype);

  return log as Logger;
};
