import * as fs from 'node:fs';
import { parse as parseYaml } from 'yaml';
import { SchemaYamlSchema } from './types.js';
export class SchemaValidationError extends Error {
    constructor(message) {
        super(message);
        this.name = 'SchemaValidationError';
    }
}
/**
 * Loads and validates an artifact schema from a YAML file.
 */
export function loadSchema(filePath) {
    const content = fs.readFileSync(filePath, 'utf-8');
    return parseSchema(content);
}
/**
 * Parses and validates an artifact schema from YAML content.
 */
export function parseSchema(yamlContent) {
    const parsed = parseYaml(yamlContent);
    // Validate with Zod
    const result = SchemaYamlSchema.safeParse(parsed);
    if (!result.success) {
        const errors = result.error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join(', ');
        throw new SchemaValidationError(`Invalid schema: ${errors}`);
    }
    const schema = result.data;
    // Check for duplicate artifact IDs
    validateNoDuplicateIds(schema.artifacts);
    // Check that all requires references are valid
    validateRequiresReferences(schema.artifacts);
    // Check for cycles
    validateNoCycles(schema.artifacts);
    return schema;
}
/**
 * Validates that there are no duplicate artifact IDs.
 */
function validateNoDuplicateIds(artifacts) {
    const seen = new Set();
    for (const artifact of artifacts) {
        if (seen.has(artifact.id)) {
            throw new SchemaValidationError(`Duplicate artifact ID: ${artifact.id}`);
        }
        seen.add(artifact.id);
    }
}
/**
 * Validates that all `requires` references point to valid artifact IDs.
 */
function validateRequiresReferences(artifacts) {
    const validIds = new Set(artifacts.map(a => a.id));
    for (const artifact of artifacts) {
        for (const req of artifact.requires) {
            if (!validIds.has(req)) {
                throw new SchemaValidationError(`Invalid dependency reference in artifact '${artifact.id}': '${req}' does not exist`);
            }
        }
    }
}
/**
 * Validates that there are no cyclic dependencies.
 * Uses DFS to detect cycles and reports the full cycle path.
 */
function validateNoCycles(artifacts) {
    const artifactMap = new Map(artifacts.map(a => [a.id, a]));
    const visited = new Set();
    const inStack = new Set();
    const parent = new Map();
    function dfs(id) {
        visited.add(id);
        inStack.add(id);
        const artifact = artifactMap.get(id);
        if (!artifact)
            return null;
        for (const dep of artifact.requires) {
            if (!visited.has(dep)) {
                parent.set(dep, id);
                const cycle = dfs(dep);
                if (cycle)
                    return cycle;
            }
            else if (inStack.has(dep)) {
                // Found a cycle - reconstruct the path
                const cyclePath = [dep];
                let current = id;
                while (current !== dep) {
                    cyclePath.unshift(current);
                    current = parent.get(current);
                }
                cyclePath.unshift(dep);
                return cyclePath.join(' → ');
            }
        }
        inStack.delete(id);
        return null;
    }
    for (const artifact of artifacts) {
        if (!visited.has(artifact.id)) {
            const cycle = dfs(artifact.id);
            if (cycle) {
                throw new SchemaValidationError(`Cyclic dependency detected: ${cycle}`);
            }
        }
    }
}
//# sourceMappingURL=schema.js.map