import path from "node:path";
import { hasOwnErrorCode } from "./error-codes.js";
import { assertContainedPath } from "./path-boundary.js";
export function resolveWorkflowPath(inputPath, cwd, homeDir) {
    if (inputPath.startsWith("~/")) {
        return path.join(homeDir, inputPath.slice(2));
    }
    if (inputPath === "~") {
        return homeDir;
    }
    return path.isAbsolute(inputPath) ? inputPath : path.resolve(cwd, inputPath);
}
function isMissingDirectory(error) {
    return hasOwnErrorCode(error, "ENOENT") || hasOwnErrorCode(error, "ENOTDIR");
}
function defaultGlobForSubDirectory(subDirectory) {
    return subDirectory.startsWith("pipeline/") ? "*.yaml" : "*.md";
}
function matchesGlob(fileName, glob) {
    if (glob === "*") {
        return true;
    }
    if (glob.startsWith("*.")) {
        const suffix = glob.slice(1).toLowerCase();
        return fileName.toLowerCase().endsWith(suffix);
    }
    return fileName === glob;
}
async function readDirectory(fs, directoryPath) {
    try {
        if ((await fs.lstat(directoryPath)).isSymbolicLink()) {
            return [];
        }
        return await fs.readdir(directoryPath);
    }
    catch (error) {
        if (isMissingDirectory(error)) {
            return [];
        }
        throw error;
    }
}
async function discoverFromDirectory(options) {
    const entries = await readDirectory(options.fs, options.directoryPath);
    const docs = [];
    for (const entry of entries) {
        if (!matchesGlob(entry, options.glob)) {
            continue;
        }
        const absolutePath = path.join(options.directoryPath, entry);
        if (await isDiscoverableEntry(options.fs, absolutePath)) {
            docs.push({ fileName: entry, absolutePath });
        }
    }
    return docs;
}
async function isDiscoverableEntry(fs, filePath) {
    try {
        return !(await fs.lstat(filePath)).isSymbolicLink();
    }
    catch (error) {
        if (isMissingDirectory(error)) {
            return false;
        }
        throw error;
    }
}
export async function discoverWorkflowDocs(options) {
    const glob = options.glob ?? defaultGlobForSubDirectory(options.subDirectory);
    const projectRoot = path.join(options.cwd, ".poe-code");
    const globalRoot = path.join(options.homeDir, ".poe-code");
    const projectDirectory = path.join(projectRoot, options.subDirectory);
    const globalDirectory = path.join(globalRoot, options.subDirectory);
    assertContainedPath(projectRoot, projectDirectory, "Workflow subdirectory must remain within the state root");
    assertContainedPath(globalRoot, globalDirectory, "Workflow subdirectory must remain within the state root");
    const [projectDocs, globalDocs] = await Promise.all([
        discoverFromDirectory({
            fs: options.fs,
            directoryPath: projectDirectory,
            glob
        }),
        discoverFromDirectory({
            fs: options.fs,
            directoryPath: globalDirectory,
            glob
        })
    ]);
    const docsByFileName = new Map();
    for (const doc of globalDocs) {
        docsByFileName.set(doc.fileName, doc.absolutePath);
    }
    for (const doc of projectDocs) {
        docsByFileName.set(doc.fileName, doc.absolutePath);
    }
    return [...docsByFileName.values()].sort((left, right) => left.localeCompare(right));
}
