import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from 'node:fs';
import { dirname } from 'node:path';
import type { Manifest, ManifestEntry } from './types';

export function createEmpty(): Manifest {
    return {
        $schema: './dev-indices.schema.json',
        version: 1,
        lastId: 0,
        entries: {},
        deleted: {},
    };
}

export function loadManifest(path: string): Manifest {
    if (!existsSync(path)) return createEmpty();
    try {
        const raw = readFileSync(path, 'utf8');
        const parsed = JSON.parse(raw) as Manifest;
        if (parsed.version !== 1) {
            throw new Error(`dev-indices manifest version ${parsed.version} unsupported (expected 1)`);
        }
        parsed.entries ??= {};
        parsed.deleted ??= {};
        return parsed;
    } catch (err) {
        if (err instanceof SyntaxError) {
            throw new Error(`dev-indices manifest at ${path} is corrupt JSON: ${err.message}`);
        }
        throw err;
    }
}

export function saveManifest(path: string, manifest: Manifest): void {
    mkdirSync(dirname(path), { recursive: true });
    const tmp = path + '.tmp';
    const json = JSON.stringify(manifest, null, 2);
    writeFileSync(tmp, json, 'utf8');
    renameSync(tmp, path);
}

export function findBySignature(manifest: Manifest, signature: string): number | null {
    for (const [id, entry] of Object.entries(manifest.entries)) {
        if (entry.signature === signature) return Number(id);
    }
    return null;
}

export function addEntry(manifest: Manifest, entry: ManifestEntry): number {
    const id = manifest.lastId + 1;
    manifest.lastId = id;
    manifest.entries[String(id)] = entry;
    return id;
}

export function markDeleted(manifest: Manifest, id: number): void {
    const key = String(id);
    const entry = manifest.entries[key];
    // no-op if id was never added or already deleted — defensive, callers should not rely on this
    if (!entry) return;
    manifest.deleted[key] = {
        lastSignature: entry.signature,
        lastFile: entry.file,
        deletedAt: new Date().toISOString(),
    };
    delete manifest.entries[key];
}
