/* eslint-disable */
// This script is meant to be executed in the root directory of https://github.com/Consensys/doc.linea, which has different linting rules
// The purpose of this script is to modify the sidebars.js file to correctly include the autogenerated smart contract documentation

/**
 * PURPOSE
 *
 * Modifies sidebars.js in the root directory of https://github.com/Consensys/doc.linea to correctly include smart contract documentation pages.
 * The sidebars.js file configures the sidebar for the documentation website @ https://docs.linea.build/
 */

const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");

/**
 * TYPES
 */

/**
 * Represents a sidebar section - see https://docusaurus.io/docs/sidebar/items#sidebar-item-category
 * @property {'category'} type - Type of sidebar item
 * @property {number} label - Sidebar label text
 * @property {null} link - Sidebar item link, set to null so only collapsing/expanding the sidebar occurs on click
 * @property {boolean} collapsible - If true, sidebar item can be collapsed. If false, sidebar item is permanently expanded
 * @property {string | FolderSidebar} items - Items contained in this sidebar section
 */
class FolderSidebar {
  type = "category";
  label = "";
  link = null;
  collapsible = true;
  items = [];

  constructor(label = "", collapsible = true) {
    this.label = label;
    this.collapsible = collapsible;
  }
}

/**
 * CONSTANTS
 */
const SIDEBAR_FILE_PATH = "sidebars.js";
const SMART_CONTRACT_SIDEBAR_LABEL = "Linea smart contracts";

// Import the sidebar JS object from sidebars.js
const sidebarObject = require(path.join(__dirname, SIDEBAR_FILE_PATH));

/**
 * MAIN FUNCTION
 */

main();

function main() {
  removeExistingSmartContractSidebar(sidebarObject);
  const smartContractSidebarNode = getSmartContractSidebar();
  sidebarObject?.apiSidebar?.push(smartContractSidebarNode);
  createNewSidebarFile(sidebarObject);
}

/**
 * HELPER FUNCTIONS
 */

/**
 * Remove existing smart contract section from sidebar object
 * @param {FolderSidebar} sidebarObject
 */
function removeExistingSmartContractSidebar(sidebarObject) {
  if (sidebarObject?.apiSidebar) {
    sidebarObject.apiSidebar = sidebarObject?.apiSidebar.filter(
      (sidebarSection) => sidebarSection?.label !== SMART_CONTRACT_SIDEBAR_LABEL,
    );
  }
}

/**
 * Creates FolderSidebar representing folder structure of smart contract documentation pages
 * @returns {FolderSidebar}
 */
function getSmartContractSidebar() {
  // Create and populate smart contract sidebar
  const smartContractsPath = path.join(__dirname, "docs", "api", "linea-smart-contracts");

  let smartContractSidebar = new FolderSidebar(
    SMART_CONTRACT_SIDEBAR_LABEL,
    // Prefer having the smart contract sidebar section permanently expanded to fill out the main sidebar
    false,
  );

  populateFolderSidebar(smartContractSidebar, smartContractsPath, ".mdx");

  return smartContractSidebar;
}

/**
 * Recursive function to populate FolderSidebar for a given folder
 * Performs depth-first search (DFS) of the folder tree
 * @param {FolderSidebar} folderSidebar - Mutated throughout the function body. Mutated object is then returned by the function.
 * @param {string} subdirectoryPath - folder path
 * @param {string} fileExtension
 * @return {FolderSidebar}
 */
function populateFolderSidebar(folderSidebar, subdirectoryPath, fileExtension) {
  const folderFileList = fs.readdirSync(subdirectoryPath);

  // Ensure *.mdx files at the top of the sidebar section
  for (const fileNode of folderFileList) {
    const filePath = path.join(subdirectoryPath, fileNode);
    const fileMetadata = fs.statSync(filePath);

    // Base case => *.mdx file => Add relative path to sidebar
    if (fileMetadata.isFile() && fileNode.endsWith(fileExtension)) {
      const relativePath = path.relative(path.join(__dirname, "docs"), filePath.split(fileExtension)[0]);
      folderSidebar?.items.push(relativePath);
    }
  }

  // Ensure directories are below *.mdx files in the sidebar section
  for (const fileNode of folderFileList) {
    const filePath = path.join(subdirectoryPath, fileNode);
    const fileMetadata = fs.statSync(filePath);

    // Directory => Create new child FolderSidebar, then make recursive call to populate new child FolderSidebar
    if (fileMetadata.isDirectory()) {
      let newFolderNode = new FolderSidebar(fileNode);
      populateFolderSidebar(newFolderNode, filePath, fileExtension);
      // Add populated child FolderSidebar to current sidebar section
      folderSidebar?.items.push(newFolderNode);
    }
  }

  // Not a *.mdx file or directory => Do nothing

  return folderSidebar;
}

/**
 * Overwrites existing sidebars.js file with modified sidebar object
 * @param {FolderSidebar} sidebarObject - Entire sidebar object to save
 */
function createNewSidebarFile(sidebarObject) {
  // Create new sidebars.js file content
  const sidebarFileLine1 = "/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */";
  const sidebarFileLine2 = "const sidebars =";
  const sidebarFileLineFinal = "module.exports = sidebars;";
  const newSidebarFileContent = `${sidebarFileLine1}\n${sidebarFileLine2}\n${JSON.stringify(sidebarObject, null, 2)}\n\n${sidebarFileLineFinal}`;

  // Overwrite existing sidebars.js
  const newSidebarFilePath = path.join(__dirname, SIDEBAR_FILE_PATH);
  fs.writeFileSync(newSidebarFilePath, newSidebarFileContent);

  // Do linting
  lintJSFile(newSidebarFilePath);
}

/**
 * Execute linting of Javascript file
 * @param {string} filePath
 */
function lintJSFile(filePath) {
  try {
    const installCmd = `npm install --save-dev --no-save eslint`;
    execSync(installCmd, { stdio: "inherit" });

    const lintCmd = `npx eslint --fix --no-ignore ${filePath}`;
    // Execute command synchronously and route output directly to the current stdout
    execSync(lintCmd, { stdio: "inherit" });
  } catch (error) {
    console.error(`Error:`, error.message);
    console.error(`Exiting...`);
    process.exit(1);
  }
}
