---
import { DEFAULT_LOCALE, type Locale } from '~/consts';
import { getLocaleFromUrl, t } from '~/i18n/ui';
import { cacheAvatar } from '~/lib/avatar-cache';

declare global {
	// eslint-disable-next-line no-var
	var __WEBMENTION_TOKEN__: string;
}
const tokenRaw = (globalThis as unknown as { __WEBMENTION_TOKEN__?: string }).__WEBMENTION_TOKEN__;
const WEBMENTION_TOKEN = typeof tokenRaw === 'string' ? tokenRaw : '';

interface WMAuthor {
	name?: string;
	url?: string;
	photo?: string;
}
interface WMEntry {
	author?: WMAuthor;
	url: string;
	published?: string;
	'wm-received'?: string;
	'wm-id'?: number;
	'wm-property'?: string;
	content?: { text?: string };
}

interface Props {
	target: string | URL;
	locale?: Locale;
}

const { target, locale = getLocaleFromUrl(Astro.url) ?? DEFAULT_LOCALE } = Astro.props;

async function fetchMentions(target: string): Promise<WMEntry[]> {
	const token = WEBMENTION_TOKEN;
	if (!token) return [];
	const withSlash = target.endsWith('/') ? target : `${target}/`;
	const withoutSlash = target.replace(/\/+$/, '');
	const fetchOne = async (t: string) => {
		const url = new URL('https://webmention.io/api/mentions.jf2');
		url.searchParams.set('target', t);
		url.searchParams.set('token', token);
		url.searchParams.set('per-page', '100');
		const res = await fetch(url);
		if (!res.ok) return [] as WMEntry[];
		const json = (await res.json()) as { children?: WMEntry[] };
		return json.children ?? [];
	};
	const [a, b] = await Promise.all([fetchOne(withSlash), fetchOne(withoutSlash)]);
	const seen = new Set<number>();
	const merged: WMEntry[] = [];
	for (const m of [...a, ...b]) {
		const id = m['wm-id'];
		if (id == null || seen.has(id)) continue;
		seen.add(id);
		merged.push(m);
	}
	return merged;
}

const targetStr = target.toString();
const all = await fetchMentions(targetStr);

await Promise.all(
	all.map(async (m) => {
		if (m.author?.photo) {
			const local = await cacheAvatar(m.author.photo);
			if (local) m.author.photo = local;
			else delete m.author.photo;
		}
	}),
);

const likes = all.filter((m) => m['wm-property'] === 'like-of');
const reposts = all.filter((m) => m['wm-property'] === 'repost-of');
const replies = all.filter((m) => m['wm-property'] === 'in-reply-to');
const mentions = all.filter(
	(m) => !['like-of', 'repost-of', 'in-reply-to', 'bookmark-of'].includes(m['wm-property'] ?? ''),
);
const facepile = [...likes, ...reposts];

function authorInitial(m: WMEntry) {
	return m.author?.name?.trim()?.[0]?.toUpperCase() ?? '?';
}

function formatDate(iso?: string) {
	if (!iso) return '';
	const d = new Date(iso);
	return d.toLocaleDateString(locale === 'de' ? 'de-DE' : 'en-US', {
		year: 'numeric',
		month: 'short',
		day: 'numeric',
	});
}

const hasAny = facepile.length > 0 || replies.length > 0 || mentions.length > 0;
---

{
	hasAny && (
		<section class="webmentions" aria-labelledby="webmentions-heading">
			<h2 id="webmentions-heading">{t(locale, 'webmentions.title')}</h2>

			{facepile.length > 0 && (
				<div class="facepile-group">
					{likes.length > 0 && (
						<div class="facepile">
							<h3>
								{`${likes.length} ${t(locale, likes.length === 1 ? 'webmentions.like' : 'webmentions.likes')}`}
							</h3>
							<ul>
								{likes.map((m) => (
									<li>
										<a
											href={m.url}
											title={m.author?.name ?? m.url}
											rel="noopener nofollow external"
										>
											{m.author?.photo ? (
												<img src={m.author.photo} alt="" loading="lazy" />
											) : (
												<span class="avatar-fallback">{authorInitial(m)}</span>
											)}
										</a>
									</li>
								))}
							</ul>
						</div>
					)}

					{reposts.length > 0 && (
						<div class="facepile">
							<h3>
								{`${reposts.length} ${t(locale, reposts.length === 1 ? 'webmentions.repost' : 'webmentions.reposts')}`}
							</h3>
							<ul>
								{reposts.map((m) => (
									<li>
										<a
											href={m.url}
											title={m.author?.name ?? m.url}
											rel="noopener nofollow external"
										>
											{m.author?.photo ? (
												<img src={m.author.photo} alt="" loading="lazy" />
											) : (
												<span class="avatar-fallback">{authorInitial(m)}</span>
											)}
										</a>
									</li>
								))}
							</ul>
						</div>
					)}
				</div>
			)}

			{replies.length > 0 && (
				<div class="replies">
					<h3>{t(locale, 'webmentions.replies')}</h3>
					<ol>
						{replies.map((m) => (
							<li>
								<div class="meta">
									{m.author?.photo && (
										<img src={m.author.photo} alt="" class="avatar" loading="lazy" />
									)}
									<a
										href={m.author?.url ?? m.url}
										rel="noopener nofollow external"
										class="author"
									>
										{m.author?.name ?? m.url}
									</a>
									<a href={m.url} rel="noopener nofollow external" class="permalink">
										<time datetime={m['wm-received'] ?? m.published}>
											{formatDate(m['wm-received'] ?? m.published)}
										</time>
									</a>
								</div>
								{m.content?.text && <p>{m.content.text}</p>}
							</li>
						))}
					</ol>
				</div>
			)}

			{mentions.length > 0 && (
				<div class="mentions">
					<h3>{t(locale, 'webmentions.mentions')}</h3>
					<ul>
						{mentions.map((m) => (
							<li>
								<a href={m.url} rel="noopener nofollow external">
									{m.author?.name ?? m.url}
								</a>
								{m['wm-received'] && (
									<>
										{' · '}
										<time datetime={m['wm-received']}>{formatDate(m['wm-received'])}</time>
									</>
								)}
							</li>
						))}
					</ul>
				</div>
			)}
		</section>
	)
}

<style>
	.webmentions {
		margin-top: 3em;
		padding-top: 2em;
		border-top: 1px solid rgba(var(--gray-light), 1);
	}
	.webmentions h2 {
		margin: 0 0 1em;
		font-size: 1.25em;
	}
	.webmentions h3 {
		margin: 0 0 0.5em;
		font-size: 1em;
		color: rgb(var(--gray));
		font-weight: 600;
	}
	.facepile-group {
		display: flex;
		flex-wrap: wrap;
		gap: 2em;
		margin-bottom: 2em;
	}
	.facepile ul {
		list-style: none;
		padding: 0;
		margin: 0;
		display: flex;
		flex-wrap: wrap;
		gap: 0.4em;
	}
	.facepile a {
		display: inline-block;
		width: 36px;
		height: 36px;
		border-radius: 50%;
		overflow: hidden;
		background: rgba(var(--gray-light), 1);
	}
	.facepile img {
		width: 100%;
		height: 100%;
		object-fit: cover;
		display: block;
	}
	.avatar-fallback {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 100%;
		height: 100%;
		font-weight: 600;
		color: rgb(var(--gray-dark));
	}
	.replies ol {
		list-style: none;
		padding: 0;
		margin: 0;
		display: flex;
		flex-direction: column;
		gap: 1.25em;
	}
	.replies li {
		padding: 0.75em 1em;
		background: rgba(var(--gray-light), 0.4);
		border-radius: 8px;
	}
	.replies .meta {
		display: flex;
		align-items: center;
		gap: 0.6em;
		font-size: 0.9em;
		margin-bottom: 0.25em;
	}
	.replies .avatar {
		width: 24px;
		height: 24px;
		border-radius: 50%;
	}
	.replies .author {
		font-weight: 600;
	}
	.replies .permalink {
		margin-left: auto;
		color: rgb(var(--gray));
		font-size: 0.85em;
	}
	.replies p {
		margin: 0;
	}
	.mentions ul {
		list-style: none;
		padding: 0;
		margin: 0;
		display: flex;
		flex-direction: column;
		gap: 0.3em;
		font-size: 0.9em;
	}
</style>
