const fiscalPrintingLocalStorageKey = "currentFiscalPrinting";
let fiscalPrintingSupportedMethods;

$(document).ready(function () {
    let localStorageData = localStorage.getItem(fiscalPrintingLocalStorageKey);
    if (!localStorageData) return;

    localStorageData = JSON.parse(localStorageData);

    //TODO: uncomment when re-print implemented
    //printWithFiscalPrinter(localStorageData, localStorageData.localApiUrl, localStorageData.fiscalPrinterResponseiUrl, localStorageData.docType);
});

async function setPrinterMethods(localApi, centralApi, docType) {
    let $button = $("#fiscalPrinterWizardButton");
    $button.prop("disabled", true);
    $button.next("ul").find("a").addClass("hidden");

    let machineName = await getLocalMachineName(localApi);
    $.ajax({
        url: centralApi + "/GetDocTypeSupportedMethods", type: 'GET', cache: false, data: { machineName: machineName, docType: docType }
    }).done(function (data) {
        if (!data || !data.SupportedMethods) return;

        fiscalPrintingSupportedMethods = [];
        data.SupportedMethods.forEach(method => {
            $button.next("ul").find("a.fiscalPrinter" + method).removeClass("hidden");
            fiscalPrintingSupportedMethods.push(method);
        });
        $button.prop("disabled", false);
    });
}

function getLocalMachineName(localApi, ignoreErrorModal) {
    if (!localApi) return "";

    return new Promise(function (resolve, reject) {
        fetch(localApi + '/getMachineName', {
            method: "GET",
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Basic QUQ6YTIxODQwNzZDRTczNzlEOUZERkVEYXRhTGFiMTIz'
            }
        }).then((response) => response.json()
        ).then((data) => {
            if (data && data.machineName)
                resolve(data.machineName);
            else
                resolve(undefined);
        }).catch((error) => {
            if (!ignoreErrorModal && $("#POSFinancialOnly").val() !== "True" && $("#FiscalStatus").val() !== $("#FiscalStatusAvoidFiscalization").val())
                openFiscalServiceRequredModal();

            resolve(undefined);
        });
    });
}

function printWithFiscalPrinter(fiscalData, localApiUrl, centralApiUrl, docType) {
    let debug = false;

    fiscalData.localApiUrl = localApiUrl;
    fiscalData.centralApiUrl = centralApiUrl;
    fiscalData.docType = docType;

    let mediaResponses = objSearch(fiscalData, "MediaResponses");
    mediaResponses.forEach((mediaResponse) => mediaResponse.eraseAtEnd = false);

    let mediaRequests = [];
    objSearch(fiscalData, "MediaRequests").forEach((mediaResponse) => mediaRequests.push({ mediaName: mediaResponse.MediaName }));

    var isCOM = mediaRequests.map(function (e) { return e.mediaName.length > 2 ? e.mediaName.substring(0, 3) : e.mediaName; }).indexOf('COM') !== -1;

    if (!isCOM) localStorage.setItem(fiscalPrintingLocalStorageKey, JSON.stringify(fiscalData));

    return new Promise(async function (resolve, reject) {
        setFiscalPrinterRequests(fiscalData.MediaRequests, fiscalData.Dll, localApiUrl).then(data => {
            if (debug) console.log("after setFiscalPrinterRequests", data);

            if (isCOM) return data;

            return getFiscalPrinterResponse(mediaResponses, fiscalData.Timeout, localApiUrl, debug);
        }).then(data => {
            if (debug) console.log("after getFiscalPrinterResponse", data);

            return setFiscalPrinterResponse(data, fiscalData.AcKey, centralApiUrl + "/FiscalizationResponse", localApiUrl);
        }).then(data => {
            if (debug) console.log("after setFiscalPrinterResponse", data);
            resolve("Success");
        }).catch(error => reject(error)
        ).finally(async () => {
            if (!isCOM) await fiscalPrinterCleanup(mediaResponses.concat(mediaRequests), localApiUrl);
            if (!isCOM) localStorage.removeItem(fiscalPrintingLocalStorageKey);
            fiscalPrinterCutPaper(centralApiUrl + "/FiscalPrinterCut", localApiUrl, docType);
        });
    });
}

function setFiscalPrinterRequests(mediaRequests, dll, apiUrl) {
    return fiscalizationPromise(
        apiUrl + "/fiscPrinterRequest",
        { mediaRequests: mediaRequests, dll: dll },
        true
    );
}

function getFiscalPrinterResponse(mediaResponses, timeout, apiUrl, debug) {
    return new Promise(function (resolve, reject) {
        setTimeout(() => {
            fiscalizationPromise(
                apiUrl + "/fiscPrinterResponse",
                { mediaResponses: mediaResponses }
            ).then(data => {
                if (debug) console.log("getFiscalPrinterResponse then", data, timeout);

                var okResolved = !data.fiscErrors && !data.errorMessage;
                if (!okResolved && timeout > 0) {
                    getFiscalPrinterResponse(mediaResponses, timeout - 1, apiUrl)
                        .then(data => resolve(data))
                        .catch(error => reject(error));
                }
                else {
                    resolve(data);
                }
            }).catch((error) => {
                if (debug) console.log("getFiscalPrinterResponse catch", error, mediaResponses, timeout);
                if (timeout > 0) {
                    getFiscalPrinterResponse(mediaResponses, timeout - 1, apiUrl)
                        .then(data => resolve(data))
                        .catch(error => reject(error));
                }
                else {
                    reject(error);
                }
            });
        }, 1000);
    });
}

async function setFiscalPrinterResponse(response, acKey, apiUrl, localApi) {
    response.acKey = acKey;
    response.machineName = await getLocalMachineName(localApi);

    return fiscalizationPromise(
        apiUrl,
        response,
        true
    );
}

function fiscalPrinterCleanup(mediaResponses, apiUrl) {
    return fiscalizationPromise(
        apiUrl + "/fiscPrinterCleanup",
        { mediaCleanup: mediaResponses }
    );
}

function fiscalPrinterProcess(fiscalData, centralApiUrl, localApiUrl, docType, ignoreCut, delayCleanup) {
    let mediaResponses = objSearch(fiscalData, "MediaResponses");
    mediaResponses.forEach((mediaResponse) => mediaResponse.eraseAtEnd = false);
    let mediaRequests = [];
    objSearch(fiscalData, "MediaRequests").forEach((mediaResponse) => mediaRequests.push({ mediaName: mediaResponse.MediaName }));
    return new Promise(async function (resolve, reject) {
        setFiscalPrinterRequests(fiscalData.MediaRequests, fiscalData.Dll, localApiUrl).then(data => {
            return getFiscalPrinterResponse(mediaResponses, data.Timeout, localApiUrl);
        }).then(data => {
            resolve(data.fiscResponse);
        }).catch(error => reject(error)
        ).finally(async () => {
            if (delayCleanup) {
                setTimeout(function () {
                    fiscalPrinterCleanup(mediaResponses.concat(mediaRequests), localApiUrl);
                }, 5000);
            }
            else {
                await fiscalPrinterCleanup(mediaResponses.concat(mediaRequests), localApiUrl);
            }

            if (!ignoreCut) {
                fiscalPrinterCutPaper(centralApiUrl + "/FiscalPrinterCut", localApiUrl, docType);
            }
        });
    });
}

async function fiscalPrinterCutPaper(apiUrl, localApiUrl, docType) {
    if (fiscalPrintingSupportedMethods.indexOf("PaperCut") < 0) return;

    let machineName = await getLocalMachineName(localApiUrl);

    fiscalizationPromise(apiUrl, { docType: docType, machineName: machineName }).then(data => {
        return fiscalPrinterProcess(data, apiUrl, localApiUrl, docType, true);
    });
}

async function fiscalPrinterXReport(apiUrl, localApiUrl, title, successMessage, element) {
    let machineName = await getLocalMachineName(localApiUrl);
    if (!machineName || machineName.length === 0) return;

    let docType = getDocType();

    let $parentButton = $(element).closest(".dropdown").children("button");
    let initialIcon = $parentButton.find("i").attr("class");
    $parentButton.prop("disabled", true);
    $parentButton.find("i").removeClass(initialIcon);
    $parentButton.find("i").addClass("fa fa-spinner fa-spin");

    fiscalizationPromise(
        apiUrl + "/XReport",
        { docType: docType, machineName: machineName },
        true
    ).then(data => {
        return fiscalPrinterProcess(data, apiUrl, localApiUrl, docType, true, true);
    }).then(data => {
        openFiscalPrinterModal(data ? data : successMessage, title);
    }).catch((error) => {
        openFiscalPrinterModal(error, title);
    }).finally(() => {
        $parentButton.find("i").removeClass("fa fa-spinner fa-spin");
        $parentButton.find("i").addClass(initialIcon);
        $parentButton.prop("disabled", false);
    });
}

async function fiscalPrinterZReport(apiUrl, localApiUrl, title, successMessage, element, onOk) {
    let machineName = await getLocalMachineName(localApiUrl);
    if (!machineName || machineName.length === 0) return;

    let $parentButton = $(element);
    let initialIcon = $parentButton.find("i").attr("class");
    $parentButton.prop("disabled", true);
    $parentButton.find("i").removeClass(initialIcon);
    $parentButton.find("i").addClass("fa fa-spinner fa-spin");

    fiscalizationPromise(
        apiUrl + "/ZReport",
        { machineName: machineName },
        true
    ).then(async data => {
        await setPrinterMethods(localApiUrl, apiUrl, data.DocType);
        return fiscalPrinterProcess(data, apiUrl, localApiUrl, data.DocType, true, true);
    }).then(data => {
        openFiscalPrinterModal(data ? data : successMessage, title, null, onOk);
    }).catch((error) => {
        openFiscalPrinterModal(error, title, null, onOk);
    }).finally(() => {
        $parentButton.find("i").removeClass("fa fa-spinner fa-spin");
        $parentButton.find("i").addClass(initialIcon);
        $parentButton.prop("disabled", false);
    });
}

async function fiscalPrinterPReport(apiUrl, localApiUrl, title, successMessage) {
    let machineName = await getLocalMachineName(localApiUrl);
    if (!machineName || machineName.length === 0) return;

    let docType = getDocType();

    let $modal = $("#fiscalPrinterPReportModal");
    openFiscalPrinterModal(null, title, $modal);
    $modal.find("button").off("click");
    $modal.find("button").on("click", async function (e) {
        e.preventDefault();
        let $button = $(this);
        let initialValue = $button.html();
        $button.prop("disabled", true);
        $button.html("<i class='fa fa-spinner fa-spin'></i>");

        fiscalizationPromise(
            apiUrl + "/PReport",
            {
                docType: docType,
                dateFrom: $("#fiscalPrinterPReportModalDateFrom").val(),
                dateTo: $("#fiscalPrinterPReportModalDateTo").val(),
                machineName: machineName
            },
            true
        ).then(data => {
            return fiscalPrinterProcess(data, apiUrl, localApiUrl, docType);
        }).then(data => {
            openFiscalPrinterModal(data ? data : successMessage, title);
        }).catch((error) => {
            openFiscalPrinterModal(error, title);
        }).finally(() => {
            $modal.modal('hide');
            $button.html(initialValue);
            $button.prop("disabled", false);
        });
    });
}

async function fiscalPrinterDeposit(apiUrl, localApiUrl, title, successMessage) {
    let machineName = await getLocalMachineName(localApiUrl);
    if (!machineName || machineName.length === 0) return;

    let docType = getDocType();
    let $modal = $("#fiscalPrinterDepositModal");
    openFiscalPrinterModal(null, title, $modal);
    $modal.find("button").off("click");
    $modal.find("button").on("click", async function (e) {
        e.preventDefault();
        let $button = $(this);
        let initialValue = $button.html();
        $button.prop("disabled", true);
        $button.html("<i class='fa fa-spinner fa-spin'></i>");

        let value = toNumber($("#fiscalPrinterDepositModalAmount").val());
        let method = value > 0 ? "CashIn" : "CashOut";

        if (fiscalPrintingSupportedMethods.indexOf(method) < 0) {
            $modal.modal('hide');
            $button.html(initialValue);
            $button.prop("disabled", false);

            openFiscalPrinterModal(method + " not supported!", title);
            return;
        }

        fiscalizationPromise(
            apiUrl + "/FiscalPrinterDeposit",
            {
                docType: docType,
                amount: formatDisplay($("#fiscalPrinterDepositModalAmount").attr("step"), $("#fiscalPrinterDepositModalAmount").val(), true),
                machineName: machineName
            },
            true
        ).then(data => {
            return fiscalPrinterProcess(data, apiUrl, localApiUrl, docType);
        }).then(data => {
            openFiscalPrinterModal(data ? data : successMessage, title);
        }).catch((error) => {
            openFiscalPrinterModal(error, title);
        }).finally(() => {
            $modal.modal('hide');
            $button.html(initialValue);
            $button.prop("disabled", false);
        });
    });
}

async function fiscalPrinterOpenDrawer(apiUrl, localApiUrl, title, successMessage, element) {
    let machineName = await getLocalMachineName(localApiUrl);
    if (!machineName || machineName.length === 0) return;

    let $parentButton = $(element).closest(".dropdown").children("button");
    let initialIcon = $parentButton.find("i").attr("class");
    $parentButton.prop("disabled", true);
    $parentButton.find("i").removeClass(initialIcon);
    $parentButton.find("i").addClass("fa fa-spinner fa-spin");

    let docType = getDocType();

    fiscalizationPromise(
        apiUrl + "/FiscalPrinterOpenDrawer",
        { machineName: machineName, docType: docType },
        true
    ).then(data => {
        return fiscalPrinterProcess(data, apiUrl, localApiUrl, docType, true);
    }).then(data => {
        openFiscalPrinterModal(data ? data : successMessage, title);
    }).catch((error) => {
        openFiscalPrinterModal(error, title);
    }).finally(() => {
        $parentButton.find("i").removeClass("fa fa-spinner fa-spin");
        $parentButton.find("i").addClass(initialIcon);
        $parentButton.prop("disabled", false);
    });
}

function openFiscalPrinterModal(text, title, $modal, onOk) {
    $modal = $modal ? $modal : $("#fiscalPrinterMessageModal");

    $modal.find(".modal-footer button").off("click");
    if (onOk) $modal.find(".modal-footer button").on("click", onOk);
    $modal.find(".modal-title").text(title);
    $modal.find(".modal-text").text(text);
    $modal.modal({
        show: true,
        backdrop: 'static'
    });
}

function openFiscalServiceRequredModal($modal) {
    $modal = $modal ? $modal : $("#fiscalPrinterServiceRequiredModal");
    $modal.modal({
        show: true,
        backdrop: 'static'
    });
}

function fiscalizationPromise(url, body, rejectError) {
    return new Promise(function (resolve, reject) {
        fetch(url, {
            method: "POST",
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Basic QUQ6YTIxODQwNzZDRTczNzlEOUZERkVEYXRhTGFiMTIz'
            },
            body: JSON.stringify(toCamelCase(body))
        }).then((response) => response.json()
        ).then((data) => {
            if (data.ErrorMessage)
                data.errorMessage = data.ErrorMessage;
            if (data.FiscErrors)
                data.fiscErrors = data.FiscErrors;
            if (data.Errors)
                data.errors = data.Errors;

            if (rejectError) {
                if (data.fiscErrors || data.errorMessage || (data.errors && data.errors.length > 0)) {
                    reject(formatNewLine(data.fiscErrors) + " " + formatNewLine(data.errorMessage) + " " + (data.errors ? data.errors.join("<br/>") : ""));
                }
            }

            resolve(data);
        }).catch((error) => {
            reject(error);
        });
    });
}

function getDocType() {
    let docType = $("#DocType").val();
    return docType ? docType : $("#loginDevice").val();
}

function formatNewLine(text) {
    if (!text) return "";
    return text.replace("\r\n", '<br/>').replace("\n", '<br/>');
}

function toCamelCase(o) {
    if (!o) return "";

    var newO, origKey, newKey, value;
    if (o instanceof Array) {
        return o.map(function (value) {
            if (typeof value === "object") {
                value = toCamelCase(value);
            }
            return value;
        });
    } else {
        newO = {};
        for (origKey in o) {
            if (o.hasOwnProperty(origKey)) {
                newKey = (origKey.charAt(0).toLowerCase() + origKey.slice(1) || origKey).toString();
                value = o[origKey];
                if (value instanceof Array || (value !== null && value !== undefined && value.constructor === Object)) {
                    value = toCamelCase(value);
                }
                newO[newKey] = value;
            }
        }
    }
    return newO;
}

function objSearch(serObj, propName) {
    if (!serObj) return null;

    let results = [];
    for (prop in serObj) {
        if (prop === propName) {
            results = results.concat(serObj[prop]);
        }

        if (typeof serObj[prop] === 'object') {
            let innerResult = objSearch(serObj[prop], propName);
            if (innerResult && innerResult.length > 0)
                results = results.concat(innerResult);
        }
    }

    return results;
}