declare global {
  interface Window {
    inobyModules: InobyModule[];
    wasReady: boolean;
  }
}

/**
 * Base class for inoby modules.
 */
export abstract class InobyModule {
  runned: boolean = false;

  abstract run(): any;

  runOnReady(): any {
    InobyModuleRunner.runOnReady(this);
  }
}

/**
 * Base class for inoby component modules.
 */
export abstract class InobyComponentModule extends InobyModule {
  /**
   * Is component runnned in admin
   */
  public runnedAdmin: boolean = false;

  /**
   * Metabox component ID
   */
  public abstract metaboxId: string;

  /**
   * Run this function if in admin
   */
  public abstract runAdmin(): any;

  /**
   * Run this function in frontend
   */
  public abstract run(): any;
}

/**
 * Runs inoby module when document is ready.
 */
class InobyModuleRunner {
  isReady: boolean = false;
  static instance: InobyModuleRunner;

  public static addModule(module: InobyModule) {
    if (!window.inobyModules) {
      window.inobyModules = [];
    }
    window.inobyModules.push(module);
  }

  /**
   * Gets the singleton instance.
   * @returns singleton of InobyModuleRunner.
   */
  public static getInstance(): InobyModuleRunner {
    if (!InobyModuleRunner.instance) {
      InobyModuleRunner.instance = new InobyModuleRunner();
    }
    return InobyModuleRunner.instance;
  }

  /**
   * Runs the module when the document is ready.
   * @param module The module to be run.
   */
  static runOnReady(module: InobyModule) {
    InobyModuleRunner.addModule(module);
    if (window.wasReady) {
      InobyModuleRunner.runModule(module);
    }
  }

  /**
   * On document ready callback.
   * Runs all registred modules.
   */
  static onReady() {
    window.wasReady = true;
    window.inobyModules?.forEach((module) => {
      InobyModuleRunner.runModule(module);
    });
  }

  /**
   * Runs the module and marks it as run.
   * @param module The module to be run.
   */
  static runModule(module: InobyModule) {
    if (
      module instanceof InobyComponentModule &&
      window?.adminpage &&
      !module.runnedAdmin
    ) {
      
      /**
       * On backend run this module when preview is loaded
       * @see https://docs.metabox.io/extensions/mb-blocks/#enqueue_script
      */
     $(document).on('ready', () => {
        module.runnedAdmin = true;
        module.runAdmin();
      });
    } else {
      if (!module.runned) {
        module.runned = true;
        module.run();
        if (typeof (module as any).runAsync === "function") {
          setTimeout(() => {
            (module as any).runAsync();
          }, 200);
        }
      }
    }
  }
}

if (document.readyState !== "loading") {
  InobyModuleRunner.onReady();
} else {
  document.addEventListener("DOMContentLoaded", () =>
    InobyModuleRunner.onReady()
  );
}
