export interface MarkupContext {
	/** `owner/repo` */
	repo: string;
	ref: string;
	dir?: string;
	host?: string;
}

const ABSOLUTE = /^[a-z][a-z0-9+.-]*:|^\/\//i;

export const isAbsoluteUrl = (url: string): boolean => ABSOLUTE.test(url);

export const isRepoRelative = (url: string): boolean =>
	url !== "" && !isAbsoluteUrl(url) && !url.startsWith("#");

// `.` and `..` have to collapse here instead of reaching the url
const normalize = (path: string): string => {
	const parts: string[] = [];
	for (const part of path.split("/")) {
		if (part === "" || part === ".") continue;
		if (part === "..") parts.pop();
		else parts.push(part);
	}
	return parts.join("/");
};

const withinRepo = (path: string, ctx: MarkupContext): string =>
	normalize(path.startsWith("/") ? path : `${ctx.dir ?? ""}/${path}`);

const splitSuffix = (url: string): [string, string] => {
	const at = url.search(/[?#]/);
	return at === -1 ? [url, ""] : [url.slice(0, at), url.slice(at)];
};

// markdown-it percent-encodes destinations before we ever see them, so only the
// ref still needs encoding
const repoUrl = (kind: string, url: string, ctx: MarkupContext): string => {
	const [path, suffix] = splitSuffix(url);
	if (path === "") return url;
	const ref = encodeURIComponent(ctx.ref);
	return `/${ctx.repo}/${kind}/${ref}/${withinRepo(path, ctx)}${suffix}`;
};

export const treeUrl = (url: string, ctx: MarkupContext): string => repoUrl("tree", url, ctx);

export const rawUrl = (url: string, ctx: MarkupContext): string => repoUrl("raw", url, ctx);

export const rawSrcset = (srcset: string, ctx: MarkupContext): string =>
	srcset
		.split(",")
		.map((candidate) => {
			const [url, ...descriptors] = candidate.trim().split(/\s+/);
			if (!isRepoRelative(url)) return candidate.trim();
			return [rawUrl(url, ctx), ...descriptors].join(" ");
		})
		.join(", ");
