import { randomUUID } from 'node:crypto';
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
import { dirname } from 'node:path';
import { CliError } from '../errors/index.js';
import { OAT_VERSION } from '../shared/oat-version.js';
import { ManifestSchema, } from './manifest.types.js';
function formatIssuePath(path) {
    if (path.length === 0) {
        return '(root)';
    }
    return path.map(String).join('.');
}
function nowIso() {
    return new Date().toISOString();
}
export function createEmptyManifest() {
    return {
        version: 1,
        oatVersion: OAT_VERSION,
        entries: [],
        lastUpdated: nowIso(),
    };
}
export async function loadManifest(manifestPath) {
    try {
        const raw = await readFile(manifestPath, 'utf8');
        let parsed;
        try {
            parsed = JSON.parse(raw);
        }
        catch {
            throw new CliError(`Manifest at ${manifestPath} is not valid JSON. Delete or repair the file and re-run oat sync.`);
        }
        const result = ManifestSchema.safeParse(parsed);
        if (!result.success) {
            const issueDetails = result.error.issues
                .map((issue) => `${formatIssuePath(issue.path)}: ${issue.message}`)
                .join('; ');
            throw new CliError(`Manifest at ${manifestPath} failed validation: ${issueDetails}. Delete or repair the file and re-run oat sync.`);
        }
        return result.data;
    }
    catch (error) {
        if (typeof error === 'object' &&
            error !== null &&
            'code' in error &&
            error.code === 'ENOENT') {
            return createEmptyManifest();
        }
        if (error instanceof CliError) {
            throw error;
        }
        throw new CliError(`Failed to read manifest at ${manifestPath}: ${error instanceof Error ? error.message : 'unknown error'}`, 2);
    }
}
export async function saveManifest(manifestPath, manifest) {
    const validated = ManifestSchema.parse({
        ...manifest,
        oatVersion: OAT_VERSION,
    });
    const dir = dirname(manifestPath);
    const tempPath = `${manifestPath}.${randomUUID()}.tmp`;
    await mkdir(dir, { recursive: true });
    await writeFile(tempPath, `${JSON.stringify(validated, null, 2)}\n`, 'utf8');
    await rename(tempPath, manifestPath);
}
export function findEntry(manifest, canonicalPath, provider) {
    return manifest.entries.find((entry) => entry.canonicalPath === canonicalPath && entry.provider === provider);
}
export function addEntry(manifest, entry) {
    const nextEntries = manifest.entries.filter((candidate) => !(candidate.canonicalPath === entry.canonicalPath &&
        candidate.provider === entry.provider));
    return {
        ...manifest,
        entries: [...nextEntries, entry],
        lastUpdated: nowIso(),
    };
}
export function removeEntry(manifest, canonicalPath, provider) {
    const nextEntries = manifest.entries.filter((entry) => !(entry.canonicalPath === canonicalPath && entry.provider === provider));
    if (nextEntries.length === manifest.entries.length) {
        return manifest;
    }
    return {
        ...manifest,
        entries: nextEntries,
        lastUpdated: nowIso(),
    };
}
