import { readdir, readFile } from "node:fs/promises";
import { basename, dirname, join, relative } from "node:path";
import { parse as parseYaml } from "yaml";
import { SUPPORTED_PLUGIN_IDS } from "@superblocksteam/sdk-api";
const SUPPORTED_PLUGIN_ID_SET = new Set(SUPPORTED_PLUGIN_IDS);
const IGNORED_ROOT_DIRS = new Set([".git", "node_modules", "scratch"]);
const IGNORED_DIRS_ANY_DEPTH = new Set([
    ".git",
    "node_modules",
    "scratch",
    "v2-backup",
]);
export async function detectUnsupportedIntegrations(appRootDirPath, options = {}) {
    const yamlPaths = await findApiYamls(appRootDirPath);
    if (yamlPaths.length === 0) {
        return {
            hasUnsupportedIntegrations: false,
            unsupportedIntegrationIds: [],
            unsupportedApiNames: [],
        };
    }
    const unsupportedIntegrationIds = new Set();
    const unsupportedApiNames = new Set();
    const apiRecords = [];
    const integrationIdRefs = new Set();
    for (const yamlPath of yamlPaths) {
        const { apiName, integrationRefs } = await readApiIntegrations(yamlPath);
        apiRecords.push({ apiName, integrationRefs });
        for (const ref of integrationRefs) {
            if (UUID_LIKE_ID_REGEX.test(ref.rawIntegration)) {
                integrationIdRefs.add(ref.rawIntegration);
            }
        }
    }
    const pluginIdByIntegrationId = integrationIdRefs.size > 0 && options.resolvePluginIdsByIntegrationIds
        ? await options.resolvePluginIdsByIntegrationIds([...integrationIdRefs])
        : new Map();
    for (const { apiName, integrationRefs } of apiRecords) {
        const resolvedPluginIds = integrationRefs
            .map((ref) => resolvePluginId(ref, pluginIdByIntegrationId))
            .filter((pluginId) => typeof pluginId === "string");
        const hasUnsupported = resolvedPluginIds.some(isUnsupportedPluginId);
        if (!hasUnsupported) {
            continue;
        }
        unsupportedApiNames.add(apiName);
        for (const pluginId of resolvedPluginIds) {
            if (isUnsupportedPluginId(pluginId)) {
                unsupportedIntegrationIds.add(pluginId);
            }
        }
    }
    const unsupportedList = [...unsupportedIntegrationIds].sort();
    const unsupportedApiList = [...unsupportedApiNames].sort();
    return {
        hasUnsupportedIntegrations: unsupportedList.length > 0,
        unsupportedIntegrationIds: unsupportedList,
        unsupportedApiNames: unsupportedApiList,
    };
}
async function findApiYamls(appRootDirPath) {
    const results = [];
    const stack = [appRootDirPath];
    while (stack.length > 0) {
        const current = stack.pop();
        if (!current) {
            continue;
        }
        let entries;
        try {
            entries = await readdir(current, { withFileTypes: true });
        }
        catch {
            continue;
        }
        for (const entry of entries) {
            const absPath = join(current, entry.name);
            if (entry.isDirectory()) {
                if ((current === appRootDirPath && IGNORED_ROOT_DIRS.has(entry.name)) ||
                    IGNORED_DIRS_ANY_DEPTH.has(entry.name) ||
                    entry.name.startsWith(".")) {
                    continue;
                }
                stack.push(absPath);
                continue;
            }
            if (!entry.isFile()) {
                continue;
            }
            if (entry.name !== "api.yaml" && entry.name !== "api.yml") {
                continue;
            }
            const relParts = splitPathParts(relative(appRootDirPath, absPath));
            if (!isCanonicalV2ApiYamlPath(relParts)) {
                continue;
            }
            results.push(absPath);
        }
    }
    return results.sort();
}
function isCanonicalV2ApiYamlPath(relParts) {
    if (relParts.length < 3) {
        return false;
    }
    const fileName = relParts[relParts.length - 1];
    if (fileName !== "api.yaml" && fileName !== "api.yml") {
        return false;
    }
    // /apis/<ApiName>/api.yaml
    if (relParts[0] === "apis" && relParts[relParts.length - 2] !== "apis") {
        return true;
    }
    // /pages/<Page>/.../apis/<ApiName>/api.yaml
    if (relParts[0] === "pages") {
        const apisIndex = relParts.indexOf("apis");
        return apisIndex > 0 && apisIndex < relParts.length - 2;
    }
    return false;
}
const UUID_LIKE_ID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function isUnsupportedPluginId(pluginId) {
    return !SUPPORTED_PLUGIN_ID_SET.has(pluginId);
}
async function readApiIntegrations(yamlPath) {
    const raw = await readFile(yamlPath, "utf-8");
    const parsed = parseYaml(raw);
    const integrationRefs = collectIntegrationRefs(parsed);
    const fallbackApiName = basename(dirname(yamlPath)) || "UnknownApi";
    if (parsed &&
        typeof parsed === "object" &&
        "metadata" in parsed &&
        typeof parsed.metadata?.name ===
            "string") {
        return {
            apiName: parsed.metadata.name,
            integrationRefs,
        };
    }
    return { apiName: fallbackApiName, integrationRefs };
}
function splitPathParts(pathValue) {
    return pathValue.split(/[\\/]/);
}
function collectIntegrationRefs(root) {
    const refs = [];
    const seen = new Set();
    const walk = (node) => {
        if (!node || typeof node !== "object") {
            return;
        }
        if (Array.isArray(node)) {
            for (const child of node) {
                walk(child);
            }
            return;
        }
        const maybeStep = node.step;
        if (maybeStep && typeof maybeStep === "object") {
            const integration = maybeStep.integration;
            if (typeof integration === "string" && integration.length > 0) {
                const pluginHint = inferPluginHintFromStep(maybeStep, integration);
                const key = `${integration}::${pluginHint ?? ""}`;
                if (!seen.has(key)) {
                    refs.push({ rawIntegration: integration, pluginHint });
                    seen.add(key);
                }
            }
        }
        for (const child of Object.values(node)) {
            walk(child);
        }
    };
    walk(root);
    return refs;
}
function inferPluginHintFromStep(step, integrationValue) {
    if (!UUID_LIKE_ID_REGEX.test(integrationValue)) {
        return undefined;
    }
    const keys = Object.keys(step).filter((k) => k !== "integration");
    if (keys.length === 0) {
        return undefined;
    }
    if (keys.length === 1) {
        return keys[0];
    }
    const integrationLike = keys.find((k) => k.endsWith("integration"));
    return integrationLike ?? keys[0];
}
function resolvePluginId(ref, pluginIdByIntegrationId) {
    if (!UUID_LIKE_ID_REGEX.test(ref.rawIntegration)) {
        return ref.rawIntegration;
    }
    return pluginIdByIntegrationId.get(ref.rawIntegration) ?? ref.pluginHint;
}
//# sourceMappingURL=unsupported-integrations.js.map