import { readFileSync, readdirSync, writeFileSync } from "node:fs";
import path from "node:path";
import { API_FOLDER } from "./file-system-helpers.js";
export const YAML_TYPES_PLUGIN_NAME = "sb-yaml-api-types";
const SUPERBLOCKS_TYPE_FILE_NAME = "superblocks.d.ts";
const SUPERBLOCKS_REGISTRY_ENTRY_REGEX = /^\s*"([^"]+)":\s*\{\s*input:\s*([^;]+);\s*output:\s*(.+?)\s*\};\s*$/gm;
function loadApiTypes(rootDir, apiName) {
    const typesPath = path.join(rootDir, API_FOLDER, apiName, "types.d.ts");
    try {
        const content = readFileSync(typesPath, "utf-8");
        const inputMatch = content.match(/(?:interface|type)\s+(\w+Input)/);
        const outputMatch = content.match(/(?:interface|type)\s+(\w+(?:Response|Output))/);
        const emptyInputMatch = !inputMatch && content.match(/^\{\}\s*$/m);
        return {
            input: emptyInputMatch
                ? "any | undefined"
                : inputMatch
                    ? (inputMatch[1] ?? "any")
                    : "any",
            output: outputMatch ? (outputMatch[1] ?? "any") : "any",
        };
    }
    catch {
        return { input: "any", output: "any" };
    }
}
function isFallbackAnyMetadata(metadata) {
    return metadata.input === "any" && metadata.output === "any";
}
export function parseExistingTypeDefinitions(existingTypeDefinitions) {
    const interfaceMetadata = {};
    for (const match of existingTypeDefinitions.matchAll(SUPERBLOCKS_REGISTRY_ENTRY_REGEX)) {
        const apiName = match[1];
        const input = match[2];
        const output = match[3];
        if (apiName && input && output) {
            interfaceMetadata[apiName] = {
                input: input.trim(),
                output: output.trim(),
            };
        }
    }
    return interfaceMetadata;
}
export function mergeInterfaceMetadata(discoveredApiFolders, generatedMetadata, existingMetadata) {
    const mergedMetadata = {
        ...existingMetadata,
    };
    for (const apiName of discoveredApiFolders) {
        const generated = generatedMetadata[apiName];
        if (!generated) {
            continue;
        }
        const existing = existingMetadata[apiName];
        if (existing &&
            isFallbackAnyMetadata(generated) &&
            !isFallbackAnyMetadata(existing)) {
            // Keep the previous typed entry if this regeneration only found fallback
            // any/any types for that API.
            mergedMetadata[apiName] = existing;
            continue;
        }
        mergedMetadata[apiName] = generated;
    }
    return mergedMetadata;
}
export function shouldRegenerateTypeDefinitions(event, filePath) {
    if (event !== "add" && event !== "unlink" && event !== "change") {
        return false;
    }
    const normalizedFilePath = filePath.replaceAll("\\", "/");
    if (!normalizedFilePath.includes("/apis/")) {
        return false;
    }
    return (normalizedFilePath.endsWith("/api.yaml") ||
        normalizedFilePath.endsWith("/api.yml") ||
        normalizedFilePath.endsWith("/types.d.ts"));
}
export function buildTypeDefinitions(apiFolders, interfaceMetadata) {
    const registryEntries = [];
    for (const apiName of apiFolders) {
        const interfaces = interfaceMetadata[apiName];
        if (interfaces) {
            const { input, output } = interfaces;
            registryEntries.push(`    "${apiName}": { input: ${input}; output: ${output} };`);
        }
    }
    const registryContent = registryEntries.length > 0 ? registryEntries.join("\n") : "";
    return `// Auto-generated by Clark
// Do not edit this file manually

import "@superblocksteam/library";

declare global {
  interface SuperblocksAPIRegistry {
${registryContent}
  }
}
`;
}
export const yamlApiTypesPlugin = (syncService, aiService) => {
    let config;
    let pendingGenerationTimeout;
    const generateTypeDefinitions = async (rootDir) => {
        const apiFolders = [];
        const findApiFolders = (dir) => {
            try {
                const entries = readdirSync(path.join(dir, API_FOLDER));
                for (const entry of entries) {
                    const fullPath = path.join(dir, API_FOLDER, entry);
                    // so we skip over invalid folders, lets see if there is a yaml def
                    const file = readdirSync(fullPath).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
                    if (file) {
                        apiFolders.push(entry);
                    }
                }
            }
            catch {
                // ignore
            }
        };
        findApiFolders(rootDir);
        // Load interface metadata from individual types.d.ts files
        const generatedMetadata = {};
        for (const apiName of apiFolders) {
            const types = loadApiTypes(rootDir, apiName);
            if (types) {
                generatedMetadata[apiName] = types;
            }
        }
        const typeFilePath = path.join(rootDir, SUPERBLOCKS_TYPE_FILE_NAME);
        let existingTypeDefinitions = "";
        try {
            existingTypeDefinitions = readFileSync(typeFilePath, "utf-8");
        }
        catch {
            // ignore when file doesn't exist yet
        }
        const existingMetadata = parseExistingTypeDefinitions(existingTypeDefinitions);
        const mergedMetadata = mergeInterfaceMetadata(apiFolders, generatedMetadata, existingMetadata);
        const apiNames = Object.keys(mergedMetadata).sort((a, b) => a.localeCompare(b));
        const typeDefinitions = buildTypeDefinitions(apiNames, mergedMetadata);
        if (existingTypeDefinitions === typeDefinitions) {
            return;
        }
        try {
            // If AI is generating or there are existing drafts, write through draft system
            // so checkpointing still works
            const isAiGenerating = aiService?.isBusy() ?? false;
            const hasExistingDrafts = (await syncService?.hasLocalDraftChanges()) ?? false;
            if (syncService && (isAiGenerating || hasExistingDrafts)) {
                // Write as draft relative to app root
                const relativeFilePath = path.relative(rootDir, typeFilePath);
                await syncService.createDraftFile(relativeFilePath, typeDefinitions);
            }
            else {
                writeFileSync(typeFilePath, typeDefinitions, "utf-8");
            }
        }
        catch (error) {
            console.error("Failed to write type definitions:", error);
        }
    };
    return {
        name: YAML_TYPES_PLUGIN_NAME,
        enforce: "pre",
        configResolved(resolvedConfig) {
            config = resolvedConfig;
        },
        configureServer(server) {
            server.watcher.on("all", (event, filePath) => {
                if (config && shouldRegenerateTypeDefinitions(event, filePath)) {
                    if (pendingGenerationTimeout) {
                        clearTimeout(pendingGenerationTimeout);
                    }
                    pendingGenerationTimeout = setTimeout(() => {
                        pendingGenerationTimeout = undefined;
                        if (config) {
                            void generateTypeDefinitions(config.root);
                        }
                    }, 50);
                }
            });
        },
    };
};
//# sourceMappingURL=vite-plugin-yaml-types.js.map