import { browser } from '$app/environment';

/**
 * Optional progress callback for clear_idb() and clear_all_storage().
 * level: 'ok' = success, 'warn' = nothing to do / non-fatal, 'error' = failure, 'info' = general step
 */
export type BrowserResetLogFn = (msg: string, level?: 'info' | 'ok' | 'warn' | 'error') => void;

// Fallback list for browsers that don't support indexedDB.databases() (pre-2021 Safari etc.)
const KNOWN_IDB_NAMES = [
    'ae_core_db', 'ae_events_db', 'ae_journals_db',
    'ae_archives_db', 'ae_posts_db', 'ae_idaa_db',
    'ae_sponsorships_db', 'ae_reports_db',
];

function delete_one_idb(name: string, log?: BrowserResetLogFn): Promise<void> {
    return new Promise((resolve) => {
        const req = indexedDB.deleteDatabase(name);
        req.onsuccess = () => { log?.(`Deleted IDB database: ${name}`, 'ok'); resolve(); };
        req.onerror = () => { log?.(`ERROR deleting IDB database: ${name}`, 'error'); resolve(); };
        // blocked = another tab still has the DB open; we resolve anyway and continue
        req.onblocked = () => { log?.(`IDB delete blocked (open connections): ${name} — will proceed`, 'warn'); resolve(); };
    });
}

/**
 * Deletes all IndexedDB databases for this origin.
 *
 * Uses indexedDB.databases() when available; falls back to the known Aether DB
 * name list for older browsers. Does not reload — caller decides navigation.
 */
export async function clear_idb(log?: BrowserResetLogFn): Promise<void> {
    if (!browser) return;

    try {
        if ('databases' in indexedDB) {
            const db_list = await indexedDB.databases();
            if (db_list.length === 0) {
                log?.('No IndexedDB databases found.', 'warn');
            } else {
                for (const db_info of db_list) {
                    if (!db_info.name) continue;
                    await delete_one_idb(db_info.name, log);
                }
            }
        } else {
            log?.('indexedDB.databases() not available — deleting known Aether databases by name.', 'warn');
            for (const name of KNOWN_IDB_NAMES) {
                await delete_one_idb(name, log);
            }
        }
    } catch (err: any) {
        log?.(`ERROR clearing IndexedDB: ${err.message}`, 'error');
    }
}

/**
 * Full browser storage reset for this origin.
 *
 * Clears in mandatory order: SW unregister → Cache Storage → IDB → localStorage → sessionStorage.
 * SW and Cache Storage MUST be cleared before IDB/localStorage — otherwise the SW continues
 * serving stale JS bundles from its own Cache Storage on the next load, keeping users stuck.
 *
 * Does not reload — caller decides whether to navigate after (e.g. window.location.href = '/').
 *
 * @param log      Optional progress callback. Receives each step message and a severity level.
 *                 Use this to surface step-by-step feedback to the user (e.g. the fix-sw page).
 *                 Omit for silent operation (e.g. the sys bar reset buttons).
 * @param options  Optional behavior overrides.
 *   - preserve_ls_keys: localStorage keys to save before clearing and restore after.
 *                       Use ['ae_loc'] to keep the user signed in through the reset.
 *   - skip_ls: if true, skip clearing localStorage and sessionStorage entirely.
 *              Use this for "Clear & Reload" style resets that only need SW+Cache+IDB
 *              cleared (non-destructive to auth state — no ae_loc preservation needed).
 */
export async function clear_all_storage(
    log?: BrowserResetLogFn,
    options?: { preserve_ls_keys?: string[]; skip_ls?: boolean }
): Promise<void> {
    if (!browser) return;

    log?.('Starting full storage reset...', 'info');

    // 1. Unregister all service workers
    if ('serviceWorker' in navigator) {
        try {
            const registrations = await navigator.serviceWorker.getRegistrations();
            if (registrations.length === 0) {
                log?.('No active service worker registrations found.', 'warn');
            } else {
                log?.(`Found ${registrations.length} service worker registration(s).`, 'info');
                for (const reg of registrations) {
                    const ok = await reg.unregister();
                    log?.(`Unregistered SW at scope: ${reg.scope} (success: ${ok})`, 'ok');
                }
            }
        } catch (err: any) {
            log?.(`ERROR unregistering service workers: ${err.message}`, 'error');
        }
    } else {
        log?.('Service Workers not supported in this browser.', 'warn');
    }

    // 2. Clear all Cache Storage caches
    try {
        const cache_keys = await caches.keys();
        if (cache_keys.length === 0) {
            log?.('No Cache Storage entries found.', 'warn');
        } else {
            for (const key of cache_keys) {
                await caches.delete(key);
                log?.(`Cleared cache: ${key}`, 'ok');
            }
        }
    } catch (err: any) {
        log?.(`ERROR clearing Cache Storage: ${err.message}`, 'error');
    }

    // 3. Clear all IndexedDB databases (handles enumeration + fallback internally)
    await clear_idb(log);

    if (options?.skip_ls) {
        log?.('Skipping localStorage and sessionStorage (skip_ls=true).', 'info');
    } else {
        // 4. Clear localStorage (saving and restoring any keys that should survive the reset)
        try {
            const local_count = localStorage.length;
            const preserved: Record<string, string> = {};
            for (const key of options?.preserve_ls_keys ?? []) {
                const val = localStorage.getItem(key);
                if (val !== null) preserved[key] = val;
            }
            localStorage.clear();
            for (const [key, val] of Object.entries(preserved)) {
                localStorage.setItem(key, val);
            }
            const preserved_note = Object.keys(preserved).length
                ? ` (preserved: ${Object.keys(preserved).join(', ')})`
                : '';
            log?.(`Cleared localStorage (${local_count} item(s))${preserved_note}.`, 'ok');
        } catch (err) {
            log?.(`ERROR clearing localStorage: ${(err as Error).message}`, 'error');
        }

        // 5. Clear sessionStorage
        try {
            const session_count = sessionStorage.length;
            sessionStorage.clear();
            log?.(`Cleared sessionStorage (${session_count} item(s)).`, 'ok');
        } catch (err) {
            log?.(`ERROR clearing sessionStorage: ${(err as Error).message}`, 'error');
        }
    }
}
