import { SfError } from "@salesforce/core";
import c from "chalk";
import { NotifProviderRoot } from "./notifProviderRoot.js";
import { getCurrentGitBranch, getGitRepoName, uxLog } from "../utils/index.js";
import { UtilsNotifs } from "./index.js";
import { CONSTANTS, getEnvVar } from "../../config/index.js";
import { getSeverityIcon, removeMarkdown } from "../utils/notifUtils.js";
import { GitProvider } from "../gitProvider/index.js";
import axios from "axios";
const MAX_LOKI_LOG_LENGTH = Number(process.env.MAX_LOKI_LOG_LENGTH || 200000);
const TRUNCATE_LOKI_ELEMENTS_LENGTH = Number(process.env.TRUNCATE_LOKI_ELEMENTS_LENGTH || 500);
export class ApiProvider extends NotifProviderRoot {
    apiUrl;
    payload;
    payloadFormatted;
    metricsApiUrl;
    metricsPayload;
    getLabel() {
        return "sfdx-hardis Api connector";
    }
    // Always send notifications to API endpoint
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    isApplicableForNotif(notifMessage) {
        return true;
    }
    isUserNotifProvider() {
        return false;
    }
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    async postNotification(notifMessage) {
        const apiPromises = []; // Use Promises to optimize performances with api calls
        this.apiUrl = getEnvVar("NOTIF_API_URL");
        if (this.apiUrl == null) {
            throw new SfError("[ApiProvider] You need to define a variable NOTIF_API_URL to use sfdx-hardis Api notifications");
        }
        // Build initial payload data from notifMessage
        await this.buildPayload(notifMessage);
        // Format payload according to API endpoint: for example, Grafana loki
        await this.formatPayload();
        // Send notif
        apiPromises.push(this.sendToApi());
        // Handle Metrics API if provided
        this.metricsApiUrl = getEnvVar("NOTIF_API_METRICS_URL");
        if (this.metricsApiUrl !== null) {
            this.buildMetricsPayload();
            if (this.metricsPayload.length > 0) {
                apiPromises.push(this.sendToMetricsApi());
            }
        }
        await Promise.allSettled(apiPromises);
        return;
    }
    // Build message
    async buildPayload(notifMessage) {
        const firstLineMarkdown = UtilsNotifs.prefixWithSeverityEmoji(UtilsNotifs.slackToTeamsMarkdown(notifMessage.text.split("\n")[0]), notifMessage.severity);
        const logTitle = removeMarkdown(firstLineMarkdown);
        let logBodyText = UtilsNotifs.prefixWithSeverityEmoji(UtilsNotifs.slackToTeamsMarkdown(notifMessage.text), notifMessage.severity);
        // Add text details
        if (notifMessage?.attachments?.length && notifMessage?.attachments?.length > 0) {
            let text = "\n\n";
            for (const attachment of notifMessage.attachments) {
                if (attachment.text) {
                    text += attachment.text + "\n\n";
                }
            }
            if (text !== "") {
                logBodyText += UtilsNotifs.slackToTeamsMarkdown(text) + "\n\n";
            }
        }
        // Add action blocks
        if (notifMessage.buttons?.length && notifMessage.buttons?.length > 0) {
            logBodyText += "Links:\n\n";
            for (const button of notifMessage.buttons) {
                // Url button
                if (button.url) {
                    logBodyText += "  - " + button.text + ": " + button.url + "\n\n";
                }
            }
            logBodyText += "\n\n";
        }
        // Add sfdx-hardis ref
        logBodyText += `Powered by sfdx-hardis: ${CONSTANTS.DOC_URL_ROOT}`;
        logBodyText = removeMarkdown(logBodyText);
        // Build payload
        const repoName = (await getGitRepoName() || "").replace(".git", "");
        const currentGitBranch = await getCurrentGitBranch();
        const conn = globalThis.jsForceConn;
        const orgIdentifier = (conn.instanceUrl) ? conn.instanceUrl.replace("https://", "").replace(".my.salesforce.com", "").replace(/\./gm, "__") : currentGitBranch || "ERROR apiProvider";
        const notifKey = orgIdentifier + "!!" + notifMessage.type;
        this.payload = {
            source: "sfdx-hardis",
            type: notifMessage.type,
            orgIdentifier: orgIdentifier,
            gitIdentifier: `${repoName}/${currentGitBranch}`,
            severity: notifMessage.severity,
            data: Object.assign(notifMessage.data, {
                _dateTime: new Date().toISOString(),
                _severityIcon: getSeverityIcon(notifMessage.severity),
                _title: logTitle,
                _logBodyText: logBodyText,
                _logElements: notifMessage.logElements,
                _metrics: notifMessage.metrics,
                _metricsKeys: Object.keys(notifMessage.metrics),
                _notifKey: notifKey,
            }),
        };
        // Add job url if available
        const jobUrl = await GitProvider.getJobUrl();
        if (jobUrl) {
            this.payload.data._jobUrl = jobUrl;
        }
    }
    async formatPayload() {
        if ((this.apiUrl || "").includes("loki/api/v1/push")) {
            await this.formatPayloadLoki();
            return;
        }
        this.payloadFormatted = this.payload;
    }
    async formatPayloadLoki() {
        const currentTimeNanoseconds = Date.now() * 1000 * 1000;
        const payloadCopy = Object.assign({}, this.payload);
        delete payloadCopy.data;
        let payloadDataJson = JSON.stringify(this.payload.data);
        const bodyBytesLen = new TextEncoder().encode(payloadDataJson).length;
        // Truncate log elements if log entry is too big
        if (bodyBytesLen > MAX_LOKI_LOG_LENGTH) {
            const newPayloadData = Object.assign({}, this.payload.data);
            const logElements = newPayloadData._logElements;
            if (logElements.length > TRUNCATE_LOKI_ELEMENTS_LENGTH) {
                const truncatedLogElements = logElements.slice(0, TRUNCATE_LOKI_ELEMENTS_LENGTH);
                newPayloadData._logElements = truncatedLogElements;
                newPayloadData._logElementsTruncated = true;
                payloadDataJson = JSON.stringify(newPayloadData);
                uxLog("log", this, c.grey(`[ApiProvider] Truncated _logElements from ${logElements.length} to ${truncatedLogElements.length} to avoid Loki entry max size reached (initial size: ${bodyBytesLen} bytes)`));
            }
            else {
                newPayloadData._logBodyText = (newPayloadData._logBodyText || "").slice(0, 100) + "\n ... (truncated)";
                payloadDataJson = JSON.stringify(newPayloadData);
                uxLog("log", this, c.grey(`[ApiProvider] Truncated _logBodyText to 100 to avoid Loki entry max size reached (initial size: ${bodyBytesLen} bytes)`));
            }
        }
        this.payloadFormatted = {
            streams: [
                {
                    stream: payloadCopy,
                    values: [[`${currentTimeNanoseconds}`, payloadDataJson]],
                },
            ],
        };
    }
    // Call remote API
    async sendToApi() {
        const axiosConfig = {
            responseType: "json",
        };
        // Basic Auth
        if (getEnvVar("NOTIF_API_BASIC_AUTH_USERNAME") != null) {
            axiosConfig.auth = {
                username: getEnvVar("NOTIF_API_BASIC_AUTH_USERNAME") || "",
                password: getEnvVar("NOTIF_API_BASIC_AUTH_PASSWORD") || "",
            };
        }
        // Bearer token
        else if (getEnvVar("NOTIF_API_BEARER_TOKEN") != null) {
            axiosConfig.headers = { Authorization: `Bearer ${getEnvVar("NOTIF_API_BEARER_TOKEN")}` };
        }
        // POST message
        try {
            const axiosResponse = await axios.post(this.apiUrl || "", this.payloadFormatted, axiosConfig);
            const httpStatus = axiosResponse.status;
            if (httpStatus > 200 && httpStatus < 300) {
                uxLog("action", this, c.cyan(`[ApiProvider] Posted message to API ${this.apiUrl} (${httpStatus})`));
                if (getEnvVar("NOTIF_API_DEBUG") === "true") {
                    uxLog("action", this, c.cyan(JSON.stringify(this.payloadFormatted, null, 2)));
                }
            }
        }
        catch (e) {
            uxLog("warning", this, c.yellow(`[ApiProvider] Error while sending message to API ${this.apiUrl}: ${e.message}`));
            uxLog("log", this, c.grey("Request body: \n" + JSON.stringify(this.payloadFormatted)));
            uxLog("log", this, c.grey("Response body: \n" + JSON.stringify(e?.response?.data || {})));
        }
    }
    // Build something like MetricName,source=sfdx-hardis,orgIdentifier=hardis-group metric=12.7,min=0,max=70,percent=0.63
    buildMetricsPayload() {
        // Build tag field
        const metricTags = `source=${this.payload.source},` +
            `type=${this.payload.type},` +
            `orgIdentifier=${this.payload.orgIdentifier},` +
            `gitIdentifier=${this.payload.gitIdentifier}`;
        // Add extra fields and value
        const metricsPayloadLines = [];
        for (const metricId of Object.keys(this.payload.data._metrics)) {
            const metricData = this.payload.data._metrics[metricId];
            let metricPayloadLine = metricId + "," + metricTags + " ";
            if (typeof metricData === "number") {
                metricPayloadLine += "metric=" + metricData.toFixed(2);
                metricsPayloadLines.push(metricPayloadLine);
            }
            else if (typeof metricData === "object") {
                const metricFields = [];
                if (metricData.min) {
                    metricFields.push("min=" + metricData.min.toFixed(2));
                }
                if (metricData.max) {
                    metricFields.push("max=" + metricData.max.toFixed(2));
                }
                if (metricData.percent) {
                    metricFields.push("percent=" + metricData.percent.toFixed(2));
                }
                metricFields.push("metric=" + metricData.value.toFixed(2));
                metricPayloadLine += metricFields.join(",");
                metricsPayloadLines.push(metricPayloadLine);
            }
        }
        // Result as single string with carriage returns
        this.metricsPayload = metricsPayloadLines.join("\n");
    }
    // Call remote API
    async sendToMetricsApi() {
        const axiosConfig = {
            responseType: "json",
        };
        // Basic Auth
        if (getEnvVar("NOTIF_API_METRICS_BASIC_AUTH_USERNAME") != null) {
            axiosConfig.auth = {
                username: getEnvVar("NOTIF_API_METRICS_BASIC_AUTH_USERNAME") || "",
                password: getEnvVar("NOTIF_API_METRICS_BASIC_AUTH_PASSWORD") || "",
            };
        }
        // Bearer token
        else if (getEnvVar("NOTIF_API_METRICS_BEARER_TOKEN") != null) {
            axiosConfig.headers = { Authorization: `Bearer ${getEnvVar("NOTIF_API_METRICS_BEARER_TOKEN")}` };
        }
        // POST message
        try {
            const axiosResponse = await axios.post(this.metricsApiUrl || "", this.metricsPayload, axiosConfig);
            const httpStatus = axiosResponse.status;
            if (httpStatus > 200 && httpStatus < 300) {
                uxLog("action", this, c.cyan(`[ApiMetricProvider] Posted message to API ${this.metricsApiUrl} (${httpStatus})`));
                if (getEnvVar("NOTIF_API_DEBUG") === "true") {
                    uxLog("action", this, c.cyan(JSON.stringify(this.metricsPayload, null, 2)));
                }
            }
        }
        catch (e) {
            uxLog("warning", this, c.yellow(`[ApiMetricProvider] Error while sending message to API ${this.metricsApiUrl}: ${e.message}`));
            uxLog("log", this, c.grey("Request body: \n" + JSON.stringify(this.metricsPayload)));
            uxLog("log", this, c.grey("Response body: \n" + JSON.stringify(e?.response?.data || {})));
        }
    }
}
//# sourceMappingURL=apiProvider.js.map