import { makeDbAdapter } from './db.js';
/**
 * Feature check for OPFS + cross-origin isolation.
 * We require getDirectory + crossOriginIsolated. createSyncAccessHandle is only
 * available in Web Workers, so we cannot reliably detect it from the main thread;
 * the OPFS VFS runs in a worker and will fail at init if unsupported (we fall
 * back to memory).
 */
export function detectOpfsSupport() {
    const hasWindow = typeof window !== 'undefined';
    if (!hasWindow)
        return { available: false, reason: 'No window' };
    const hasOpfs = typeof navigator?.storage?.getDirectory === 'function';
    const isolated = window.crossOriginIsolated === true;
    const ok = hasOpfs && isolated;
    return ok
        ? { available: true }
        : {
            available: false,
            reason: !hasOpfs
                ? 'navigator.storage.getDirectory unavailable'
                : 'cross-origin isolation required',
        };
}
const DB_RELATED_FILE_SUFFIXES = ['', '-journal', '-wal'];
/**
 * Remove database and related files (journal, wal) from OPFS storage.
 * Call only after the database handle is closed.
 *
 * @param filename - Path used when opening (e.g. /treecrdt.db or /treecrdt-playground.db)
 */
export async function clearOpfsStorage(filename) {
    if (typeof navigator?.storage?.getDirectory !== 'function')
        return;
    const path = filename.startsWith('/') ? filename.slice(1) : filename;
    const parts = path.split('/').filter(Boolean);
    const base = parts.pop() ?? 'treecrdt.db';
    const dirPath = parts;
    let dirHandle;
    try {
        const root = await navigator.storage.getDirectory();
        if (dirPath.length === 0) {
            dirHandle = root;
        }
        else {
            let current = root;
            for (const segment of dirPath) {
                current = await current.getDirectoryHandle(segment);
            }
            dirHandle = current;
        }
    }
    catch {
        return;
    }
    for (const suffix of DB_RELATED_FILE_SUFFIXES) {
        const name = base + suffix;
        try {
            await dirHandle.removeEntry(name);
        }
        catch (e) {
            if (e?.name !== 'NotFoundError')
                throw e;
        }
    }
}
/**
 * Check whether any DB-related files exist in OPFS for the given filename.
 *
 * @param filename - Path used when opening (e.g. /treecrdt.db or /drop-test-xxx.db)
 */
export async function opfsStorageExists(filename) {
    if (typeof navigator?.storage?.getDirectory !== 'function')
        return false;
    const path = filename.startsWith('/') ? filename.slice(1) : filename;
    const parts = path.split('/').filter(Boolean);
    const base = parts.pop() ?? 'treecrdt.db';
    const dirPath = parts;
    let dirHandle;
    try {
        const root = await navigator.storage.getDirectory();
        if (dirPath.length === 0) {
            dirHandle = root;
        }
        else {
            let current = root;
            for (const segment of dirPath) {
                current = await current.getDirectoryHandle(segment);
            }
            dirHandle = current;
        }
    }
    catch {
        return false;
    }
    for (const suffix of DB_RELATED_FILE_SUFFIXES) {
        const name = base + suffix;
        try {
            await dirHandle.getFileHandle(name);
            return true;
        }
        catch (e) {
            if (e?.name !== 'NotFoundError')
                throw e;
        }
    }
    return false;
}
/**
 * Create an OPFS VFS bound to the provided wa-sqlite Module.
 * Uses local copies of wa-sqlite's example VFS implementations to avoid reaching into vendor paths.
 */
export async function createOpfsVfs(module, opts = {}) {
    const name = opts.name ?? 'opfs';
    if (opts.kind === 'any-context') {
        // @ts-ignore vendored module lacks type declarations
        const { OPFSAnyContextVFS } = await import('./vendor/OPFSAnyContextVFS.js');
        return OPFSAnyContextVFS.create(name, module, { lockPolicy: 'exclusive' });
    }
    // @ts-ignore vendored module lacks type declarations
    const { OPFSCoopSyncVFS } = await import('./vendor/OPFSCoopSyncVFS.js');
    return OPFSCoopSyncVFS.create(name, module);
}
/**
 * Convenience: open a wa-sqlite handle with CRDT extension ready, using OPFS when requested.
 */
export async function openWithStorage(opts) {
    const { moduleFactory, filename = ':memory:', sqliteApi, storage } = opts;
    let module = await moduleFactory();
    const sqlite3 = sqliteApi.Factory(module);
    let file = filename;
    if (storage === 'opfs') {
        const support = detectOpfsSupport();
        if (!support.available) {
            throw new Error(`OPFS unsupported: ${support.reason ?? 'unknown reason'}`);
        }
        const vfs = await createOpfsVfs(module, { name: 'opfs', kind: opts.opfsVfs });
        sqlite3.vfs_register(vfs, true);
        file = filename === ':memory:' ? '/treecrdt.db' : filename;
    }
    const handle = await sqlite3.open_v2(file);
    const db = makeDbAdapter(sqlite3, handle);
    return {
        db,
        close: async () => {
            try {
                await sqlite3.close(handle);
            }
            catch {
                /* ignore */
            }
        },
    };
}
