import React, { useCallback, useEffect, useMemo, useState, } from "react";
import Route from "../../../../Types/API/Route";
import URL from "../../../../Types/API/URL";
import CopyTextButton from "../../CopyTextButton/CopyTextButton";
import Icon from "../../Icon/Icon";
import IconProp from "../../../../Types/Icon/IconProp";
import Link from "../../Link/Link";
import OneUptimeDate from "../../../../Types/Date";
import SeverityBadge from "./SeverityBadge";
import API from "../../../Utils/API/API";
import ModelAPI from "../../../Utils/ModelAPI/ModelAPI";
import { APP_API_URL } from "../../../Config";
import HTTPErrorResponse from "../../../../Types/API/HTTPErrorResponse";
const prepareBody = (body) => {
    if (!body) {
        return {
            isJson: false,
            pretty: "",
            compact: "",
            raw: "",
        };
    }
    try {
        const parsed = JSON.parse(body);
        const pretty = JSON.stringify(parsed, null, 2);
        const compact = JSON.stringify(parsed);
        return {
            isJson: true,
            pretty,
            compact,
            raw: body,
        };
    }
    catch (_a) {
        return {
            isJson: false,
            pretty: body,
            compact: body,
            raw: body,
        };
    }
};
function parseContextRow(row) {
    return {
        id: String(row["_id"] || ""),
        time: String(row["time"] || ""),
        severity: String(row["severityText"] || "Unspecified"),
        body: String(row["body"] || ""),
        primaryEntityId: String(row["primaryEntityId"] || ""),
    };
}
const LogDetailsPanel = (props) => {
    var _a, _b, _c, _d, _e;
    const [activeTab, setActiveTab] = useState("details");
    const [contextBefore, setContextBefore] = useState([]);
    const [contextAfter, setContextAfter] = useState([]);
    const [contextLoading, setContextLoading] = useState(false);
    const [contextError, setContextError] = useState("");
    const [contextLoaded, setContextLoaded] = useState(false);
    const variant = props.variant || "floating";
    const primaryEntityId = ((_a = props.log.primaryEntityId) === null || _a === void 0 ? void 0 : _a.toString()) || "";
    const service = props.serviceMap[primaryEntityId];
    const serviceName = (service === null || service === void 0 ? void 0 : service.name) || primaryEntityId || "Unknown service";
    const serviceColor = ((service === null || service === void 0 ? void 0 : service.serviceColor) && (service === null || service === void 0 ? void 0 : service.serviceColor.toString())) || "#64748b";
    const bodyDetails = useMemo(() => {
        var _a;
        return prepareBody((_a = props.log.body) === null || _a === void 0 ? void 0 : _a.toString());
    }, [props.log.body]);
    const attributeEntries = useMemo(() => {
        const raw = props.log.attributes;
        if (!raw) {
            return [];
        }
        return Object.keys(raw)
            .sort((left, right) => {
            return left.localeCompare(right);
        })
            .map((key) => {
            const rawValue = raw[key];
            const value = rawValue === null || rawValue === undefined
                ? ""
                : typeof rawValue === "string"
                    ? rawValue
                    : JSON.stringify(rawValue);
            return { key, value };
        });
    }, [props.log.attributes]);
    const attributesAsJson = useMemo(() => {
        if (attributeEntries.length === 0) {
            return null;
        }
        const flat = {};
        for (const entry of attributeEntries) {
            flat[entry.key] = entry.value;
        }
        return JSON.stringify(flat, null, 2);
    }, [attributeEntries]);
    const traceId = ((_b = props.log.traceId) === null || _b === void 0 ? void 0 : _b.toString()) || "";
    const spanId = ((_c = props.log.spanId) === null || _c === void 0 ? void 0 : _c.toString()) || "";
    const traceRoute = useMemo(() => {
        if (!traceId || !props.getTraceRoute) {
            return undefined;
        }
        return props.getTraceRoute(traceId, props.log);
    }, [traceId, props]);
    const spanRoute = useMemo(() => {
        if (!spanId) {
            return undefined;
        }
        if (props.getSpanRoute) {
            return props.getSpanRoute(spanId, props.log);
        }
        if (props.getTraceRoute && traceId) {
            const baseRoute = props.getTraceRoute(traceId, props.log);
            if (!baseRoute) {
                return undefined;
            }
            if (baseRoute instanceof Route) {
                const nextRoute = new Route(baseRoute.toString());
                nextRoute.addQueryParams({ spanId });
                return nextRoute;
            }
            const nextUrl = URL.fromURL(baseRoute);
            nextUrl.addQueryParam("spanId", spanId);
            return nextUrl;
        }
        return undefined;
    }, [spanId, props, traceId]);
    const loadContext = useCallback(async () => {
        var _a;
        if (!props.projectId || !primaryEntityId || !props.log.time) {
            setContextError("Missing project or service information for context.");
            return;
        }
        try {
            setContextLoading(true);
            setContextError("");
            const response = await API.post({
                url: URL.fromString(APP_API_URL.toString()).addRoute("/telemetry/logs/context"),
                data: {
                    logId: ((_a = props.log.getColumnValue("_id")) === null || _a === void 0 ? void 0 : _a.toString()) || "",
                    primaryEntityId: primaryEntityId,
                    time: props.log.time
                        ? OneUptimeDate.toString(props.log.time)
                        : "",
                    count: 5,
                },
                headers: Object.assign({}, ModelAPI.getCommonHeaders()),
            });
            if (response instanceof HTTPErrorResponse) {
                throw response;
            }
            const before = response.data["before"] || [];
            const after = response.data["after"] || [];
            setContextBefore(before.map(parseContextRow));
            setContextAfter(after.map(parseContextRow));
            setContextLoaded(true);
        }
        catch (err) {
            setContextError(`Failed to load log context. ${API.getFriendlyErrorMessage(err)}`);
        }
        finally {
            setContextLoading(false);
        }
    }, [props.projectId, primaryEntityId, props.log]);
    useEffect(() => {
        if (activeTab === "context" && !contextLoaded && !contextLoading) {
            void loadContext();
        }
    }, [activeTab, contextLoaded, contextLoading, loadContext]);
    // Reset context when log changes
    useEffect(() => {
        setContextLoaded(false);
        setContextBefore([]);
        setContextAfter([]);
        setContextError("");
        setActiveTab("details");
    }, [props.log]);
    const containerClassName = variant === "embedded"
        ? "rounded-lg border border-gray-200 bg-white p-5 shadow-sm"
        : "rounded-lg border border-gray-200 bg-white p-5 shadow-md";
    const headerBorderClass = "border-gray-200";
    const surfaceCardClass = "border-gray-200 bg-gray-50";
    const smallBadgeClass = "inline-flex items-center gap-1 rounded-full border border-gray-200 bg-gray-50 px-2 py-1 text-[11px] font-mono uppercase tracking-wide text-gray-600";
    const tabClass = (isActive) => {
        return `px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${isActive
            ? "bg-indigo-50 text-indigo-700 border border-indigo-200"
            : "text-gray-600 hover:text-gray-800 hover:bg-gray-50 border border-transparent"}`;
    };
    const renderContextLogRow = (ctxLog, isCurrent) => {
        const rowClass = isCurrent
            ? "border-l-2 border-l-indigo-500 bg-indigo-50"
            : "border-l-2 border-l-transparent hover:bg-gray-50";
        return (React.createElement("div", { key: ctxLog.id || ctxLog.time, className: `flex items-start gap-3 px-3 py-2 ${rowClass}` },
            React.createElement("span", { className: "flex-none whitespace-nowrap font-mono text-[11px] text-gray-400" }, ctxLog.time
                ? OneUptimeDate.getDateAsUserFriendlyFormattedString(OneUptimeDate.fromString(ctxLog.time))
                : "-"),
            React.createElement(SeverityBadge, { severity: ctxLog.severity }),
            React.createElement("span", { className: "min-w-0 flex-1 truncate font-mono text-xs text-gray-700" }, ctxLog.body.slice(0, 200) || "-")));
    };
    return (React.createElement("div", { className: containerClassName },
        React.createElement("div", { className: `flex flex-col gap-4 border-b ${headerBorderClass} pb-4 lg:flex-row lg:items-start lg:justify-between` },
            React.createElement("div", { className: "flex flex-1 items-start gap-3" },
                React.createElement("span", { className: "mt-1 h-3 w-3 flex-none rounded-full border border-gray-200", style: { backgroundColor: serviceColor }, "aria-hidden": "true" }),
                React.createElement("div", { className: "space-y-3" },
                    React.createElement("div", { className: "flex flex-wrap items-center gap-3" },
                        React.createElement("h3", { className: "text-lg font-semibold text-gray-900" }, serviceName),
                        React.createElement(SeverityBadge, { severity: props.log.severityText })),
                    React.createElement("div", { className: "flex flex-wrap items-center gap-2" },
                        props.log.time && (React.createElement("span", { className: smallBadgeClass },
                            React.createElement(Icon, { icon: IconProp.Clock, className: "h-3 w-3" }),
                            OneUptimeDate.getDateAsUserFriendlyFormattedString(props.log.time))),
                        traceId && (React.createElement("span", { className: smallBadgeClass },
                            React.createElement(Icon, { icon: IconProp.Logs, className: "h-3 w-3" }),
                            "Trace")),
                        spanId && (React.createElement("span", { className: smallBadgeClass },
                            React.createElement(Icon, { icon: IconProp.Terminal, className: "h-3 w-3" }),
                            "Span"))))),
            props.onClose && (React.createElement("button", { type: "button", onClick: props.onClose, className: "flex h-9 w-9 items-center justify-center rounded-full border border-gray-200 bg-gray-50 text-gray-400 transition-colors hover:border-gray-300 hover:text-gray-600", title: "Close details" },
                React.createElement(Icon, { icon: IconProp.Close, className: "h-4 w-4" })))),
        React.createElement("div", { className: "mt-3 flex items-center gap-1" },
            React.createElement("button", { type: "button", className: tabClass(activeTab === "details"), onClick: () => {
                    setActiveTab("details");
                } }, "Details"),
            props.projectId && (React.createElement("button", { type: "button", className: tabClass(activeTab === "context"), onClick: () => {
                    setActiveTab("context");
                } }, "Context"))),
        activeTab === "details" && (React.createElement("div", { className: "mt-4 space-y-5 text-sm text-gray-700" },
            React.createElement("section", { className: "space-y-3" },
                React.createElement("header", { className: "flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400" },
                    React.createElement("span", null, "Log Body"),
                    React.createElement(CopyTextButton, { textToBeCopied: bodyDetails.raw, size: "xs", variant: "ghost", iconOnly: false, title: "Copy log body" })),
                React.createElement("div", { className: `rounded-lg border ${surfaceCardClass} p-4` }, bodyDetails.isJson ? (React.createElement("pre", { className: "max-h-80 overflow-auto whitespace-pre-wrap break-words font-mono text-[13px] leading-6 text-gray-800" }, bodyDetails.pretty)) : (React.createElement("p", { className: "whitespace-pre-wrap break-words font-mono text-[13px] leading-6 text-gray-800" }, bodyDetails.pretty || "-")))),
            (traceId || spanId) && (React.createElement("section", { className: "grid gap-4 md:grid-cols-2" },
                traceId && (React.createElement("div", { className: `rounded-lg border ${surfaceCardClass} p-4` },
                    React.createElement("div", { className: "mb-2 flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400" },
                        React.createElement("span", null, "Trace ID"),
                        React.createElement(CopyTextButton, { textToBeCopied: traceId, size: "xs", variant: "ghost", iconOnly: true, title: "Copy trace id" })),
                    React.createElement("div", { className: "flex items-center justify-between gap-2" },
                        traceRoute ? (React.createElement(Link, { to: traceRoute, className: "max-w-full truncate font-mono text-xs text-indigo-600 hover:text-indigo-500", title: `View trace ${traceId}` }, traceId)) : (React.createElement("span", { className: "max-w-full truncate font-mono text-xs text-gray-700", title: traceId }, traceId)),
                        traceRoute && (React.createElement(Icon, { icon: IconProp.ExternalLink, className: "h-4 w-4 flex-none text-indigo-400" }))))),
                spanId && (React.createElement("div", { className: `rounded-lg border ${surfaceCardClass} p-4` },
                    React.createElement("div", { className: "mb-2 flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400" },
                        React.createElement("span", null, "Span ID"),
                        React.createElement(CopyTextButton, { textToBeCopied: spanId, size: "xs", variant: "ghost", iconOnly: true, title: "Copy span id" })),
                    React.createElement("div", { className: "flex items-center justify-between gap-2" },
                        spanRoute ? (React.createElement(Link, { to: spanRoute, className: "max-w-full truncate font-mono text-xs text-indigo-600 hover:text-indigo-500", title: `View span ${spanId}` }, spanId)) : (React.createElement("span", { className: "max-w-full truncate font-mono text-xs text-gray-700", title: spanId }, spanId)),
                        spanRoute && (React.createElement(Icon, { icon: IconProp.ExternalLink, className: "h-4 w-4 flex-none text-indigo-400" }))))))),
            attributeEntries.length > 0 && (React.createElement("section", { className: "space-y-3" },
                React.createElement("header", { className: "flex items-center justify-between text-[11px] uppercase tracking-wide text-gray-400" },
                    React.createElement("span", null, "Attributes"),
                    attributesAsJson && (React.createElement(CopyTextButton, { textToBeCopied: attributesAsJson, size: "xs", variant: "ghost", iconOnly: false, title: "Copy attributes as JSON" }))),
                React.createElement("div", { className: `max-h-80 overflow-auto rounded-lg border ${surfaceCardClass}` },
                    React.createElement("ul", { className: "divide-y divide-gray-200" }, attributeEntries.map((entry) => {
                        return (React.createElement("li", { key: entry.key, className: "group flex items-start gap-3 px-3 py-2 hover:bg-white" },
                            React.createElement("span", { className: "w-56 flex-none truncate font-mono text-[12px] text-gray-500", title: entry.key }, entry.key),
                            React.createElement("span", { className: "min-w-0 flex-1 break-all font-mono text-[12px] text-gray-800", title: entry.value }, entry.value || (React.createElement("span", { className: "italic text-gray-400" }, "empty"))),
                            React.createElement("div", { className: "flex flex-none items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100" },
                                props.onFilterByAttribute && entry.value && (React.createElement("button", { type: "button", className: "rounded p-1 text-gray-400 hover:bg-gray-100 hover:text-indigo-600", title: `Filter by ${entry.key}: ${entry.value}`, onClick: () => {
                                        props.onFilterByAttribute(entry.key, entry.value);
                                    } },
                                    React.createElement(Icon, { icon: IconProp.Filter, className: "h-3.5 w-3.5" }))),
                                React.createElement(CopyTextButton, { textToBeCopied: entry.value, size: "xs", variant: "ghost", iconOnly: true, title: `Copy ${entry.key}` }))));
                    }))))))),
        activeTab === "context" && (React.createElement("div", { className: "mt-4 text-sm text-gray-700" },
            contextLoading && (React.createElement("div", { className: "flex items-center justify-center py-8 text-xs text-gray-400" }, "Loading surrounding logs...")),
            contextError && (React.createElement("div", { className: "rounded-md bg-red-50 p-3 text-xs text-red-600" }, contextError)),
            !contextLoading && !contextError && contextLoaded && (React.createElement("div", { className: "divide-y divide-gray-100 rounded-lg border border-gray-200" },
                contextBefore.length === 0 && contextAfter.length === 0 && (React.createElement("div", { className: "px-3 py-6 text-center text-xs text-gray-400" }, "No surrounding logs found for this service.")),
                contextBefore.map((ctxLog) => {
                    return renderContextLogRow(ctxLog, false);
                }),
                renderContextLogRow({
                    id: ((_d = props.log.getColumnValue("_id")) === null || _d === void 0 ? void 0 : _d.toString()) || "current",
                    time: props.log.time ? props.log.time.toString() : "",
                    severity: ((_e = props.log.severityText) === null || _e === void 0 ? void 0 : _e.toString()) || "Unspecified",
                    body: props.log.body || "",
                    primaryEntityId: primaryEntityId,
                }, true),
                contextAfter.map((ctxLog) => {
                    return renderContextLogRow(ctxLog, false);
                })))))));
};
export default LogDetailsPanel;
//# sourceMappingURL=LogDetailsPanel.js.map