import { abbreviateHome, lastPathSegment } from './path-display';
import {
	DIRECTORY_PATH_SUFFIX,
	FILE_URI_PREFIX,
	MENTION_BADGE_FILE_ICON_PATHS,
	MENTION_BADGE_FOLDER_ICON_PATHS,
	MENTION_LINK_SCAN_FLAGS
} from '$lib/constants';
import { FileMentionEntryType } from '$lib/enums';
import type { FileMentionEntry } from '$lib/types';

export {
	MENTION_BADGE_CLASSNAME,
	MENTION_BADGE_ICON_CLASSNAME,
	MENTION_BADGE_SVG_ATTRIBUTES,
	MENTION_BADGE_FILE_ICON_PATHS,
	MENTION_BADGE_FOLDER_ICON_PATHS
} from '$lib/constants';

// `)` is allowed in a path only when not followed by whitespace or `[`,
// so macOS paths parse while adjacent badges still terminate the match.
const FILE_MENTION_LINK_SOURCE = String.raw`\[([^\]\n]+?)\]\(file:\/\/((?:[^)\n]|\)(?![\s[]))+)\)`;

export function fileMentionLinkRe(flags = ''): RegExp {
	return new RegExp(FILE_MENTION_LINK_SOURCE, flags);
}

export function containsFileMentionLink(value: string): boolean {
	return fileMentionLinkRe().test(value);
}

// Escape each path segment for a markdown link destination (spaces/parens
// break CommonMark); keeps the trailing slash that marks a directory.
export function encodeFileLinkPath(path: string): string {
	return path
		.split('/')
		.map((segment) => encodeURIComponent(segment))
		.join('/');
}

// Malformed escape sequences fall back to the input unchanged.
export function decodeFileLinkPath(path: string): string {
	try {
		return path
			.split('/')
			.map((segment) => decodeURIComponent(segment))
			.join('/');
	} catch {
		return path;
	}
}

export interface MentionTextSegment {
	text: string;
	mention: { name: string; path: string } | null;
}

/**
 * Split raw text into plain runs and `[name](file://path)` mentions.
 * The raw-text renderers walk these segments to draw badges without
 * handing the message to the markdown parser, so a `#` stays a `#`.
 */
export function splitMentionSegments(value: string): MentionTextSegment[] {
	const linkRe = fileMentionLinkRe(MENTION_LINK_SCAN_FLAGS);
	const segments: MentionTextSegment[] = [];

	let cursor = 0;
	let match: RegExpExecArray | null;

	while ((match = linkRe.exec(value)) !== null) {
		if (match.index > cursor) {
			segments.push({ mention: null, text: value.slice(cursor, match.index) });
		}

		segments.push({
			mention: { name: match[1], path: decodeFileLinkPath(match[2]) },
			text: match[0]
		});

		cursor = match.index + match[0].length;
	}

	if (cursor < value.length) segments.push({ mention: null, text: value.slice(cursor) });

	return segments;
}

export function getMentionBadgeIconPaths(path: string): readonly string[] {
	return path.endsWith(DIRECTORY_PATH_SUFFIX)
		? MENTION_BADGE_FOLDER_ICON_PATHS
		: MENTION_BADGE_FILE_ICON_PATHS;
}

export function getMentionBadgeLabel(
	name: string,
	path: string,
	showFullPath: boolean,
	home?: string | null
): string {
	if (!showFullPath) return name;

	const decoded = decodeFileLinkPath(path.replace(/\/+$/, ''));

	if (!decoded) return name;

	return abbreviateHome(decoded, home);
}

/**
 * Build the markdown link that replaces a mention token. Entry `path` is
 * already rooted, so `file://` + `/abs` yields the canonical `file:///`.
 * Null when the token is invalid.
 */
export function buildMentionInsertion(
	entry: FileMentionEntry,
	value: string,
	token: { start: number; end: number }
): { newValue: string; caretOffset: number } | null {
	if (token.start < 0 || token.end > value.length || token.start > token.end) return null;

	// Strip the entry's directory marker so it is not doubled below.
	const cleanedPath = entry.path.replace(/\/+$/, '');
	const pathWithSeparator =
		entry.type === FileMentionEntryType.DIRECTORY ? `${cleanedPath}/` : cleanedPath;
	const basename = lastPathSegment(cleanedPath) || entry.name;
	const insertion = `[${basename}](${FILE_URI_PREFIX}${encodeFileLinkPath(pathWithSeparator)}) `;
	const newValue = value.slice(0, token.start) + insertion + value.slice(token.end);

	return { caretOffset: token.start + insertion.length, newValue };
}
