import fs from 'fs';
import { readFile, readdir } from 'fs/promises';
import path from 'path';

/**
 * Asynchronously retrieves a list of diagrams with their corresponding private and public paths.
 *
 * This function scans the `processes` directory, recursively listing all files and filtering for those
 * with a `.bpmn` extension. It constructs and returns an array of objects, each containing the name of
 * the file, its private path (relative to the file system), and its public path (relative to the processes directory).
 *
 * @async
 * @function getDiagramsPaths
 * @returns {Promise<{name: string, privatePath: string, publicPath: string}[]>}
 *   A promise that resolves to an array of objects, each with the following properties:
 *   - `name` {string}: The name of the diagram file.
 *   - `privatePath` {string}: The full path to the diagram file in the file system.
 *   - `publicPath` {string}: The path to the diagram file relative to the `processes` directory.
 */
export async function getDiagramsPaths() {
  const directory = getProcessesPath();
  const files = await readdir(directory, {
    recursive: true,
    withFileTypes: true,
  });

  const paths = files
    .filter((file) => file.name.endsWith('.bpmn'))
    .map((file) => {
      const privatePath = path.join(file.path, file.name);
      const publicPath = path.join(file.path.replace(directory, ''), file.name);

      const finalPublicFilename = publicPath.startsWith('/') ? publicPath.slice(1) : publicPath;

      return {
        name: finalPublicFilename,
        privatePath: path.resolve(privatePath),
        publicPath: finalPublicFilename.replace(/ /g, '_'),
      };
    }).sort((a, b) => a.publicPath.localeCompare(b.publicPath));

  return paths;
}

export async function getDiagramByPath(filepath: string) {
  const diagramEntry = (await getDiagramsPaths()).find((entry) => entry.publicPath === filepath);
  if (!diagramEntry) {
    throw new Error('Diagram not found');
  }

  return readFile(diagramEntry.privatePath, 'utf8');
}

function getProcessesPath(): string {
  if (process.env.PROCESSES_DIR) {
    return process.env.PROCESSES_DIR;
  }

  const cwd = process.env.CWD ? path.resolve(process.env.CWD) : process.cwd();
  const root = path.join(cwd, 'processes');
  const parent = path.join(cwd, '..', 'processes');

  if (fs.existsSync(root)) {
    return root;
  }

  if (fs.existsSync(parent)) {
    return parent;
  }

  throw new Error(`Could not find the processes directory in ${cwd} or its parent directory.`);
}
