(function (global) {
    'use strict';

    const DB_NAME = 'karvi_partner_contracts';
    const DB_VERSION = 2;
    const STORE = 'contracts';

    function contractStoreId(contract) {
        const id = Number(contract && contract.id);
        if (Number.isFinite(id) && id > 0) {
            return id;
        }

        const token = String((contract && contract.token) || '');
        if (token === '') {
            return Date.now();
        }

        let hash = 0;
        for (let i = 0; i < token.length; i += 1) {
            hash = ((hash << 5) - hash) + token.charCodeAt(i);
            hash |= 0;
        }

        return Math.abs(hash) || Date.now();
    }

    function ensureObjectStore(db) {
        if (!db.objectStoreNames.contains(STORE)) {
            db.createObjectStore(STORE, { keyPath: 'id' });
        }
    }

    function deleteDatabase() {
        return new Promise(function (resolve, reject) {
            const request = indexedDB.deleteDatabase(DB_NAME);

            request.onsuccess = function () {
                resolve();
            };

            request.onerror = function () {
                reject(request.error || new Error('Could not reset IndexedDB.'));
            };

            request.onblocked = function () {
                reject(new Error('IndexedDB reset blocked — close other tabs and try again.'));
            };
        });
    }

    function openDb(allowReset) {
        return new Promise(function (resolve, reject) {
            if (!global.indexedDB) {
                reject(new Error('IndexedDB is not available.'));
                return;
            }

            const request = indexedDB.open(DB_NAME, DB_VERSION);

            request.onerror = function () {
                reject(request.error || new Error('Could not open IndexedDB.'));
            };

            request.onupgradeneeded = function (event) {
                ensureObjectStore(event.target.result);
            };

            request.onsuccess = function () {
                const db = request.result;

                if (!db.objectStoreNames.contains(STORE)) {
                    db.close();

                    if (allowReset === false) {
                        reject(new Error('IndexedDB object store is missing.'));
                        return;
                    }

                    deleteDatabase()
                        .then(function () {
                            return openDb(false);
                        })
                        .then(resolve)
                        .catch(reject);
                    return;
                }

                resolve(db);
            };
        });
    }

    function saveContracts(contracts) {
        return openDb(true).then(function (db) {
            return new Promise(function (resolve, reject) {
                const syncedAt = new Date().toISOString();
                let tx;

                try {
                    tx = db.transaction(STORE, 'readwrite');
                } catch (error) {
                    db.close();
                    reject(error);
                    return;
                }

                const store = tx.objectStore(STORE);

                store.clear();

                (contracts || []).forEach(function (contract) {
                    const record = Object.assign({}, contract, {
                        id: contractStoreId(contract),
                        syncedAt: syncedAt,
                    });
                    store.put(record);
                });

                tx.oncomplete = function () {
                    db.close();
                    resolve(syncedAt);
                };

                tx.onerror = function () {
                    db.close();
                    reject(tx.error || new Error('Could not save contracts.'));
                };
            });
        });
    }

    function getAllContracts() {
        return openDb(true).then(function (db) {
            return new Promise(function (resolve, reject) {
                let tx;

                try {
                    tx = db.transaction(STORE, 'readonly');
                } catch (error) {
                    db.close();
                    reject(error);
                    return;
                }

                const request = tx.objectStore(STORE).getAll();

                request.onsuccess = function () {
                    db.close();
                    const list = request.result || [];
                    list.sort(function (a, b) {
                        return String(b.created_at || '').localeCompare(String(a.created_at || ''));
                    });
                    resolve(list);
                };

                request.onerror = function () {
                    db.close();
                    reject(request.error || new Error('Could not read contracts.'));
                };
            });
        });
    }

    function clearAll() {
        return deleteDatabase();
    }

    global.KarviContractsDB = {
        saveContracts: saveContracts,
        getAllContracts: getAllContracts,
        clearAll: clearAll,
    };
})(window);
