import fs from "fs";
import { task } from "hardhat/config";
import { HardhatPluginError } from "hardhat/plugins";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import path from "path";

const paths = [
  "@fhenixprotocol/cofhe-mock-contracts/ACL.sol",
  "@fhenixprotocol/cofhe-mock-contracts/MockCoFHE.sol",
  "@fhenixprotocol/cofhe-mock-contracts/MockQueryDecrypter.sol",
  "@fhenixprotocol/cofhe-mock-contracts/MockTaskManager.sol",
  "@fhenixprotocol/cofhe-mock-contracts/MockZkVerifier.sol",
  "@fhenixprotocol/cofhe-mock-contracts/TestBed.sol",
];
const pluginName = "cofhe-mock-contracts";

const generate = function (dependency: string) {
  return [
    "// SPDX-License-Identifier: UNLICENSED",
    "pragma solidity >0.0.0;",
    `import '${dependency}';`,
  ]
    .map((l) => `${l}\n`)
    .join("");
};

export const compileMockContractPaths = async (
  hre: HardhatRuntimeEnvironment,
) => {
  // other packages may incorrectly set a relative sources path so it must be explicitly resolved
  const sources = path.resolve(hre.config.paths.sources);

  const directory = path.resolve(sources, pluginName);
  const tracker = path.resolve(directory, `.${pluginName}`);

  if (!fs.existsSync(sources)) {
    fs.mkdirSync(sources);
  }

  if (!directory.startsWith(sources)) {
    throw new HardhatPluginError(
      pluginName,
      "resolved path must be inside of sources directory",
    );
  }

  if (directory === sources) {
    throw new HardhatPluginError(
      pluginName,
      "resolved path must not be sources directory",
    );
  }

  if (fs.existsSync(directory)) {
    // delete directory only if tracker is found or directory is empty
    if (fs.existsSync(tracker) || fs.readdirSync(directory).length === 0) {
      fs.rmSync(directory, { recursive: true });
    } else {
      throw new HardhatPluginError(
        pluginName,
        `temporary source directory must have been generated by ${pluginName}`,
      );
    }
  }

  fs.mkdirSync(directory);
  fs.writeFileSync(
    tracker,
    `directory approved for write access by ${pluginName}\n`,
  );

  for (const dependency of paths) {
    const fullPath = path.join(directory, dependency);

    if (!fs.existsSync(path.dirname(fullPath))) {
      fs.mkdirSync(path.dirname(fullPath), { recursive: true });
    }

    fs.writeFileSync(fullPath, generate(dependency));
  }

  try {
    await hre.run("compile:silent");
  } finally {
    fs.rmSync(directory, { recursive: true });
  }
};

// Override the compile:silent task to filter out warning logs
task("compile:silent", "Compiles without printing warnings").setAction(
  async (args, hre, runSuper) => {
    // Store original console methods
    const originalLog = console.log;
    const originalWarn = console.warn;
    const originalError = console.error;

    // Override console methods to filter warnings
    console.log = (...args) => {
      const logString = args.join(" ");
      if (!logString.includes("Warning")) {
        originalLog.apply(console, args);
      }
    };

    console.warn = (...args) => {
      const logString = args.join(" ");
      if (!logString.includes("Warning")) {
        originalWarn.apply(console, args);
      }
    };

    console.error = (...args) => {
      const logString = args.join(" ");
      if (!logString.includes("Warning")) {
        originalError.apply(console, args);
      }
    };

    try {
      // Run the original compile task
      await hre.run("compile");
    } finally {
      // Restore original console methods
      console.log = originalLog;
      console.warn = originalWarn;
      console.error = originalError;
    }
  },
);
