/**
 * Pure formatting utilities shared across extensions and the core agent.
 *
 * Purpose: centralise widely-used, dependency-free format helpers so extension
 * shared/ shims can re-export from the package rather than duplicating source.
 * No @singularity-forge/tui dependency — safe for any import context.
 *
 * Consumer: 35+ bundled extensions via src/resources/extensions/shared/format-utils.js.
 */

// ─── Duration Formatting ──────────────────────────────────────────────────────

/** Format a millisecond duration as a compact human-readable string. */
export function formatDuration(ms: number): string {
	if (ms > 0 && ms < 1000) return `${ms}ms`;
	const s = Math.floor(ms / 1000);
	if (s < 60) return `${s}s`;
	const m = Math.floor(s / 60);
	const rs = s % 60;
	if (m < 60) return `${m}m ${rs}s`;
	const h = Math.floor(m / 60);
	const rm = m % 60;
	return `${h}h ${rm}m`;
}

// ─── Token Count Formatting ──────────────────────────────────────────────────

/** Format a token count as a compact human-readable string (e.g. 1.5k, 1.50M). */
export function formatTokenCount(count: number): string {
	if (count < 1000) return `${count}`;
	if (count < 1_000_000) return `${(count / 1000).toFixed(1)}k`;
	return `${(count / 1_000_000).toFixed(2)}M`;
}

// ─── Text Truncation ─────────────────────────────────────────────────────────

/** Truncate a string to `maxLength` characters, replacing the last character with an ellipsis if needed. */
export function truncateWithEllipsis(text: string, maxLength: number): string {
	if (text.length <= maxLength) return text;
	return text.slice(0, maxLength - 1) + "…";
}

// ─── Data Visualization ───────────────────────────────────────────────────────

/**
 * Render a sparkline from numeric values using Unicode block characters.
 * Uses loop-based max to avoid stack overflow on large arrays.
 */
export function sparkline(values: number[]): string {
	if (values.length === 0) return "";
	const chars = "▁▂▃▄▅▆▇█";
	let max = 0;
	for (const v of values) {
		if (v > max) max = v;
	}
	if (max === 0) return chars[0].repeat(values.length);
	return values
		.map((v) => chars[Math.min(7, Math.floor((v / max) * 7))])
		.join("");
}

// ─── Date Formatting ─────────────────────────────────────────────────────────

/** Format an ISO date string as a compact locale string (e.g. "Mar 17, 2025, 02:30 PM"). */
export function formatDateShort(iso: string): string {
	try {
		const d = new Date(iso);
		return d.toLocaleString("en-US", {
			month: "short",
			day: "numeric",
			year: "numeric",
			hour: "2-digit",
			minute: "2-digit",
		});
	} catch {
		return iso;
	}
}

// ─── Hyperlinks ──────────────────────────────────────────────────────────────

/** Wrap text in an OSC 8 hyperlink for terminals that support clickable links. */
export function fileLink(filePath: string, displayText?: string): string {
	const uri = `file://${filePath}`;
	const label = displayText ?? filePath;
	return `\x1b]8;;${uri}\x07${label}\x1b]8;;\x07`;
}

// ─── ANSI Stripping ───────────────────────────────────────────────────────────

/** Strip ANSI escape sequences from a string. */
export function stripAnsi(s: string): string {
	return s.replace(/\x1b\[[0-9;]*m/g, "");
}

// ─── String Array Normalization ─────────────────────────────────────────────

export interface NormalizeStringArrayOptions {
	/** Deduplicate items after filtering and trimming. */
	dedupe?: boolean;
}

/**
 * Normalize an unknown value to a string array.
 * Filters to string items, trims whitespace, removes empty strings.
 * Optionally deduplicates.
 */
export function normalizeStringArray(
	value: unknown,
	options?: NormalizeStringArrayOptions,
): string[] {
	if (!Array.isArray(value)) return [];
	const items = (value as unknown[])
		.filter((item): item is string => typeof item === "string")
		.map((item) => item.trim())
		.filter(Boolean);
	return options?.dedupe ? [...new Set(items)] : items;
}
