import path from "path";
import fs from "fs";
import http from "http";
import { Initializer, Connection, api, log, config, route } from "actionhero";

declare module "actionhero" {
  export interface Api {
    next: {
      app?: any;
      render?: (Connection: Connection) => void;
      handle?: (req: http.ClientRequest, res: http.ServerResponse) => void;
    };
  }
}

export class Next extends Initializer {
  constructor() {
    super();
    this.name = "next";
    this.loadPriority = 1000;
    this.startPriority = 899;
    this.stopPriority = 101;
  }

  async initialize() {
    api.next = {
      render: async (connection: Connection) => {
        if (connection.type !== "web") {
          throw new Error('Connections for next.js apps must be of type "web"');
        }
        const { req, res } = connection.rawConnection;
        return api.next.handle(req, res);
      },
    };
  }

  async start() {
    if (config.web.enabled !== true) return;

    if (!config.next.enabled) {
      log("next disabled");
      return;
    }

    if (config.next.dev) {
      log("Running next in development mode...");
    }

    // We don't want to statically import next until we know we need it. It loads a lot and has problems in test mode
    // We also don't want next to be part of grouparoo/core, but to come from the UI plugins (could be locally installed or hoisted)
    const nextWithinPluginPath = path.join(
      config.general.paths.next[0],
      "node_modules",
      "next"
    );
    const next = await import(
      fs.existsSync(nextWithinPluginPath) ? nextWithinPluginPath : "next"
    );
    const conf = await import(
      path.join(config.general.paths.next[0], "next.config.js")
    );

    api.next.app = next.default({
      dev: config.next.dev,
      quiet: config.next.quiet,
      dir: config.general.paths.next[0],
      conf,
    });

    api.next.handle = api.next.app.getRequestHandler();
    await api.next.app.prepare();

    if (config.web.enabled === true) {
      route.registerRoute("get", "/", "next:render", null, true);
    }
  }

  async stop() {
    if (api.next.app) await api.next.app.close();
  }
}
