var db;
openDb();
init();
console.log('satrt');
var SYNC_IN_PROGRESS = false;

self.addEventListener('install', function (event) {
    console.log("=== install === ", event);
    self.skipWaiting();
});

self.addEventListener('sync', function (event) {
    console.log("=== sync === ", event);
});

self.addEventListener('activate', function (event) {
    console.log("== ACTIVATE ==");
    //init();
});

self.addEventListener('message', function (event) {
    console.log("== message ==", event);
});

self.addEventListener('fetch', function (event) {
});


function init() {
    console.log('service worker init');
    setInterval(() => {
        startSyncNetworkCache();
}, 60 * 1000);

    if (!db) {
        openDb()
    }
}


function openDb() {
    return new Promise(resolve => {
        var openRequest = indexedDB.open('_network_cache_storage');

    openRequest.onsuccess = function () {
        db = this.result; // Average 8ms
        resolve(db);
    };

});
}

function createNetworkCacheObjectStore(event) {
    var objectStore = event.target.result.createObjectStore("network_cache", {keyPath: "id"});
    objectStore.createIndex("status_index", "status");
}


function startSyncNetworkCache() {
    console.log("<<<<<<<<<<< startSyncNetworkCache >>>>>>>>>>>>>", SYNC_IN_PROGRESS);
    if (!SYNC_IN_PROGRESS) {
        syncNetworkCache();
    }

}


async function syncNetworkCache() {
    SYNC_IN_PROGRESS = true;

    var failNetworkCalls = await getFailNetworkCalls();

    if (failNetworkCalls && failNetworkCalls.length > 0) {

        var eventList = [];
        var defaultCallList = [];

        for (var i = 0; i < failNetworkCalls.length; i++) {
            var networkCall = failNetworkCalls[i];
            if (networkCall.message_type === 'event') {
                eventList.push(networkCall);
            }
            else {
                defaultCallList.push(networkCall);
            }
        }

        var successCount = 0;
        if(defaultCallList.length>0){
            successCount += await sendFailDefaultCallList(defaultCallList);
        }
        if (eventList.length>0){
            successCount += await sendFailEvents(eventList);
        }


        if (successCount > 0) {
            setTimeout(() => {
                console.log("--- sync... ---");
                syncNetworkCache();
            },5 * 1000);
        } else {
            SYNC_IN_PROGRESS = false;
        }

    } else {
        SYNC_IN_PROGRESS = false;
    }
}


async function getNetworkCacheObjectStore() {
    if (!db) {
        db = await openDb();
    }
    return new Promise(resolve => {

        var transaction = db.transaction("network_cache", "readwrite");
    var objectStore = transaction.objectStore('network_cache');
    resolve(objectStore);
});
}

async function getFailNetworkCalls() {
    var networkCacheObjectStore = await getNetworkCacheObjectStore();
    return new Promise(resolve => {
        var count = 0;
    var resultArray = new Array();
    networkCacheObjectStore.openCursor().onsuccess = function (event) {

        var cursor = event.target.result;
        if (cursor && count < 10) {

            if (cursor.value.status == 'fail' || ((new Date().getTime() - cursor.value.last_update_time ) > (5 * 60 * 1000))) {

                resultArray.push(cursor.value);
                count++;
                var newData = cursor.value;
                newData['status'] = 'in-progress';
                newData['last_update_time'] = new Date().getTime();
                cursor.update(newData);
            }
            cursor.continue();

        } else {
            resolve(resultArray);
        }
    }
});
}

async function sendFailDefaultCallList(defaultCallList) {

    var successCount = 0;

    for (var i = 0; i < defaultCallList.length; i++) {
        var networkCall = defaultCallList[i];

        var result = await sendPOST(networkCall["url"], networkCall["message"], networkCall["message_type"], networkCall["message_header"]);
        if (result) {
            successCount++;
        }
    }
    return new Promise(resolve => {
        resolve(successCount);
});
}


async function sendFailEvents(eventList) {

    var successCount = 0;

    var records = [];
    var url = '';
    var headers = {};
    var sendTime = new Date().getTime();

    for (var i = 0; i < eventList.length; i++) {
        var event = eventList[i];

        var record = {};
        var msg = event.message;
        var w1 = msg.w1;
        var cc = w1.cc;
        cc['o'] = sendTime;

        var random = Math.floor(Math.random() * 4) + 1;
        record['value'] = event.message;
        record['key'] = random.toString();

        records.push(record);
        url = event.url;
        headers = event.message_header;

    }

    var data = {};
    data['records'] = records;
    var result = await sendPOST(url, data, 'event', headers);
    if (result) {
        successCount++;
    }
    return new Promise(resolve => {
        resolve(successCount);
});
}


function sendPOST(url, msg, msgType, headers) {
    return new Promise(resolve => {
        fetch(new Request(url, {
            headers: headers,
            method: "POST",
            body: JSON.stringify(msg)
        }))
.then(function (response) {
        processResponse(response, msg, msgType);
        resolve(response.ok);

    }, function (err) {
        processErrorResponse(err, msg, msgType);
        resolve(false);
    });
});
}


function processResponse(response, msg, msgType) {

    switch (msgType) {
        case 'event':
            if (response.ok) {
                response.json().then(result => {
                    if (result && result.offsets) {
                    let offsets = result.offsets;

                    if (offsets.length > 0) {
                        let offset = offsets.pop();
                        if (offset.error_code == null) {
                            // success
                            updateEventStatus(msg, 'remove');
                            return;
                        }
                    }
                }
                updateEventStatus(msg, 'fail');
            })

            } else {
                updateEventStatus(msg, 'fail');

            }
            break;

        default:
            if (response.ok) {
                response.json().then(result => {
                    if (result && result.msg_id) {
                    if (result.acknowledgement && result.acknowledgement === "success") {
                        this.removeCacheRecord(result.msg_id);
                        return;
                    }
                }
            });
            }

            this.updateCacheRecordStatus(msg.msg_id, "fail");
            return;
            break;
    }
}

function updateEventStatus(eventMsg, status) {
    var records = eventMsg.records;

    for (var i = 0; i < records.length; i++) {
        var record = records[i];
        var msg_id = record.value.w2.mi;

        if (status === 'remove') {
            this.removeCacheRecord(msg_id);
        } else {
            this.updateCacheRecordStatus(msg_id, status);
        }
    }
}

function processErrorResponse(err, msg, msgType) {
    switch (msgType) {
        case 'event':
            updateEventStatus(msg, 'fail');
            break;

        default:
            this.updateCacheRecordStatus(msg.msg_id, "fail");
            break;
    }
}


async function updateCacheRecordStatus(msg_id, status) {
    let networkCacheObjectStore = await getNetworkCacheObjectStore();
    networkCacheObjectStore.openCursor(IDBKeyRange.only(msg_id)).onsuccess = function (event) {
        var cursor = event.target.result;

        if (cursor) {
            let data = cursor.value;
            data["status"] = status;
            data["last_update_time"] = new Date().getTime();
            cursor.update(data);
            cursor.continue();
        }
    }
}


async function removeCacheRecord(msg_id) {
    let networkCacheObjectStore = await getNetworkCacheObjectStore();

    let delReq = networkCacheObjectStore.delete(msg_id);
    delReq.onsuccess = (event) => {
    }
}

