import { join } from "path";
import { $, cd } from "zx";
import { existsSync } from "node:fs";
import { readdir } from "node:fs/promises";
import { getTxzName, pluginName, startingDir } from "./utils/consts";
import { setupTxzEnv, TxzEnv } from "./cli/setup-txz-environment";
import { cleanupTxzFiles } from "./utils/cleanup";
import { apiDir } from "./utils/paths";

// Recursively search for manifest files
const findManifestFiles = async (dir: string): Promise<string[]> => {
  try {
    const entries = await readdir(dir, { withFileTypes: true });
    const files: string[] = [];

    for (const entry of entries) {
      const fullPath = join(dir, entry.name);
      if (entry.isDirectory()) {
        try {
          files.push(...(await findManifestFiles(fullPath)));
        } catch (error) {
          // Log and continue if a subdirectory can't be read
          console.warn(`Warning: Could not read directory ${fullPath}: ${error.message}`);
        }
      } else if (
        entry.isFile() &&
        (entry.name === "manifest.json" || entry.name === "ui.manifest.json")
      ) {
        files.push(entry.name);
      }
    }

    return files;
  } catch (error) {
    if (error.code === 'ENOENT') {
      console.warn(`Directory does not exist: ${dir}`);
      return [];
    }
    throw error; // Re-throw other errors
  }
};

const validateSourceDir = async (validatedEnv: TxzEnv) => {
  if (!validatedEnv.ci) {
    console.log("Validating TXZ source directory");
  }
  const sourceDir = join(startingDir, "source");
  if (!existsSync(sourceDir)) {
    throw new Error(`Source directory ${sourceDir} does not exist`);
  }
  // Validate existence of webcomponent files:
  // source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components
  const webcomponentDir = join(
    sourceDir,
    "dynamix.unraid.net",
    "usr",
    "local",
    "emhttp",
    "plugins",
    "dynamix.my.servers",
    "unraid-components"
  );
  if (!existsSync(webcomponentDir)) {
    throw new Error(`Webcomponent directory ${webcomponentDir} does not exist`);
  }

  const manifestFiles = await findManifestFiles(webcomponentDir);
  const hasManifest = manifestFiles.includes("manifest.json");
  const hasUiManifest = manifestFiles.includes("ui.manifest.json");

  if (!hasManifest || !hasUiManifest) {
    console.log("Existing Manifest Files:", manifestFiles);
    throw new Error(
      `Webcomponents must contain both "ui.manifest.json" and "manifest.json" - be sure to have run pnpm build:wc in unraid-ui`
    );
  }

  if (!existsSync(apiDir)) {
    throw new Error(`API directory ${apiDir} does not exist`);
  }
  const packageJson = join(apiDir, "package.json");
  if (!existsSync(packageJson)) {
    throw new Error(`API package.json file ${packageJson} does not exist`);
  }
  // Now CHMOD the api/dist directory files to allow execution
  await $`chmod +x ${apiDir}/dist/*.js`;
};

const buildTxz = async (validatedEnv: TxzEnv) => {
  await validateSourceDir(validatedEnv);
  const txzPath = join(validatedEnv.txzOutputDir, getTxzName());

  // Create package - must be run from within the pre-pack directory
  // Use cd option to run command from prePackDir
  await cd(join(startingDir, "source", pluginName));
  $.verbose = true;

  await $`${join(startingDir, "scripts/makepkg")} --chown y --compress -${
    validatedEnv.compress
  } --linkadd y ${txzPath}`;
  $.verbose = false;
  await cd(startingDir);
};

const main = async () => {
  try {
    const validatedEnv = await setupTxzEnv(process.argv);
    await cleanupTxzFiles();
    await buildTxz(validatedEnv);
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

await main();
