import { useEffect } from "react";
import { identity } from "ramda";

import MockLogger from "./const";

const mockObj = new MockLogger("mock", "native-utils");

/**
 * This method dynamically imports quick-brick-xray
 * It allows us to remove Xray as a dependency, and it returns everything the same way you would have expected from the direct dependency
 * If plugin is not avaiable it will throw a warning in the console, but it should not prevent the app from rendering.
 * When you have no Xray plugin defined either in Zapp or as a direct dependency of your package you will get a mock version of Xray.
 * This is a way of deprecating the expectation that Xray is globally available, encourages adding the dependency or handling the lack of the plugin
 * Using this in lieu of importing Xray directly on native plugins and packages, protects against crashing the app for lack of plugin
 * To use the Xray context methods use methods such as useXray, or withXray which expose additional methods for toast, and remote debugger
 * On DOM applications we added Xray to the context provider to the root of the app of the app so it could be used via context as well
 * DOM App: https://github.com/applicaster/QuickBrick/blob/master/packages/zapp-react-dom-app/index.js#L23
 * Provider: https://github.com/applicaster/Zapp-Frameworks/blob/master/plugins/quick-brick-xray/src/plugin/XrayContextProvider.tsx#L22
 * Use getUtilsLogger if you want to use the native-utils logger that is already instantiated on app launch
 */
export const getXray = (): Xray => {
  try {
    const xray = require("@applicaster/quick-brick-xray");

    return { ...xray, Logger: xray.default };
  } catch (error) {
    // eslint-disable-next-line no-console
    console.trace(
      "To use Xray you will need to add it as a direct dependency in your package.json or you need to add it using Zapp. "
    );

    return { Logger: MockLogger, withXray: identity } as Xray;
  }
};

/**
 * This method uses dynamically imported quick-brick-xray and it returns the core loggers
 * At the moment this is only used in the logger/index.ts but its exported methods are used all over
 * Many components that use this logger instead of instantiating its own logger
 */
export const getUtilsLogger = (): XrayUtils => {
  const xray = getXray();
  const { Logger } = xray || {};

  if (xray && !Logger.mock) {
    const quickBrickLogger = new Logger("QuickBrick", "app");
    const coreLogger = quickBrickLogger.addSubsystem("core");
    const logger = quickBrickLogger.addSubsystem("plugins");
    const logLevels = Logger.logLevels;
    const utilsLogger = coreLogger.addSubsystem("utils");

    return { quickBrickLogger, coreLogger, logger, logLevels, utilsLogger };
  }

  return {
    quickBrickLogger: mockObj,
    coreLogger: mockObj,
    logger: mockObj,
    logLevels: MockLogger.logLevels,
    utilsLogger: mockObj,
  }; // TODO: check if this is the best approach
};

const { quickBrickLogger, coreLogger, logger, logLevels, utilsLogger } =
  getUtilsLogger();

/**
 * useLogger is a React hook that can be used within the React lifecycle
 * This method is not one of the context methods, it uses the instance of xray
 * created in zapp-react-native-utils
 */
function useLogger(
  logger: XRayLoggerI,
  eventData: UseLoggerHookArgs,
  deps: string[] = []
) {
  const {
    level = logLevels.verbose,
    logIf = () => true,
    ...logEvent
  } = eventData;

  useEffect(() => {
    if (logIf()) {
      logger?.[level]?.(logEvent);
    }
  }, deps);
}

const createLogger = ({
  category = "General",
  subsystem = "MyPlugin",
  parent = null,
}) => {
  const XRayLogger = getXray().Logger as any;
  const logger = new XRayLogger(category, subsystem, parent);

  const log_verbose = (message: string, data: any = null) => {
    logger.log({
      message,
      data,
    });
  };

  const log_debug = (message: string, data: any = null) => {
    logger.debug({
      message,
      data,
    });
  };

  const log_info = (message: string, data: any = null) => {
    logger.info({
      message,
      data,
    });
  };

  const log_warning = (message: string, data: any = null) => {
    logger.warning({
      message,
      data,
    });
  };

  const log_error = (message: string, data: any = null) => {
    logger.error({
      message,
      data,
    });
  };

  return { log_verbose, log_debug, log_info, log_error, log_warning };
};

export {
  createLogger,
  useLogger,
  quickBrickLogger,
  coreLogger,
  logger,
  logLevels,
  utilsLogger,
};
