/**
 * Render a set of MwSource into a single markwhen `.mw` document.
 *
 * The output is deterministic for a given input (stable sort + escaping)
 * so that projections can be cached and diffed.
 */

import type { MwEvent, MwProjection, MwSource } from './types';

const MAX_TITLE = 90;
const TAG_COLOR_DEFAULTS: Record<string, string> = {
	rcal: 'blue',
	rnotes: 'green',
	rtasks: 'orange',
	rvote: 'purple',
	rschedule: 'teal',
	rtrips: 'amber',
	rinbox: 'gray',
};

export interface RenderOptions {
	title?: string;
	timezone?: string;
	/** Default view when a markwhen renderer opens the doc. */
	view?: 'timeline' | 'calendar' | 'gantt';
	/** Extra frontmatter tag colors to merge with inferred defaults. */
	colors?: Record<string, string>;
	/**
	 * Absolute URL base (protocol + host, no trailing slash). If set, any
	 * relative `href` on events is prefixed with this so the markwhen
	 * parser recognizes it as a link. Required for in-timeline "open in
	 * rApp" clickthrough.
	 */
	baseUrl?: string;
}

function iso(ts: number): string {
	// UTC YYYY-MM-DD HH:mm:ss form; markwhen accepts ISO-like.
	const d = new Date(ts);
	const pad = (n: number) => String(n).padStart(2, '0');
	return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ` +
		`${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`;
}

function sanitizeTitle(raw: string): string {
	const t = raw.replace(/[\r\n]+/g, ' ').trim();
	return t.length > MAX_TITLE ? t.slice(0, MAX_TITLE - 1) + '…' : t;
}

function formatRange(ev: MwEvent): string {
	if (ev.end && ev.end > ev.start) return `${iso(ev.start)} / ${iso(ev.end)}`;
	return iso(ev.start);
}

function formatTags(tags: string[] | undefined): string {
	if (!tags || tags.length === 0) return '';
	return ' ' + tags.map(t => `#${t.replace(/\s+/g, '_')}`).join(' ');
}

function dedup(events: MwEvent[]): MwEvent[] {
	const seen = new Set<string>();
	const out: MwEvent[] = [];
	for (const ev of events) {
		if (seen.has(ev.sourceId)) continue;
		seen.add(ev.sourceId);
		out.push(ev);
	}
	return out;
}

function absolutizeHref(href: string, baseUrl?: string): string {
	if (/^[a-z]+:\/\//i.test(href)) return href;
	if (!baseUrl) return href;
	const base = baseUrl.replace(/\/$/, '');
	return href.startsWith('/') ? `${base}${href}` : `${base}/${href}`;
}

function stripEmoji(s: string): string {
	// Pull leading emoji + space off a label like "📅 Calendar" → "Calendar".
	return s.replace(/^\p{Extended_Pictographic}\s*/u, '').trim() || s;
}

export function renderMarkwhen(sources: MwSource[], opts: RenderOptions = {}): MwProjection {
	const title = opts.title ?? 'rSpace Timeline';
	const tz = opts.timezone ?? 'UTC';
	const view = opts.view ?? 'timeline';

	const colors: Record<string, string> = { ...opts.colors };
	for (const s of sources) {
		if (s.color) colors[s.tag] = s.color;
		else if (!colors[s.tag] && TAG_COLOR_DEFAULTS[s.id]) colors[s.tag] = TAG_COLOR_DEFAULTS[s.id];
	}

	const frontColors = Object.entries(colors)
		.map(([t, c]) => `#${t}: ${c}`)
		.join('\n');

	const lines: string[] = [];
	lines.push('---');
	lines.push(`title: ${title}`);
	lines.push(`view: ${view}`);
	lines.push(`timezone: ${tz}`);
	if (frontColors) lines.push(frontColors);
	lines.push('---', '');
	lines.push('// Generated by shared/markwhen/projection.ts — do not hand-edit.', '');

	const allEvents: MwEvent[] = [];

	for (const src of sources) {
		const events = dedup([...src.events].sort((a, b) => a.start - b.start));
		if (events.length === 0) continue;

		const openLabel = `Open in ${stripEmoji(src.label)}`;
		lines.push(`section ${src.label} #${src.tag}`);
		for (const ev of events) {
			const titleLine = `${formatRange(ev)}: ${sanitizeTitle(ev.title)}${formatTags(ev.tags)}`;
			lines.push(titleLine);
			if (ev.description) {
				for (const dLine of ev.description.split(/\r?\n/)) {
					if (dLine.trim()) lines.push('  ' + dLine);
				}
			}
			if (ev.href) {
				const url = absolutizeHref(ev.href, opts.baseUrl);
				// Markdown-style link so the markwhen parser detects it as a
				// clickable link in the event's description tooltip/popup.
				lines.push(`  [${openLabel}](${url})`);
			}
		}
		lines.push('endSection', '');
		allEvents.push(...events);
	}

	return {
		text: lines.join('\n'),
		events: allEvents,
		count: allEvents.length,
	};
}
