#!/usr/bin/env node

const fs = require("fs").promises;
const path = require("path");
const minimist = require("minimist");
const cliProgress = require("cli-progress");

// Parse command-line arguments
const args = minimist(process.argv.slice(2), {
  alias: { v: "verbose", h: "help" },
  boolean: ["verbose", "help"],
});

// Function to display help message
function displayHelp() {
  console.log(`
    Usage: node index.js [path] [options]
    Options:
      -v, --verbose     Enable verbose logging with progress bar
      -h, --help        Display this help message
    Provide a path directly as an argument
  `);
}

// Check for help flag
if (args.help) {
  displayHelp();
  process.exit(0);
}

// Main processing logic
async function processPath(targetPath) {
  try {
    const stats = await fs.lstat(targetPath);
    const depth = 0;

    if (stats.isDirectory()) {
      await processDirectory(targetPath, depth);
    } else if (stats.isFile()) {
      await normalizeFileName(targetPath);
    }
  } catch (error) {
    console.error(`Error processing path "${targetPath}":`, error);
  }
}

// Function to determine if a file/directory should be ignored
function shouldIgnore(itemName) {
  const ignoredItems = [".git", "node_modules", ".env"];
  return ignoredItems.includes(itemName);
}

function normalizeToNFC(str) {
  return str.normalize("NFC");
}

function normalizeToNFD(str) {
  return str.normalize("NFD");
}

// Function to normalize file names
async function normalizeFileName(filePath) {
  const dir = path.dirname(filePath);
  const oldName = path.basename(filePath);
  const newName = convertToNFC(oldName);

  if (oldName !== newName && !shouldIgnore(oldName)) {
    const newPath = path.join(dir, newName);
    try {
      await fs.rename(filePath, newPath);
      return newPath;
    } catch (error) {
      console.error(`Failed to rename "${oldName}":`, error);
      return filePath;
    }
  }
  return filePath;
}

let entriesLength = 0;

// Function to process directories recursively
async function processDirectory(dirPath, depth = 0) {
  try {
    const entries = await fs.readdir(dirPath, { withFileTypes: true });
    const filesToProcess = entries.filter(
      (entry) => !entry.isDirectory()
    ).length;
    let processedFiles = 0;
    if (args.verbose && depth == 0) {
      entriesLength = entries.length;
      console.log("Processing directory:", dirPath);
      progressBar.start(entriesLength, 0);
    }

    for (const entry of entries) {
      const fullPath = path.join(dirPath, entry.name);
      if (entry.isDirectory()) {
        if (!shouldIgnore(entry.name)) {
          await processDirectory(fullPath, depth + 1);
        }
      } else {
        await normalizeFileName(fullPath);
      }
      if (args.verbose && filesToProcess > 0 && depth == 0) {
        processedFiles++;
        progressBar.update((processedFiles / entriesLength) * entriesLength);
      }
    }
    await normalizeFileName(dirPath);

    if (args.verbose && depth == 0) {
      progressBar.stop();
    }
  } catch (error) {
    console.error(`Error processing directory "${dirPath}":`, error);
  }
}

// Initialize progress bar
const progressBar = new cliProgress.SingleBar(
  {
    format: "{bar} {percentage}% | {value}/{total}",
    clearOnComplete: false,
  },
  cliProgress.Presets.shades_classic
);

module.exports = {
  processPath,
  shouldIgnore,
  normalizeToNFC,
  normalizeToNFD,
  normalizeFileName,
};

// Handle input: if no flags, assume first non-flag argument is a path
const nonFlagArgs = args._;
if (nonFlagArgs.length > 0) {
  processPath(nonFlagArgs[0]);
} else {
  displayHelp();
}
