import { getOutputForAutofixStep, getStatusDisplayName, isTerminalStatus } from "./seer-CderEQ-X.js";

//#region src/internal/formatting.ts
const LANGUAGE_EXTENSIONS = {
	".java": "java",
	".py": "python",
	".js": "javascript",
	".jsx": "javascript",
	".ts": "javascript",
	".tsx": "javascript",
	".rb": "ruby",
	".php": "php"
};
const LANGUAGE_MODULE_PATTERNS = [[/^(java\.|com\.|org\.)/, "java"]];
/**
* Detects the programming language of a stack frame based on the file extension.
* Falls back to the platform parameter if no filename is available or extension is unrecognized.
*
* @param frame - The stack frame containing file and location information
* @param platform - Optional platform hint to use as fallback
* @returns The detected language or platform fallback or "unknown"
*/
function detectLanguage(frame, platform) {
	if (frame.filename) {
		const ext = frame.filename.toLowerCase().match(/\.[^.]+$/)?.[0];
		if (ext && LANGUAGE_EXTENSIONS[ext]) return LANGUAGE_EXTENSIONS[ext];
	}
	if (frame.module) {
		for (const [pattern, language] of LANGUAGE_MODULE_PATTERNS) if (pattern.test(frame.module)) return language;
	}
	return platform || "unknown";
}
/**
* Formats a stack frame into a language-specific string representation.
* Different languages have different conventions for displaying stack traces.
*
* @param frame - The stack frame to format
* @param frameIndex - Optional frame index for languages that display frame numbers
* @param platform - Optional platform hint for language detection fallback
* @returns Formatted stack frame string
*/
function formatFrameHeader(frame, frameIndex, platform) {
	const language = detectLanguage(frame, platform);
	switch (language) {
		case "java": {
			const className = frame.module || "UnknownClass";
			const method = frame.function || "<unknown>";
			const source = frame.filename || "Unknown Source";
			const location = frame.lineNo ? `:${frame.lineNo}` : "";
			return `at ${className}.${method}(${source}${location})`;
		}
		case "python": {
			const file = frame.filename || frame.absPath || frame.module || "<unknown>";
			const func = frame.function || "<module>";
			const line = frame.lineNo ? `, line ${frame.lineNo}` : "";
			return `  File "${file}"${line}, in ${func}`;
		}
		case "javascript": return `${[
			frame.filename,
			frame.lineNo,
			frame.colNo
		].filter((i) => !!i).join(":")}${frame.function ? ` (${frame.function})` : ""}`;
		case "ruby": {
			const file = frame.filename || frame.module || "<unknown>";
			const func = frame.function ? ` \`${frame.function}\`` : "";
			const line = frame.lineNo ? `:${frame.lineNo}:in` : "";
			return `    from ${file}${line}${func}`;
		}
		case "php": {
			const file = frame.filename || "<unknown>";
			const line = frame.lineNo ? `(${frame.lineNo})` : "";
			const func = frame.function || "<unknown>";
			const prefix = frameIndex !== void 0 ? `#${frameIndex} ` : "";
			return `${prefix}${file}${line}: ${func}()`;
		}
		default: {
			const func = frame.function || "<unknown>";
			const location = frame.filename || frame.module || "<unknown>";
			const line = frame.lineNo ? `:${frame.lineNo}` : "";
			const col = frame.colNo != null ? `:${frame.colNo}` : "";
			return `    at ${func} (${location}${line}${col})`;
		}
	}
}
/**
* Formats a Sentry event into a structured markdown output.
* Includes error messages, stack traces, request info, and contextual data.
*
* @param event - The Sentry event to format
* @param options - Additional formatting context
* @returns Formatted markdown string
*/
function formatEventOutput(event, options) {
	let output = "";
	const messageEntry = event.entries.find((e) => e.type === "message");
	const exceptionEntry = event.entries.find((e) => e.type === "exception");
	const threadsEntry = event.entries.find((e) => e.type === "threads");
	const requestEntry = event.entries.find((e) => e.type === "request");
	const spansEntry = event.entries.find((e) => e.type === "spans");
	if (messageEntry) output += formatMessageInterfaceOutput(event, messageEntry.data);
	if (exceptionEntry) output += formatExceptionInterfaceOutput(event, exceptionEntry.data);
	else if (threadsEntry) output += formatThreadsInterfaceOutput(event, threadsEntry.data);
	if (requestEntry) output += formatRequestInterfaceOutput(event, requestEntry.data);
	if (event.type === "transaction") output += formatPerformanceIssueOutput(event, spansEntry?.data, options);
	output += formatTags(event.tags);
	output += formatContext(event.context);
	output += formatContexts(event.contexts);
	return output;
}
/**
* Extracts the context line matching the frame's line number for inline display.
* This is used in the full stacktrace view to show the actual line of code
* that caused the error inline with the stack frame.
*
* @param frame - The stack frame containing context lines
* @returns The line of code at the frame's line number, or empty string if not available
*/
function renderInlineContext(frame) {
	if (!frame.context?.length || !frame.lineNo) return "";
	const contextLine = frame.context.find(([lineNo]) => lineNo === frame.lineNo);
	return contextLine ? `\n${contextLine[1]}` : "";
}
/**
* Renders an enhanced view of a stack frame with context lines and variables.
* Used for the "Most Relevant Frame" section to provide detailed information
* about the most relevant application frame where the error occurred.
*
* @param frame - The stack frame to render with enhanced information
* @param event - The Sentry event containing platform information for language detection
* @returns Formatted string with frame header, context lines, and variables table
*/
function renderEnhancedFrame(frame, event) {
	const parts = [];
	parts.push("**Most Relevant Frame:**");
	parts.push("─────────────────────");
	parts.push(formatFrameHeader(frame, void 0, event.platform));
	if (frame.context?.length) {
		const contextLines = renderContextLines(frame);
		if (contextLines) {
			parts.push("");
			parts.push(contextLines);
		}
	}
	if (frame.vars && Object.keys(frame.vars).length > 0) {
		parts.push("");
		parts.push(renderVariablesTable(frame.vars));
	}
	return parts.join("\n");
}
function formatExceptionInterfaceOutput(event, data) {
	const parts = [];
	const exceptions = data.values || (data.value ? [data.value] : []);
	if (exceptions.length === 0) return "";
	const isChained = exceptions.length > 1;
	[...exceptions].reverse().forEach((exception, index) => {
		if (!exception) return;
		if (isChained && index > 0) {
			parts.push("");
			parts.push(getExceptionChainMessage(event.platform || null, index, exceptions.length));
			parts.push("");
		}
		const exceptionTitle = `${exception.type}${exception.value ? `: ${exception.value}` : ""}`;
		parts.push(index === 0 ? "### Error" : `### ${exceptionTitle}`);
		parts.push("");
		if (index === 0) {
			parts.push("```");
			parts.push(exceptionTitle);
			parts.push("```");
			parts.push("");
		}
		if (!exception.stacktrace || !exception.stacktrace.frames) {
			parts.push("**Stacktrace:**");
			parts.push("```");
			parts.push("No stacktrace available");
			parts.push("```");
			return;
		}
		const frames = exception.stacktrace.frames;
		if (index === 0) {
			const firstInAppFrame = findFirstInAppFrame(frames);
			if (firstInAppFrame && (firstInAppFrame.context?.length || firstInAppFrame.vars)) {
				parts.push(renderEnhancedFrame(firstInAppFrame, event));
				parts.push("");
				parts.push("**Full Stacktrace:**");
				parts.push("────────────────");
			} else parts.push("**Stacktrace:**");
		} else parts.push("**Stacktrace:**");
		parts.push("```");
		parts.push(frames.map((frame) => {
			const header = formatFrameHeader(frame, void 0, event.platform);
			const context = renderInlineContext(frame);
			return `${header}${context}`;
		}).join("\n"));
		parts.push("```");
	});
	parts.push("");
	parts.push("");
	return parts.join("\n");
}
/**
* Get the appropriate exception chain message based on the platform
*/
function getExceptionChainMessage(platform, index, totalExceptions) {
	const defaultMessage = "**During handling of the above exception, another exception occurred:**";
	if (!platform) return defaultMessage;
	switch (platform.toLowerCase()) {
		case "python": return "**During handling of the above exception, another exception occurred:**";
		case "java": return "**Caused by:**";
		case "csharp":
		case "dotnet": return "**---> Inner Exception:**";
		case "ruby": return "**Caused by:**";
		case "go": return "**Wrapped error:**";
		case "rust": return `**Caused by (${index}):**`;
		default: return defaultMessage;
	}
}
function formatRequestInterfaceOutput(event, data) {
	if (!data.method || !data.url) return "";
	return `### HTTP Request\n\n**Method:** ${data.method}\n**URL:** ${data.url}\n\n`;
}
function formatMessageInterfaceOutput(event, data) {
	if (!data.formatted && !data.message) return "";
	const message = data.formatted || data.message || "";
	return `### Error\n\n\`\`\`\n${message}\n\`\`\`\n\n`;
}
function formatThreadsInterfaceOutput(event, data) {
	if (!data.values || data.values.length === 0) return "";
	const crashedThread = data.values.find((t) => t.crashed);
	if (!crashedThread?.stacktrace?.frames) return "";
	const parts = [];
	if (crashedThread.name) {
		parts.push(`**Thread** (${crashedThread.name})`);
		parts.push("");
	}
	const frames = crashedThread.stacktrace.frames;
	const firstInAppFrame = findFirstInAppFrame(frames);
	if (firstInAppFrame && (firstInAppFrame.context?.length || firstInAppFrame.vars)) {
		parts.push(renderEnhancedFrame(firstInAppFrame, event));
		parts.push("");
		parts.push("**Full Stacktrace:**");
		parts.push("────────────────");
	} else parts.push("**Stacktrace:**");
	parts.push("```");
	parts.push(frames.map((frame) => {
		const header = formatFrameHeader(frame, void 0, event.platform);
		const context = renderInlineContext(frame);
		return `${header}${context}`;
	}).join("\n"));
	parts.push("```");
	parts.push("");
	return parts.join("\n");
}
/**
* Renders surrounding source code context for a stack frame.
* Shows a window of code lines around the error line with visual indicators.
*
* @param frame - The stack frame containing context lines
* @param contextSize - Number of lines to show before and after the error line (default: 3)
* @returns Formatted context lines with line numbers and arrow indicator for the error line
*/
function renderContextLines(frame, contextSize = 3) {
	if (!frame.context || frame.context.length === 0 || !frame.lineNo) return "";
	const lines = [];
	const errorLine = frame.lineNo;
	const maxLineNoWidth = Math.max(...frame.context.map(([lineNo]) => lineNo.toString().length));
	for (const [lineNo, code] of frame.context) {
		const isErrorLine = lineNo === errorLine;
		const lineNoStr = lineNo.toString().padStart(maxLineNoWidth, " ");
		if (Math.abs(lineNo - errorLine) <= contextSize) if (isErrorLine) lines.push(`  → ${lineNoStr} │ ${code}`);
		else lines.push(`    ${lineNoStr} │ ${code}`);
	}
	return lines.join("\n");
}
/**
* Formats a variable value for display in the variables table.
* Handles different types appropriately and safely, converting complex objects
* to readable representations and handling edge cases like circular references.
*
* @param value - The variable value to format (can be any type)
* @param maxLength - Maximum length for stringified objects/arrays (default: 80)
* @returns Human-readable string representation of the value
*/
function formatVariableValue(value, maxLength = 80) {
	try {
		if (typeof value === "string") return `"${value}"`;
		if (value === null) return "null";
		if (value === void 0) return "undefined";
		if (typeof value === "object") {
			const stringified = JSON.stringify(value);
			if (stringified.length > maxLength) {
				const truncateAt = maxLength - 6;
				let truncated = stringified.substring(0, truncateAt);
				const lastComma = truncated.lastIndexOf(",");
				if (lastComma > 0) truncated = truncated.substring(0, lastComma);
				if (Array.isArray(value)) return `${truncated}, ...]`;
				return `${truncated}, ...}`;
			}
			return stringified;
		}
		return String(value);
	} catch {
		return `<${typeof value}>`;
	}
}
/**
* Renders a table of local variables in a tree-like format.
* Uses box-drawing characters to create a visual hierarchy of variables
* and their values at the point where the error occurred.
*
* @param vars - Object containing variable names as keys and their values
* @returns Formatted variables table with tree-style prefix characters
*/
function renderVariablesTable(vars) {
	const entries = Object.entries(vars);
	if (entries.length === 0) return "";
	const lines = ["Local Variables:"];
	const lastIndex = entries.length - 1;
	entries.forEach(([key, value], index) => {
		const prefix = index === lastIndex ? "└─" : "├─";
		const valueStr = formatVariableValue(value);
		lines.push(`${prefix} ${key}: ${valueStr}`);
	});
	return lines.join("\n");
}
/**
* Finds the first application frame (in_app) in a stack trace.
* Searches from the bottom of the stack (oldest frame) to find the first
* frame that belongs to the user's application code rather than libraries.
*
* @param frames - Array of stack frames, typically in reverse chronological order
* @returns The first in-app frame found, or undefined if none exist
*/
function findFirstInAppFrame(frames) {
	for (let i = frames.length - 1; i >= 0; i--) if (frames[i].inApp === true) return frames[i];
	return void 0;
}
/**
* Constants for performance issue formatting
*/
const MAX_SPANS_IN_TREE = 10;
/**
* Safely parse a number from a string, returning a default if invalid
*/
function safeParseInt(value, defaultValue) {
	if (typeof value === "number") return value;
	if (typeof value === "string") {
		const parsed = Number.parseInt(value, 10);
		return Number.isNaN(parsed) ? defaultValue : parsed;
	}
	return defaultValue;
}
function normalizeSpanId(value) {
	if (typeof value === "string" && value) return value;
	return void 0;
}
function getSpanIdentifier(span) {
	if (span.span_id !== void 0) return normalizeSpanId(span.span_id);
	if (span.id !== void 0) return normalizeSpanId(span.id);
	return void 0;
}
function getSpanDurationMs(span) {
	if (typeof span.timestamp === "number" && typeof span.start_timestamp === "number") {
		const deltaSeconds = span.timestamp - span.start_timestamp;
		if (Number.isFinite(deltaSeconds) && deltaSeconds >= 0) return deltaSeconds * 1e3;
	}
	if (typeof span.duration === "number" && Number.isFinite(span.duration)) return span.duration >= 0 ? span.duration : 0;
	return 0;
}
function normalizeIdArray(values) {
	if (!Array.isArray(values)) return [];
	return values.map((value) => normalizeSpanId(value)).filter((value) => value !== void 0);
}
function isValidSpanArray(value) {
	return Array.isArray(value);
}
/**
* Get the repeating span descriptions from evidence data.
* Prefers repeatingSpansCompact (more concise) over repeatingSpans (verbose).
*/
function getRepeatingSpanLines(evidenceData) {
	if (Array.isArray(evidenceData.repeatingSpansCompact) && evidenceData.repeatingSpansCompact.length > 0) return evidenceData.repeatingSpansCompact.map((s) => typeof s === "string" ? s.trim() : "").filter((s) => s.length > 0);
	if (Array.isArray(evidenceData.repeatingSpans) && evidenceData.repeatingSpans.length > 0) return evidenceData.repeatingSpans.map((s) => typeof s === "string" ? s.trim() : "").filter((s) => s.length > 0);
	return [];
}
function isTraceSpan(node) {
	if (node === null || typeof node !== "object") return false;
	const candidate = node;
	return candidate.event_type === "span" && typeof candidate.event_id === "string";
}
function buildTraceSpanTree(trace, parentSpanIds, offenderSpanIds, maxSpans) {
	const offenderSet = new Set(offenderSpanIds);
	const spanMap = /* @__PURE__ */ new Map();
	function indexSpan(span) {
		const spanId = normalizeSpanId(span.additional_attributes?.span_id) || span.event_id;
		if (spanId && spanId.length > 0) spanMap.set(spanId, span);
		for (const child of span.children ?? []) if (isTraceSpan(child)) indexSpan(child);
	}
	for (const node of trace) if (isTraceSpan(node)) indexSpan(node);
	const roots = [];
	const budget = {
		count: 0,
		limit: maxSpans
	};
	for (const parentId of parentSpanIds) {
		const span = spanMap.get(parentId);
		if (!span) continue;
		const perfSpan = convertTraceSpanToPerformanceSpan(span, offenderSet, budget, 0);
		if (perfSpan) roots.push(perfSpan);
		if (budget.count >= budget.limit) break;
	}
	if (roots.length === 0 && offenderSpanIds.length > 0) for (const offenderId of offenderSpanIds) {
		const span = spanMap.get(offenderId);
		if (!span) continue;
		const perfSpan = convertTraceSpanToPerformanceSpan(span, offenderSet, budget, 0);
		if (perfSpan) roots.push(perfSpan);
		if (budget.count >= budget.limit) break;
	}
	if (roots.length === 0) return [];
	return renderPerformanceSpanTree(roots);
}
function convertTraceSpanToPerformanceSpan(span, offenderSet, budget, level) {
	if (budget.count >= budget.limit) return null;
	budget.count += 1;
	const spanId = normalizeSpanId(span.additional_attributes?.span_id) || span.event_id;
	const performanceSpan = {
		span_id: spanId,
		op: span.op || "unknown",
		description: formatTraceSpanDescription(span),
		duration: getTraceSpanDurationMs(span),
		is_n1_query: offenderSet.has(spanId),
		children: [],
		level
	};
	for (const child of span.children ?? []) {
		if (!isTraceSpan(child)) continue;
		if (budget.count >= budget.limit) break;
		const childSpan = convertTraceSpanToPerformanceSpan(child, offenderSet, budget, level + 1);
		if (childSpan) performanceSpan.children.push(childSpan);
		if (budget.count >= budget.limit) break;
	}
	return performanceSpan;
}
function formatTraceSpanDescription(span) {
	if (span.name && span.name.trim().length > 0) return span.name.trim();
	if (span.description && span.description.trim().length > 0) return span.description.trim();
	if (span.op && span.op.trim().length > 0) return span.op.trim();
	return "unnamed";
}
function getTraceSpanDurationMs(span) {
	if (typeof span.duration === "number" && span.duration >= 0) return span.duration;
	if (typeof span.end_timestamp === "number" && typeof span.start_timestamp === "number") {
		const deltaSeconds = span.end_timestamp - span.start_timestamp;
		if (Number.isFinite(deltaSeconds) && deltaSeconds >= 0) return deltaSeconds * 1e3;
	}
	return 0;
}
function buildOffenderSummaries(spans, offenderSpanIds) {
	if (offenderSpanIds.length === 0) return [];
	const spanMap = /* @__PURE__ */ new Map();
	for (const span of spans) {
		const identifier = getSpanIdentifier(span);
		if (identifier) spanMap.set(identifier, span);
	}
	const summaries = [];
	for (const offenderId of offenderSpanIds) {
		const span = spanMap.get(offenderId);
		if (span) {
			const description = span.description || span.op || `Span ${offenderId}`;
			const duration = getSpanDurationMs(span);
			const durationLabel = duration > 0 ? ` (${Math.round(duration)}ms)` : "";
			summaries.push(`${description}${durationLabel} [${offenderId}] [N+1]`);
		} else summaries.push(`Span ${offenderId} [N+1]`);
	}
	return summaries;
}
/**
* Renders a hierarchical tree of performance spans using box-drawing characters.
* Highlights N+1 queries with a special indicator.
*
* @param spans - Array of selected performance spans
* @returns Array of formatted strings representing the tree
*/
function renderPerformanceSpanTree(spans) {
	const lines = [];
	function renderSpan(span, prefix = "", isLast = true) {
		const connector = prefix === "" ? "" : isLast ? "└─ " : "├─ ";
		const displayName = span.description?.trim() || span.op || "unnamed";
		const shortId = span.span_id ? span.span_id.substring(0, 8) : "unknown";
		const durationDisplay = span.duration > 0 ? `${Math.round(span.duration)}ms` : "unknown";
		const metadataParts = [shortId];
		if (span.op && span.op !== "default") metadataParts.push(span.op);
		metadataParts.push(durationDisplay);
		const line = `${prefix}${connector}${displayName} [${metadataParts.join(" · ")}]${span.is_n1_query ? " [N+1]" : ""}`;
		lines.push(line);
		for (let i = 0; i < span.children.length; i++) {
			const child = span.children[i];
			const isLastChild = i === span.children.length - 1;
			const childPrefix = prefix + (isLast ? "   " : "│  ");
			renderSpan(child, childPrefix, isLastChild);
		}
	}
	for (let i = 0; i < spans.length; i++) {
		const span = spans[i];
		const isLastRoot = i === spans.length - 1;
		renderSpan(span, "", isLastRoot);
	}
	return lines;
}
function selectN1QuerySpans(spans, evidence, maxSpans = MAX_SPANS_IN_TREE) {
	const selected = [];
	let spanCount = 0;
	const offenderSpanIds = normalizeIdArray(evidence.offenderSpanIds);
	const parentSpanIds = normalizeIdArray(evidence.parentSpanIds);
	let parentSpan = null;
	if (parentSpanIds.length > 0) {
		const parent = spans.find((span) => {
			const identifier = getSpanIdentifier(span);
			return identifier ? parentSpanIds.includes(identifier) : false;
		});
		if (parent) {
			parentSpan = {
				span_id: getSpanIdentifier(parent) ?? "unknown",
				op: parent.op || "unknown",
				description: parent.description || evidence.parentSpan || "Parent Operation",
				duration: getSpanDurationMs(parent),
				is_n1_query: false,
				children: [],
				level: 0
			};
			selected.push(parentSpan);
			spanCount += 1;
		}
	}
	if (offenderSpanIds.length > 0) {
		const offenderSet = new Set(offenderSpanIds);
		const offenderSpans = spans.filter((span) => {
			const identifier = getSpanIdentifier(span);
			return identifier ? offenderSet.has(identifier) : false;
		}).slice(0, Math.max(0, maxSpans - spanCount));
		for (const span of offenderSpans) {
			const perfSpan = {
				span_id: getSpanIdentifier(span) ?? "unknown",
				op: span.op || "db.query",
				description: span.description || "Database Query",
				duration: getSpanDurationMs(span),
				is_n1_query: true,
				children: [],
				level: parentSpan ? 1 : 0
			};
			if (parentSpan) parentSpan.children.push(perfSpan);
			else selected.push(perfSpan);
			spanCount += 1;
			if (spanCount >= maxSpans) break;
		}
	}
	return selected;
}
/**
* Known Sentry performance issue types that we handle.
*
* NOTE: We intentionally only implement formatters for high-value performance issues
* that provide complex insights. Not all issue types need custom formatting - many
* can rely on the generic evidenceDisplay fields that Sentry provides.
*
* Currently fully implemented:
* - N+1 query detection (DB and API)
*
* Partially implemented:
* - Slow DB queries (shows parent span only)
*
* Not implemented (lower priority):
* - Asset-related issues (render blocking, uncompressed, large payloads)
* - File I/O issues
* - Consecutive queries
*/
const KNOWN_PERFORMANCE_ISSUE_TYPES = {
	N_PLUS_ONE_DB_QUERIES: "performance_n_plus_one_db_queries",
	N_PLUS_ONE_API_CALLS: "performance_n_plus_one_api_calls",
	SLOW_DB_QUERY: "performance_slow_db_query",
	RENDER_BLOCKING_ASSET: "performance_render_blocking_asset",
	CONSECUTIVE_DB_QUERIES: "performance_consecutive_db_queries",
	FILE_IO_MAIN_THREAD: "performance_file_io_main_thread",
	M_N_PLUS_ONE_DB_QUERIES: "performance_m_n_plus_one_db_queries",
	UNCOMPRESSED_ASSET: "performance_uncompressed_asset",
	LARGE_HTTP_PAYLOAD: "performance_large_http_payload"
};
/**
* Map numeric occurrence types to issue types (from Sentry's codebase).
*
* Sentry uses numeric type IDs internally in the occurrence data structure,
* but string issue types in the UI and other APIs. This mapping converts
* between them.
*
* Source: sentry/static/app/types/group.tsx in Sentry's codebase
* Range: 1xxx = transaction-based performance issues
*        2xxx = profile-based performance issues
*/
const OCCURRENCE_TYPE_TO_ISSUE_TYPE = {
	1001: KNOWN_PERFORMANCE_ISSUE_TYPES.SLOW_DB_QUERY,
	1004: KNOWN_PERFORMANCE_ISSUE_TYPES.RENDER_BLOCKING_ASSET,
	1006: KNOWN_PERFORMANCE_ISSUE_TYPES.N_PLUS_ONE_DB_QUERIES,
	1906: KNOWN_PERFORMANCE_ISSUE_TYPES.N_PLUS_ONE_DB_QUERIES,
	1007: KNOWN_PERFORMANCE_ISSUE_TYPES.CONSECUTIVE_DB_QUERIES,
	1008: KNOWN_PERFORMANCE_ISSUE_TYPES.FILE_IO_MAIN_THREAD,
	1009: "performance_consecutive_http",
	1010: KNOWN_PERFORMANCE_ISSUE_TYPES.N_PLUS_ONE_API_CALLS,
	1910: KNOWN_PERFORMANCE_ISSUE_TYPES.N_PLUS_ONE_API_CALLS,
	1012: KNOWN_PERFORMANCE_ISSUE_TYPES.UNCOMPRESSED_ASSET,
	1013: "performance_db_main_thread",
	1015: KNOWN_PERFORMANCE_ISSUE_TYPES.LARGE_HTTP_PAYLOAD,
	1016: "performance_http_overhead"
};
/**
* Formats N+1 query issue evidence data.
*
* N+1 queries are a common performance anti-pattern where code executes
* 1 query to get a list of items, then N additional queries (one per item)
* instead of using a single JOIN or batch query.
*
* Evidence fields we use:
* - parentSpan: The operation that triggered the N+1 queries
* - repeatingSpansCompact/repeatingSpans: The query pattern being repeated
* - numberRepeatingSpans: How many times the query was executed
* - offenderSpanIds: IDs of the actual span instances
* - parentSpanIds: IDs of parent spans for tree visualization
*/
function formatN1QueryEvidence(evidenceData, spansData, performanceTrace) {
	const parts = [];
	if (evidenceData.parentSpan) {
		parts.push("**Parent Operation:**");
		parts.push(`${evidenceData.parentSpan}`);
		parts.push("");
	}
	const repeatingLines = getRepeatingSpanLines(evidenceData);
	if (repeatingLines.length > 0) {
		parts.push("### Repeated Database Queries");
		parts.push("");
		const queryCount = evidenceData.numberRepeatingSpans ? safeParseInt(evidenceData.numberRepeatingSpans, 0) : evidenceData.numPatternRepetitions || evidenceData.offenderSpanIds?.length || 0;
		if (queryCount > 0) parts.push(`**Query executed ${queryCount} times:**`);
		if (repeatingLines.length === 1) {
			parts.push("```sql");
			parts.push(repeatingLines[0]);
			parts.push("```");
			parts.push("");
		} else {
			parts.push("**Repeated operations:**");
			for (const line of repeatingLines) parts.push(`- ${line}`);
			parts.push("");
		}
	}
	const parentSpanIds = normalizeIdArray(evidenceData.parentSpanIds);
	const offenderSpanIds = normalizeIdArray(evidenceData.offenderSpanIds);
	const traceLines = performanceTrace ? buildTraceSpanTree(performanceTrace, parentSpanIds, offenderSpanIds, MAX_SPANS_IN_TREE) : [];
	if (traceLines.length > 0) {
		parts.push(`### Span Tree (Limited to ${MAX_SPANS_IN_TREE} spans)`);
		parts.push("");
		parts.push("```");
		parts.push(...traceLines);
		parts.push("```");
		parts.push("");
	} else {
		const spanTree = isValidSpanArray(spansData) ? selectN1QuerySpans(spansData, evidenceData, MAX_SPANS_IN_TREE) : [];
		if (spanTree.length > 0) {
			parts.push(`### Span Tree (Limited to ${MAX_SPANS_IN_TREE} spans)`);
			parts.push("");
			parts.push("```");
			parts.push(...renderPerformanceSpanTree(spanTree));
			parts.push("```");
			parts.push("");
		} else if (isValidSpanArray(spansData)) {
			const offenderSummaries = buildOffenderSummaries(spansData, offenderSpanIds);
			if (offenderSummaries.length > 0) {
				parts.push("### Offending Spans");
				parts.push("");
				for (const summary of offenderSummaries) parts.push(`- ${summary}`);
			}
		}
	}
	return parts.join("\n");
}
/**
* Formats slow DB query issue evidence data.
*
* Currently only partially implemented - shows parent span information.
* Full implementation would show query duration, explain plan, etc.
*
* This is lower priority as the generic evidenceDisplay fields usually
* provide sufficient information for slow query issues.
*/
function formatSlowDbQueryEvidence(evidenceData, spansData) {
	const parts = [];
	if (evidenceData.parentSpan) {
		parts.push("**Parent Operation:**");
		parts.push(`${evidenceData.parentSpan}`);
		parts.push("");
	}
	console.warn("[formatSlowDbQueryEvidence] Evidence data rendering not yet fully implemented");
	return parts.join("\n");
}
/**
* Formats performance issue details from transaction events based on the issue type.
*
* This is the main dispatcher for performance issue formatting. It:
* 1. Detects the issue type from occurrence data (numeric or string)
* 2. Calls the appropriate type-specific formatter if implemented
* 3. Falls back to generic evidenceDisplay fields for unimplemented types
* 4. Provides span analysis fallback for events without occurrence data
*
* The occurrence data structure comes from Sentry's performance issue detection
* and contains evidence about what triggered the issue.
*
* @param event - The transaction event containing performance issue data
* @param spansData - The spans data from the event entries
* @returns Formatted markdown string with performance issue details
*/
function formatPerformanceIssueOutput(event, spansData, options) {
	const parts = [];
	const occurrence = event.occurrence;
	if (!occurrence) return "";
	let issueType;
	if (typeof occurrence.type === "number") issueType = OCCURRENCE_TYPE_TO_ISSUE_TYPE[occurrence.type];
	else issueType = occurrence.issueType || occurrence.type;
	const evidenceData = occurrence.evidenceData;
	if (evidenceData) switch (issueType) {
		case KNOWN_PERFORMANCE_ISSUE_TYPES.N_PLUS_ONE_DB_QUERIES:
		case KNOWN_PERFORMANCE_ISSUE_TYPES.N_PLUS_ONE_API_CALLS:
		case KNOWN_PERFORMANCE_ISSUE_TYPES.M_N_PLUS_ONE_DB_QUERIES: {
			const result = formatN1QueryEvidence(evidenceData, spansData, options?.performanceTrace);
			if (result) parts.push(result);
			break;
		}
		case KNOWN_PERFORMANCE_ISSUE_TYPES.SLOW_DB_QUERY: {
			const result = formatSlowDbQueryEvidence(evidenceData, spansData);
			if (result) parts.push(result);
			break;
		}
		default: if (issueType) console.warn(`[formatPerformanceIssueOutput] No custom formatter for issue type: ${issueType}`);
	}
	if (evidenceData?.transactionName) {
		parts.push("**Transaction:**");
		parts.push(`${evidenceData.transactionName}`);
		parts.push("");
	}
	if (occurrence.evidenceDisplay?.length > 0) {
		for (const display of occurrence.evidenceDisplay) if (display.important) {
			parts.push(`**${display.name}:**`);
			parts.push(`${display.value}`);
			parts.push("");
		}
	}
	return parts.length > 0 ? `${parts.join("\n")}\n` : "";
}
function formatTags(tags) {
	if (!tags || tags.length === 0) return "";
	return `### Tags\n\n${tags.map((tag) => `**${tag.key}**: ${tag.value}`).join("\n")}\n\n`;
}
function formatContext(context) {
	if (!context || Object.keys(context).length === 0) return "";
	return `### Extra Data\n\nAdditional data attached to this event.\n\n${Object.entries(context).map(([key, value]) => {
		return `**${key}**: ${JSON.stringify(value, void 0, 2)}`;
	}).join("\n")}\n\n`;
}
function formatContexts(contexts) {
	if (!contexts || Object.keys(contexts).length === 0) return "";
	return `### Additional Context\n\nThese are additional context provided by the user when they're instrumenting their application.\n\n${Object.entries(contexts).map(([name, data]) => `**${name}**\n${Object.entries(data).filter(([key, _]) => key !== "type").map(([key, value]) => {
		return `${key}: ${JSON.stringify(value, void 0, 2)}`;
	}).join("\n")}`).join("\n\n")}\n\n`;
}
/**
* Formats a brief Seer analysis summary for inclusion in issue details.
* Shows current status and high-level insights, prompting to use analyze_issue_with_seer for full details.
*
* @param autofixState - The autofix state containing Seer analysis data
* @param organizationSlug - The organization slug for the issue
* @param issueId - The issue ID (shortId)
* @returns Formatted markdown string with Seer summary, or empty string if no analysis exists
*/
function formatSeerSummary(autofixState, organizationSlug, issueId) {
	if (!autofixState || !autofixState.autofix) return "";
	const { autofix } = autofixState;
	const parts = [];
	parts.push("## Seer Analysis");
	parts.push("");
	const statusDisplay = getStatusDisplayName(autofix.status);
	if (!isTerminalStatus(autofix.status)) {
		parts.push(`**Status:** ${statusDisplay}`);
		parts.push("");
	}
	if (autofix.steps.length > 0) {
		const completedSteps = autofix.steps.filter((step) => step.status === "COMPLETED");
		const solutionStep = completedSteps.find((step) => step.type === "solution");
		if (solutionStep) {
			const solutionDescription = solutionStep.description;
			if (solutionDescription && typeof solutionDescription === "string" && solutionDescription.trim()) {
				parts.push("**Summary:**");
				parts.push(solutionDescription.trim());
			} else {
				const solutionOutput = getOutputForAutofixStep(solutionStep);
				const lines = solutionOutput.split("\n");
				const firstParagraph = lines.find((line) => line.trim().length > 50 && !line.startsWith("#") && !line.startsWith("*"));
				if (firstParagraph) {
					parts.push("**Summary:**");
					parts.push(firstParagraph.trim());
				}
			}
		} else if (completedSteps.length > 0) {
			const rootCauseStep = completedSteps.find((step) => step.type === "root_cause_analysis");
			if (rootCauseStep) {
				const typedStep = rootCauseStep;
				if (typedStep.causes && typedStep.causes.length > 0 && typedStep.causes[0].description) {
					parts.push("**Root Cause Identified:**");
					parts.push(typedStep.causes[0].description.trim());
				}
			} else parts.push(`**Progress:** ${completedSteps.length} of ${autofix.steps.length} steps completed`);
		}
	} else if (isTerminalStatus(autofix.status)) {
		if (autofix.status === "FAILED" || autofix.status === "ERROR") parts.push("**Status:** Analysis failed.");
		else if (autofix.status === "CANCELLED") parts.push("**Status:** Analysis was cancelled.");
		else if (autofix.status === "NEED_MORE_INFORMATION" || autofix.status === "WAITING_FOR_USER_RESPONSE") parts.push("**Status:** Analysis paused - additional information needed.");
	} else parts.push("Analysis has started but no results yet.");
	if (autofix.steps.length > 0 && isTerminalStatus(autofix.status)) {
		if (autofix.status === "FAILED" || autofix.status === "ERROR") {
			parts.push("");
			parts.push("**Status:** Analysis failed.");
		} else if (autofix.status === "CANCELLED") {
			parts.push("");
			parts.push("**Status:** Analysis was cancelled.");
		} else if (autofix.status === "NEED_MORE_INFORMATION" || autofix.status === "WAITING_FOR_USER_RESPONSE") {
			parts.push("");
			parts.push("**Status:** Analysis paused - additional information needed.");
		}
	}
	parts.push("");
	parts.push(`**Note:** For detailed root cause analysis and solutions, call \`analyze_issue_with_seer(organizationSlug='${organizationSlug}', issueId='${issueId}')\``);
	return `${parts.join("\n")}\n\n`;
}
/**
* Formats a Sentry issue with its latest event into comprehensive markdown output.
* Includes issue metadata, event details, and usage instructions.
*
* @param params - Object containing organization slug, issue, event, and API service
* @returns Formatted markdown string with complete issue information
*/
function formatIssueOutput({ organizationSlug, issue, event, apiService, autofixState, performanceTrace }) {
	let output = `# Issue ${issue.shortId} in **${organizationSlug}**\n\n`;
	const isPerformanceIssue = issue.issueType?.startsWith("performance_") || issue.issueCategory === "performance";
	if (isPerformanceIssue && issue.metadata) {
		const issueTitle = issue.metadata.title || issue.title;
		output += `**Description**: ${issueTitle}\n`;
		if (issue.metadata.location) output += `**Location**: ${issue.metadata.location}\n`;
		if (issue.metadata.value) output += `**Query Pattern**: \`${issue.metadata.value}\`\n`;
	} else {
		output += `**Description**: ${issue.title}\n`;
		output += `**Culprit**: ${issue.culprit}\n`;
	}
	output += `**First Seen**: ${new Date(issue.firstSeen).toISOString()}\n`;
	output += `**Last Seen**: ${new Date(issue.lastSeen).toISOString()}\n`;
	output += `**Occurrences**: ${issue.count}\n`;
	output += `**Users Impacted**: ${issue.userCount}\n`;
	output += `**Status**: ${issue.status}\n`;
	output += `**Platform**: ${issue.platform}\n`;
	output += `**Project**: ${issue.project.name}\n`;
	output += `**URL**: ${apiService.getIssueUrl(organizationSlug, issue.shortId)}\n`;
	output += "\n";
	output += "## Event Details\n\n";
	output += `**Event ID**: ${event.id}\n`;
	if (event.type === "error" || event.type === "default") output += `**Occurred At**: ${new Date(event.dateCreated).toISOString()}\n`;
	if (event.message) output += `**Message**:\n${event.message}\n`;
	output += "\n";
	output += formatEventOutput(event, { performanceTrace });
	if (autofixState) output += formatSeerSummary(autofixState, organizationSlug, issue.shortId);
	output += "# Using this information\n\n";
	output += `- You can reference the IssueID in commit messages (e.g. \`Fixes ${issue.shortId}\`) to automatically close the issue when the commit is merged.\n`;
	output += "- The stacktrace includes both first-party application code as well as third-party code, its important to triage to first-party code.\n";
	return output;
}

//#endregion
export { formatEventOutput, formatFrameHeader, formatIssueOutput };
//# sourceMappingURL=formatting-DytLf-hH.js.map