import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';

import ChronikRow from './ChronikRow.svelte';
import type { components } from '$lib/generated/api';

type ActivityFeedItemDTO = components['schemas']['ActivityFeedItemDTO'];

afterEach(cleanup);

const baseItem: ActivityFeedItemDTO = {
	kind: 'TEXT_SAVED',
	actor: { initials: 'MR', color: '#7a4f9a', name: 'Max Raddatz' },
	documentId: 'doc-1',
	documentTitle: 'Brief 1920',
	happenedAt: '2026-04-19T10:00:00Z',
	youMentioned: false,
	count: 1
};

describe('ChronikRow', () => {
	it('renders the document title', async () => {
		render(ChronikRow, { item: baseItem });
		await expect.element(page.getByText('Brief 1920')).toBeInTheDocument();
	});

	it('renders actor initials in avatar', async () => {
		render(ChronikRow, { item: baseItem });
		await expect.element(page.getByText('MR')).toBeInTheDocument();
	});

	it('renders "?" fallback avatar when actor is missing', async () => {
		const item: ActivityFeedItemDTO = { ...baseItem, actor: undefined };
		render(ChronikRow, { item });
		const fallback = document.querySelector('[data-testid="chronik-avatar-fallback"]');
		expect(fallback).not.toBeNull();
		expect(fallback?.textContent?.trim()).toBe('?');
	});

	it('wraps the row in a link to the document', async () => {
		render(ChronikRow, { item: baseItem });
		const link = document.querySelector('a[href="/documents/doc-1"]');
		expect(link).not.toBeNull();
	});

	// --- #668 negative guarantee: Chronik never fabricates a letter date ---
	it('renders the activity timestamp, not a letter date, and no undated badge', async () => {
		// The row shows the relative activity time (happenedAt), never the letter's
		// documentDate — ActivityFeedItemDTO carries no date surface to badge.
		render(ChronikRow, { item: baseItem });
		// No undated badge is introduced into a Chronik row.
		expect(document.querySelector('[data-testid="undated-badge"]')).toBeNull();
		// No fabricated "Datum unbekannt" letter-date label appears.
		await expect.element(page.getByText('Datum unbekannt')).not.toBeInTheDocument();
	});

	// --- simple variant ---
	it('renders simple variant when count === 1 and not a mention', async () => {
		render(ChronikRow, { item: baseItem });
		// No rollup count badge
		expect(document.querySelector('[data-testid="chronik-count-badge"]')).toBeNull();
		// No for-you marker
		expect(document.querySelector('[data-testid="chronik-foryou-marker"]')).toBeNull();
		// No comment preview
		expect(document.querySelector('[data-testid="chronik-comment-preview"]')).toBeNull();
	});

	// --- rollup variant ---
	it('renders rollup variant with count badge when count > 1', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'TEXT_SAVED',
			count: 3,
			happenedAt: '2026-04-19T10:00:00Z',
			happenedAtUntil: '2026-04-19T11:30:00Z'
		};
		render(ChronikRow, { item });
		const badge = document.querySelector('[data-testid="chronik-count-badge"]');
		expect(badge).not.toBeNull();
		expect(badge?.textContent).toContain('3');
	});

	it('renders a time range with an en-dash for rollup variant', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'FILE_UPLOADED',
			count: 5,
			happenedAt: '2026-04-19T10:00:00Z',
			happenedAtUntil: '2026-04-19T11:30:00Z'
		};
		render(ChronikRow, { item });
		// en-dash character U+2013
		const body = document.body.textContent ?? '';
		expect(body).toContain('\u2013');
	});

	// --- for-you variant ---
	it('renders for-you marker when youMentioned is true', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'MENTION_CREATED',
			youMentioned: true
		};
		render(ChronikRow, { item });
		const marker = document.querySelector('[data-testid="chronik-foryou-marker"]');
		expect(marker).not.toBeNull();
	});

	it('applies accent border to for-you variant outer wrapper', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'MENTION_CREATED',
			youMentioned: true
		};
		render(ChronikRow, { item });
		const wrapper = document.querySelector('[data-variant="for-you"]');
		expect(wrapper).not.toBeNull();
		expect(wrapper?.className).toContain('border-accent');
	});

	// --- comment variant ---
	it('renders comment preview for COMMENT_ADDED kind', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'COMMENT_ADDED'
		};
		render(ChronikRow, { item });
		const preview = document.querySelector('[data-testid="chronik-comment-preview"]');
		expect(preview).not.toBeNull();
	});

	it('comment preview does NOT duplicate the document title verbatim', async () => {
		// Leonie: user sees the title twice otherwise — looks like the comment is quoting itself.
		// Until the backend exposes item.commentPreview, the placeholder must be distinct.
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'COMMENT_ADDED',
			documentTitle: 'Brief vom 12. Juli 1920'
		};
		render(ChronikRow, { item });
		const preview = document.querySelector('[data-testid="chronik-comment-preview"]');
		expect(preview).not.toBeNull();
		expect(preview?.textContent).not.toContain('Brief vom 12. Juli 1920');
	});

	// --- deep-link href for comment events ---
	it('links to /documents/:id?commentId=…&annotationId=… for COMMENT_ADDED', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'COMMENT_ADDED',
			commentId: 'comment-7',
			annotationId: 'annot-9'
		};
		render(ChronikRow, { item });
		const link = document.querySelector(
			'a[href="/documents/doc-1?commentId=comment-7&annotationId=annot-9"]'
		);
		expect(link).not.toBeNull();
	});

	it('links to /documents/:id?commentId=…&annotationId=… for MENTION_CREATED', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'MENTION_CREATED',
			youMentioned: true,
			commentId: 'comment-8',
			annotationId: 'annot-11'
		};
		render(ChronikRow, { item });
		const link = document.querySelector(
			'a[href="/documents/doc-1?commentId=comment-8&annotationId=annot-11"]'
		);
		expect(link).not.toBeNull();
	});

	it('falls back to bare document href when commentId is absent on a comment row', async () => {
		// Back-compat for old/missing backend payloads. Still navigates sensibly.
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'COMMENT_ADDED'
		};
		render(ChronikRow, { item });
		const link = document.querySelector('a[href="/documents/doc-1"]');
		expect(link).not.toBeNull();
	});

	it('links to commentId-only URL when commentId is set but annotationId is absent', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'COMMENT_ADDED',
			commentId: 'comment-7'
			// annotationId absent — comment on a non-annotation block
		};
		render(ChronikRow, { item });
		const link = document.querySelector('a[href="/documents/doc-1?commentId=comment-7"]');
		expect(link).not.toBeNull();
	});

	// --- commentPreview content ---
	it('renders commentPreview text when variant is comment and commentPreview is present', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'COMMENT_ADDED',
			commentPreview: 'Hello family, great letter!'
		};
		render(ChronikRow, { item });
		const preview = document.querySelector('[data-testid="chronik-comment-preview"]');
		expect(preview).not.toBeNull();
		expect(preview?.textContent).toContain('Hello family, great letter!');
	});

	it('renders placeholder ellipsis when variant is comment and commentPreview is null', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'COMMENT_ADDED',
			commentPreview: undefined
		};
		render(ChronikRow, { item });
		const preview = document.querySelector('[data-testid="chronik-comment-preview"]');
		expect(preview).not.toBeNull();
		expect(preview?.textContent?.trim()).toBe('„…"');
	});

	it('does not render preview paragraph for non-comment variants', async () => {
		const item: ActivityFeedItemDTO = { ...baseItem, kind: 'TEXT_SAVED' };
		render(ChronikRow, { item });
		expect(document.querySelector('[data-testid="chronik-comment-preview"]')).toBeNull();
	});

	it('link has aria-label containing preview text for comment variant with preview', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'COMMENT_ADDED',
			commentPreview: 'A wonderful letter from grandma'
		};
		render(ChronikRow, { item });
		const link = document.querySelector('a[aria-label]');
		expect(link).not.toBeNull();
		expect(link?.getAttribute('aria-label')).toContain('A wonderful letter from grandma');
	});

	it('link still has aria-label for comment variant when commentPreview is absent', async () => {
		const item: ActivityFeedItemDTO = {
			...baseItem,
			kind: 'COMMENT_ADDED',
			commentPreview: undefined
		};
		render(ChronikRow, { item });
		const link = document.querySelector('a[aria-label]');
		expect(link).not.toBeNull();
		expect(link?.getAttribute('aria-label')).not.toBeNull();
	});

	// --- robustness: title rendering for edge cases ---
	it('still renders the row link when documentTitle is an empty string', async () => {
		// Felix: verbText.indexOf(docTitle) returned 0 for empty titles — the span
		// collapsed and before/after both emptied out. Swap to a sentinel-based
		// approach so this case renders like every other row.
		const empty: ActivityFeedItemDTO = { ...baseItem, documentTitle: '' };
		render(ChronikRow, { item: empty });
		const link = document.querySelector('a[href="/documents/doc-1"]');
		expect(link).not.toBeNull();
	});

	it('renders a short document title that could substring-match the verb', async () => {
		const short: ActivityFeedItemDTO = { ...baseItem, documentTitle: 'Brief' };
		render(ChronikRow, { item: short });
		const titleEls = document.querySelectorAll('[data-testid="chronik-doc-title"]');
		expect(titleEls.length).toBe(1);
		expect(titleEls[0].textContent).toBe('Brief');
	});
});
