// engine/spec-versioning/version-store.ts — Store and retrieve spec version snapshots
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { existsSync } from 'node:fs';
const VERSIONS_FILENAME = 'versions.json';
/** Build the storage directory for a spec's versions. */
export function getVersionsDir(dataDir, specId) {
    return join(dataDir, 'spec-versions', specId);
}
/** Build the path to the versions.json file for a spec. */
function getVersionsFilePath(dataDir, specId) {
    return join(getVersionsDir(dataDir, specId), VERSIONS_FILENAME);
}
/** Load the version store for a spec. Returns empty store if not found. */
export async function loadVersionStore(dataDir, specId) {
    const filePath = getVersionsFilePath(dataDir, specId);
    try {
        const raw = await readFile(filePath, 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return { specId, versions: [] };
    }
}
/** Persist the version store to disk. Creates directories as needed. */
async function saveVersionStore(dataDir, specId, store) {
    const dir = getVersionsDir(dataDir, specId);
    if (!existsSync(dir)) {
        await mkdir(dir, { recursive: true });
    }
    const filePath = join(dir, VERSIONS_FILENAME);
    await writeFile(filePath, JSON.stringify(store, null, 2), 'utf-8');
}
/** Validate that a tag matches semantic versioning (vMAJOR.MINOR.PATCH). */
export function isValidVersionTag(tag) {
    return /^v\d+\.\d+\.\d+$/.test(tag);
}
/** Check if a tag already exists in the store. */
function tagExists(store, tag) {
    return store.versions.some((v) => v.tag === tag);
}
/** Create and persist a new version snapshot. Returns the created snapshot. */
export async function createVersion(dataDir, input) {
    const { specId, tag, content, message, author } = input;
    if (!isValidVersionTag(tag)) {
        throw new Error(`Invalid version tag "${tag}". Use semantic versioning format: v1.0.0, v1.1.0, v2.0.0`);
    }
    const store = await loadVersionStore(dataDir, specId);
    if (tagExists(store, tag)) {
        throw new Error(`Version "${tag}" already exists for spec ${specId}. Use a different tag.`);
    }
    const version = {
        tag,
        timestamp: new Date().toISOString(),
        content,
        ...(message !== undefined ? { message } : {}),
        ...(author !== undefined ? { author } : {}),
    };
    store.versions.push(version);
    await saveVersionStore(dataDir, specId, store);
    return version;
}
/** Retrieve a specific snapshot by tag. Returns null if not found. */
export async function getVersion(dataDir, specId, tag) {
    const store = await loadVersionStore(dataDir, specId);
    return store.versions.find((v) => v.tag === tag) ?? null;
}
/** List all snapshots for a spec, ordered from oldest to newest. */
export async function listVersions(dataDir, specId) {
    const store = await loadVersionStore(dataDir, specId);
    return [...store.versions].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
}
/** Get the latest version tag for a spec. Returns null if no versions exist. */
export async function getLatestVersionTag(dataDir, specId) {
    const versions = await listVersions(dataDir, specId);
    if (versions.length === 0) {
        return null;
    }
    const latest = versions[versions.length - 1];
    return latest?.tag ?? null;
}
/** Suggest the next patch version given the current latest tag. */
export function suggestNextTag(latestTag) {
    if (latestTag === null) {
        return 'v1.0.0';
    }
    const match = /^v(\d+)\.(\d+)\.(\d+)$/.exec(latestTag);
    if (!match) {
        return 'v1.0.0';
    }
    const major = Number(match[1]);
    const minor = Number(match[2]);
    const patch = Number(match[3]);
    return `v${major}.${minor}.${patch + 1}`;
}
//# sourceMappingURL=version-store.js.map