function defineStructure() {
    addColumn("success");
    addColumn("message");
    addColumn("key");
    addColumn("invoiceNumber");
    addColumn("serie");
    addColumn("emissionDate");
    addColumn("operationDate");
    addColumn("supplierName");
    addColumn("documentValue");
    addColumn("totalItemsValue");
    addColumn("situation");
    addColumn("fiscalOperationDescription");
    addColumn("itemCount");
    addColumn("itensJson");
    addColumn("storeId");
    addColumn("invoiceId");
    addColumn("emitterEmployeeId");
    addColumn("updatedAt");
}

function onSync(lastSyncDate) {}

function createDataset(fields, constraints, sortFields) {
    var dataset = DatasetBuilder.newDataset();
    addDefaultColumns(dataset);

    try {
        var key = normalizeDigits(getConstraintValue(constraints, "key"));
        if (!key) {
            addErrorRow(dataset, "Informe a chave da NFe.");
            return dataset;
        }

        if (!/^\d{44}$/.test(key)) {
            addErrorRow(dataset, "A chave da NFe deve conter 44 digitos.");
            return dataset;
        }

        var auth = resolveAuth(constraints);
        var authVariants = buildAuthVariants(auth);
        var diagnostics = [];

        var query = "page=1&page_size=50&key=" + key + "&sort_by=updatedAt&sort_order=desc";
        var endpoints = [
            "/v2/vendas/rgb-transferencias?" + query,
            "v2/vendas/rgb-transferencias?" + query,
            "/rgb-transferencias?" + query
        ];

        var apiObj = null;
        var hitInfo = "";
        var lastApiMessage = "";

        try {
            var clientService = fluigAPI.getAuthorizeClientService();
            for (var i = 0; i < endpoints.length; i++) {
                var endpoint = endpoints[i];
                for (var a = 0; a < authVariants.length; a++) {
                    var authVariant = authVariants[a];
                    var resp = invokeAuthorizedGet(clientService, endpoint, authVariant);
                    diagnostics.push(endpoint + " [" + authVariant.name + "] => HTTP " + trim(resp.status));
                    var parsed = parseApiPayload(resp.body);
                    if (parsed && trim(parsed.message)) {
                        lastApiMessage = trim(parsed.message);
                    }
                    if (String(resp.status) === "200" && isApiSuccess(parsed)) {
                        apiObj = parsed;
                        hitInfo = endpoint + " [" + authVariant.name + "]";
                        break;
                    }
                }
                if (apiObj) {
                    break;
                }
            }
        } catch (eService) {
            diagnostics.push("authorizeClientService exception: " + eService);
        }

        if (!apiObj) {
            var directUrls = [
                "https://api.grupoginseng.com.br/v2/vendas/rgb-transferencias?" + query
            ];

            for (var d = 0; d < directUrls.length; d++) {
                var url = directUrls[d];
                for (var da = 0; da < authVariants.length; da++) {
                    var directAuthVariant = authVariants[da];
                    var directResp = fetchDirect(url, 30000, directAuthVariant);
                    diagnostics.push(url + " [" + directAuthVariant.name + "] => HTTP " + trim(directResp.status));
                    var parsedDirect = parseApiPayload(directResp.body);
                    if (parsedDirect && trim(parsedDirect.message)) {
                        lastApiMessage = trim(parsedDirect.message);
                    }
                    if (String(directResp.status) === "200" && isApiSuccess(parsedDirect)) {
                        apiObj = parsedDirect;
                        hitInfo = url + " [" + directAuthVariant.name + "]";
                        break;
                    }
                }
                if (apiObj) {
                    break;
                }
            }
        }

        if (!apiObj) {
            var msg = "Falha ao consultar API da NFe. Tentativas: " + diagnostics.join(" | ");
            if (lastApiMessage) {
                msg += " | Ultima mensagem da API: " + lastApiMessage;
            }
            addErrorRow(dataset, msg);
            return dataset;
        }

        var dataNfe = extractInvoiceData(apiObj, key);
        if (!dataNfe) {
            addErrorRow(dataset, trim(apiObj.message) || "NFe nao encontrada. Fonte: " + hitInfo);
            return dataset;
        }
        var itens = resolveItems(dataNfe);
        var itensJson = JSON.stringify(buildNfeItems(itens));

        dataset.addRow([
            "true",
            "OK (" + hitInfo + ")",
            trim(dataNfe.key || dataNfe.nfeKey || dataNfe.chave || key),
            trim(dataNfe.invoiceNumber || dataNfe.numeroNota || dataNfe.numero || dataNfe.number),
            trim(dataNfe.serie || dataNfe.series),
            formatIsoDate(dataNfe.emissionDate || dataNfe.issueDate || dataNfe.invoiceDate),
            formatIsoDate(dataNfe.operationDate || dataNfe.transactionDate || dataNfe.operation_at),
            trim(dataNfe.supplierName || dataNfe.supplier || dataNfe.fornecedor || dataNfe.emitente),
            trim(dataNfe.documentValue || dataNfe.totalValue || dataNfe.valorDocumento || dataNfe.amount),
            trim(dataNfe.totalItemsValue || dataNfe.itemsValue || dataNfe.valorItens || dataNfe.totalValue),
            trim(dataNfe.situation || dataNfe.status),
            trim(dataNfe.fiscalOperationDescription || dataNfe.operationDescription || dataNfe.naturezaOperacao),
            String(itens.length),
            itensJson,
            trim(dataNfe.storeId || dataNfe.pdv || dataNfe.storeCode || dataNfe.lojaId),
            trim(dataNfe.invoiceId || dataNfe.id_venda || dataNfe.id || dataNfe.transferId),
            trim(dataNfe.emitterEmployeeId || dataNfe.emitterId || dataNfe.usuarioEmissor),
            formatIsoDateTime(dataNfe.updatedAt || dataNfe.modifiedAt || dataNfe.lastUpdate)
        ]);
    } catch (e) {
        addErrorRow(dataset, "Erro ao consultar a NFe: " + e);
    }

    return dataset;
}

function onMobileSync(user) {}

function addDefaultColumns(dataset) {
    dataset.addColumn("success");
    dataset.addColumn("message");
    dataset.addColumn("key");
    dataset.addColumn("invoiceNumber");
    dataset.addColumn("serie");
    dataset.addColumn("emissionDate");
    dataset.addColumn("operationDate");
    dataset.addColumn("supplierName");
    dataset.addColumn("documentValue");
    dataset.addColumn("totalItemsValue");
    dataset.addColumn("situation");
    dataset.addColumn("fiscalOperationDescription");
    dataset.addColumn("itemCount");
    dataset.addColumn("itensJson");
    dataset.addColumn("storeId");
    dataset.addColumn("invoiceId");
    dataset.addColumn("emitterEmployeeId");
    dataset.addColumn("updatedAt");
}

function addErrorRow(dataset, message) {
    dataset.addRow([
        "false",
        String(message || "Erro ao consultar NFe."),
        "",
        "",
        "",
        "",
        "",
        "",
        "",
        "",
        "",
        "",
        "0",
        "[]",
        "",
        "",
        "",
        ""
    ]);
}

function getConstraintValue(constraints, fieldName) {
    if (!constraints || !fieldName) return "";
    var target = String(fieldName).toLowerCase();

    for (var i = 0; i < constraints.length; i++) {
        var c = constraints[i];
        if (!c || !c.fieldName) continue;
        if (String(c.fieldName).toLowerCase() === target) {
            return c.initialValue;
        }
    }
    return "";
}

function resolveAuth(constraints) {
    var token = trim(getFirstConstraintValue(constraints, [
        "token",
        "accessToken",
        "xAccessToken",
        "bearerToken",
        "access_token"
    ]));
    if (!token) {
        try {
            token = trim(java.lang.System.getenv("GINSENG_FISCAL_TOKEN"));
        } catch (e1) {}
    }
    if (!token) {
        // fallback informado durante homologacao
        token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhcGlnaW5zZW5nIiwiZXhwIjoxNzg3NDQ4MDY3fQ.GJqcIJBkMIfp_q_KRzgGuAHWWo93j3FWo3TObKqlAwA";
    }

    var basicUser = trim(getFirstConstraintValue(constraints, [
        "basicUser",
        "username",
        "user"
    ]));
    var basicPass = trim(getFirstConstraintValue(constraints, [
        "basicPass",
        "password",
        "pass"
    ]));

    // Fallback operacional para homologacao, conforme credenciais validadas em teste manual.
    if (!basicUser) basicUser = "fluig";
    if (!basicPass) basicPass = "Ginseng@";

    return {
        token: token,
        basicUser: basicUser,
        basicPass: basicPass
    };
}

function getFirstConstraintValue(constraints, names) {
    if (!names || !(names instanceof Array)) return "";
    for (var i = 0; i < names.length; i++) {
        var val = getConstraintValue(constraints, names[i]);
        if (trim(val)) return val;
    }
    return "";
}

function buildAuthVariants(auth) {
    auth = auth || {};
    var variants = [];

    var token = trim(auth.token);
    var basicUser = trim(auth.basicUser);
    var basicPass = trim(auth.basicPass);
    var basicHeader = buildBasicAuthHeader(basicUser, basicPass);

    if (token) {
        variants.push({
            name: "Bearer+XToken",
            authorization: "Bearer " + token,
            xAccessToken: token
        });
        variants.push({
            name: "XToken",
            xAccessToken: token
        });
        variants.push({
            name: "Bearer",
            authorization: "Bearer " + token
        });
    }

    if (basicHeader) {
        variants.push({
            name: "Basic",
            authorization: basicHeader
        });
        if (token) {
            variants.push({
                name: "Basic+XToken",
                authorization: basicHeader,
                xAccessToken: token
            });
        }
    }

    variants.push({ name: "NoAuth" });
    return dedupeAuthVariants(variants);
}

function dedupeAuthVariants(variants) {
    var out = [];
    var seen = {};

    for (var i = 0; i < variants.length; i++) {
        var v = variants[i] || {};
        var signature = trim(v.authorization) + "|" + trim(v.xAccessToken);
        if (seen[signature]) continue;
        seen[signature] = true;
        out.push(v);
    }

    if (!out.length) {
        out.push({ name: "NoAuth" });
    }

    return out;
}

function invokeAuthorizedGet(clientService, endpoint, authVariant) {
    var headers = { "Accept": "application/json" };
    applyAuthHeaders(headers, authVariant);

    var data = {
        companyId: String(getValue("WKCompany") || "1"),
        serviceCode: "Fastapi",
        endpoint: endpoint,
        method: "get",
        timeoutService: "30000",
        params: {},
        headers: headers
    };

    var vo = clientService.invoke(JSON.stringify(data));
    return {
        status: vo ? String(vo.getHttpStatusResult() || "") : "",
        body: vo ? String(vo.getResult() || "") : ""
    };
}

function parseApiPayload(bodyText) {
    var raw = trim(bodyText);
    if (!raw) return null;

    var obj = parseJsonSafe(raw);
    if (!obj) return null;

    if (typeof obj.success !== "undefined" || typeof obj.data !== "undefined") {
        return obj;
    }

    if (obj.content) {
        if (typeof obj.content === "string") {
            var c = parseJsonSafe(obj.content);
            if (c) return c;
        } else {
            return obj.content;
        }
    }

    if (obj.result) {
        if (typeof obj.result === "string") {
            var r = parseJsonSafe(obj.result);
            if (r) return r;
        } else {
            return obj.result;
        }
    }

    return obj;
}

function isApiSuccess(obj) {
    if (!obj) return false;
    if (obj.success === true) return true;
    if (String(obj.success).toLowerCase() === "true") return true;
    if (obj.data || obj.items || obj.result || obj.content) return true;
    return false;
}

function extractInvoiceData(apiObj, expectedKey) {
    if (!apiObj) return null;
    var normalizedKey = normalizeDigits(expectedKey);
    return extractInvoiceFromNode(apiObj, normalizedKey);
}

function extractInvoiceFromNode(node, normalizedKey) {
    if (!node) return null;

    if (typeof node === "string") {
        var parsed = parseJsonSafe(node);
        if (!parsed) return null;
        return extractInvoiceFromNode(parsed, normalizedKey);
    }

    if (node instanceof Array) {
        return extractInvoiceFromArray(node, normalizedKey);
    }

    if (matchesInvoiceObject(node, normalizedKey)) {
        return node;
    }

    var nestedCandidates = [
        node.data,
        node.items,
        node.rows,
        node.results,
        node.content,
        node.result,
        node.list,
        node.transferencias,
        node.rgbTransferencias
    ];

    for (var i = 0; i < nestedCandidates.length; i++) {
        var nested = extractInvoiceFromNode(nestedCandidates[i], normalizedKey);
        if (nested) return nested;
    }

    return null;
}

function extractInvoiceFromArray(list, normalizedKey) {
    if (!list || !(list instanceof Array) || list.length === 0) return null;

    for (var i = 0; i < list.length; i++) {
        var item = list[i];
        if (matchesInvoiceObject(item, normalizedKey)) {
            return item;
        }
    }

    return list[0] || null;
}

function matchesInvoiceObject(item, normalizedKey) {
    if (!item || item instanceof Array || typeof item !== "object") return false;

    var keyCandidate = normalizeDigits(item.key || item.nfeKey || item.chave || item.invoiceKey || item.nfe_key);
    if (normalizedKey && keyCandidate) {
        return keyCandidate === normalizedKey;
    }

    if (keyCandidate) return true;

    var hasIdentity = trim(item.invoiceNumber || item.numeroNota || item.invoiceId || item.transferId);
    return !!hasIdentity;
}

function resolveItems(dataNfe) {
    if (!dataNfe || typeof dataNfe !== "object") return [];

    var candidates = [
        dataNfe.itens,
        dataNfe.itensJson,
        dataNfe.items,
        dataNfe.products,
        dataNfe.produtos
    ];

    for (var i = 0; i < candidates.length; i++) {
        var raw = candidates[i];
        if (!raw) continue;

        if (raw instanceof Array) {
            return raw;
        }

        if (typeof raw === "string") {
            var parsed = parseJsonSafe(raw);
            if (parsed instanceof Array) {
                return parsed;
            }
        }
    }

    return [];
}

function fetchDirect(url, timeoutMs, auth) {
    var conn = null;
    var reader = null;
    try {
        var URL = java.net.URL;
        var InputStreamReader = java.io.InputStreamReader;
        var BufferedReader = java.io.BufferedReader;
        var StringBuilder = java.lang.StringBuilder;

        conn = new URL(url).openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(timeoutMs || 30000);
        conn.setReadTimeout(timeoutMs || 30000);
        conn.setRequestProperty("Accept", "application/json");

        var authHeaders = {};
        applyAuthHeaders(authHeaders, auth);
        for (var h in authHeaders) {
            if (!authHeaders.hasOwnProperty(h)) continue;
            if (!authHeaders[h]) continue;
            conn.setRequestProperty(h, String(authHeaders[h]));
        }

        var status = conn.getResponseCode();
        var stream = (status >= 200 && status < 300) ? conn.getInputStream() : conn.getErrorStream();
        if (stream == null) return { status: status, body: "" };

        reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
        var sb = new StringBuilder();
        var line = null;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }

        return { status: String(status), body: String(sb.toString()) };
    } catch (e) {
        return { status: "", body: "" };
    } finally {
        try { if (reader) reader.close(); } catch (e1) {}
        try { if (conn) conn.disconnect(); } catch (e2) {}
    }
}

function applyAuthHeaders(headers, authVariant) {
    headers = headers || {};
    authVariant = authVariant || {};

    var authorization = trim(authVariant.authorization);
    var xAccessToken = trim(authVariant.xAccessToken);

    if (authorization) {
        headers.Authorization = authorization;
    }
    if (xAccessToken) {
        headers["x-access-token"] = xAccessToken;
    }
}

function buildBasicAuthHeader(basicUser, basicPass) {
    var user = trim(basicUser);
    var pass = trim(basicPass);
    if (!user || !pass) return "";

    var raw = String(user) + ":" + String(pass);
    var bytes = new java.lang.String(raw).getBytes("UTF-8");
    var encoded = java.util.Base64.getEncoder().encodeToString(bytes);
    return "Basic " + String(encoded);
}

function buildNfeItems(items) {
    var out = [];
    if (!items || !(items instanceof Array)) return out;

    for (var i = 0; i < items.length; i++) {
        var item = items[i] || {};
        out.push({
            productId: trim(item.productId),
            quantity: toNumber(item.quantity || item.completeQuantity),
            code: trim(item.code || item.sku || item.productCode || item.codigo || item.productId),
            unitValue: toNumber(item.unitValue || item.unit_value || item.value)
        });
    }

    return out;
}

function toNumber(value) {
    var text = trim(value).replace(",", ".");
    var n = parseFloat(text);
    return isNaN(n) ? 0 : n;
}

function normalizeDigits(value) {
    return String(value == null ? "" : value).replace(/\D/g, "");
}

function parseJsonSafe(text) {
    try {
        return JSON.parse(text);
    } catch (e) {
        return null;
    }
}

function trim(value) {
    return String(value == null ? "" : value).trim();
}

function formatIsoDate(value) {
    var text = trim(value);
    if (!text) return "";

    try {
        var datePart = text.split("T")[0].split("-");
        if (datePart.length !== 3) {
            return text;
        }
        return datePart[2] + "/" + datePart[1] + "/" + datePart[0];
    } catch (e) {
        return text;
    }
}

function formatIsoDateTime(value) {
    var text = trim(value);
    if (!text) return "";

    try {
        var dt = text.split("T");
        if (dt.length < 1) return text;

        var datePart = dt[0].split("-");
        if (datePart.length !== 3) return text;

        var timePart = "";
        if (dt.length > 1) {
            timePart = dt[1].replace("Z", "");
            var dotIndex = timePart.indexOf(".");
            if (dotIndex >= 0) {
                timePart = timePart.substring(0, dotIndex);
            }
        }

        var dateStr = datePart[2] + "/" + datePart[1] + "/" + datePart[0];
        if (!timePart) return dateStr;

        return dateStr + " " + timePart;
    } catch (e) {
        return text;
    }
}
