export abstract class Plugin {
  abstract name: string;
  abstract rewriteTriggers: string[];
  renameTo?: string;
  abstract longLasting: boolean;
  abstract build?(): void;
  isBinary = false;

  abstract rewriteFile(
    file: string | Buffer,
    filePath: string
  ): Promise<Buffer | string | undefined | null | void>;
}

import * as fs from "fs";
import * as path from "path";

export default class SSSG {
  plugins: Plugin[] = [];
  outputFolder!: string;
  inputFolder!: string;
  constructor({
    outputFolder,
    inputFolder,
  }: {
    outputFolder: string;
    inputFolder: string;
  }) {
    this.inputFolder = inputFolder;
    this.outputFolder = outputFolder;
  }

  async run({ plugins }: { plugins: Plugin[] }) {
    this.plugins = plugins;
    if (!fs.existsSync(this.outputFolder)) fs.mkdirSync(this.outputFolder);
  }

  async build() {
    console.time("Building..");
    for (const plugin of this.plugins) {
      if (plugin.build) {
        plugin.build();
      }
    }
    try {
      fs.rmSync(this.outputFolder, { recursive: true });
    } catch {}

    const sourceFiles = fs.readdirSync(this.inputFolder, {
      recursive: true,
      withFileTypes: true,
    });
    const globalPlugins = this.plugins.filter((z) =>
      z.rewriteTriggers.includes("*")
    );

    for await (const file of sourceFiles) {
      if (!file.isFile()) continue;

      const type = file.name.split(".").at(-1);
      if (!type) continue;

      const shortname = file.name.slice(
        0,
        file.name.length - (type.length + 1)
      );
      const availablePlugins = this.plugins.filter((z) =>
        z.rewriteTriggers.includes(type)
      );

      if (availablePlugins.length == 0) {
        const oldPath = path.join(file.parentPath, file.name);
        fs.cpSync(
          oldPath,
          oldPath.replace(this.inputFolder, this.outputFolder)
        );
      }

      for await (const plugin of availablePlugins) {
        const oldPath = path.join(file.parentPath, file.name);
        const newPath = path
          .join(
            file.parentPath,
            shortname +
              "." +
              (plugin.rewriteTriggers.includes("*") || plugin.renameTo == "keep"
                ? type
                : plugin.renameTo)
          )
          .replace(this.inputFolder, this.outputFolder);
        let data: string | Buffer = fs.readFileSync(oldPath);

        if (!plugin.isBinary) {
          data = data.toString("utf8");
        }

        for await (const globalPlugin of globalPlugins) {
          if (!globalPlugin.isBinary && plugin.isBinary) continue;

          const rewritten = await globalPlugin.rewriteFile(data, oldPath);
          if (!rewritten) continue;
          data = rewritten;
        }

        let rewrite = await plugin.rewriteFile(data, oldPath);

        if (!rewrite) continue;

        fs.mkdirSync(path.dirname(newPath), { recursive: true });
        fs.writeFileSync(
          newPath,
          plugin.isBinary
            ? new Uint8Array(rewrite as Buffer)
            : (rewrite as string)
        );
      }
    }
    console.timeEnd("Building..");
  }
}
