import { describe, expect, it } from 'vitest';
import { groupDocuments } from './groupDocuments';

type Doc = {
	id: string;
	documentDate?: string | null;
	sender?: { displayName: string } | null;
	receivers?: { displayName: string }[];
};

const doc = (overrides: Partial<Doc> & { id: string }): Doc => ({
	documentDate: null,
	sender: null,
	receivers: [],
	...overrides
});

// ─── DATE sort ───────────────────────────────────────────────────────────────

describe('groupDocuments — DATE sort', () => {
	it('produces one group per distinct year', () => {
		const docs = [
			doc({ id: 'a', documentDate: '1923-04-12' }),
			doc({ id: 'b', documentDate: '1938-01-01' }),
			doc({ id: 'c', documentDate: '1965-08-03' })
		];
		const groups = groupDocuments(docs, 'DATE', 'Undatiert');
		expect(groups.map((g) => g.label)).toEqual(['1923', '1938', '1965']);
		expect(groups.every((g) => g.documents.length === 1)).toBe(true);
	});

	it('puts multiple docs from the same year into one group', () => {
		const docs = [
			doc({ id: 'a', documentDate: '1938-03-01' }),
			doc({ id: 'b', documentDate: '1938-11-15' })
		];
		const groups = groupDocuments(docs, 'DATE', 'Undatiert');
		expect(groups).toHaveLength(1);
		expect(groups[0].label).toBe('1938');
		expect(groups[0].documents).toHaveLength(2);
	});

	it('places undated docs in the fallback group at the bottom', () => {
		const docs = [
			doc({ id: 'a', documentDate: '1938-01-01' }),
			doc({ id: 'b', documentDate: null }),
			doc({ id: 'c', documentDate: null })
		];
		const groups = groupDocuments(docs, 'DATE', 'Undatiert');
		expect(groups).toHaveLength(2);
		expect(groups[0].label).toBe('1938');
		expect(groups[1].label).toBe('Undatiert');
		expect(groups[1].documents.map((d) => d.id)).toEqual(['b', 'c']);
	});

	it('returns one group with fallback label when all docs are undated', () => {
		const docs = [doc({ id: 'a' }), doc({ id: 'b' })];
		const groups = groupDocuments(docs, 'DATE', 'Undatiert');
		expect(groups).toHaveLength(1);
		expect(groups[0].label).toBe('Undatiert');
	});

	it('returns one group when all docs are from the same year', () => {
		const docs = [
			doc({ id: 'a', documentDate: '1938-01-01' }),
			doc({ id: 'b', documentDate: '1938-06-15' })
		];
		const groups = groupDocuments(docs, 'DATE', 'Undatiert');
		expect(groups).toHaveLength(1);
	});
});

// ─── SENDER sort ─────────────────────────────────────────────────────────────

describe('groupDocuments — SENDER sort', () => {
	it('produces one group per distinct sender', () => {
		const docs = [
			doc({ id: 'a', sender: { displayName: 'Anna Müller' } }),
			doc({ id: 'b', sender: { displayName: 'Karl Bauer' } }),
			doc({ id: 'c', sender: { displayName: 'Anna Müller' } })
		];
		const groups = groupDocuments(docs, 'SENDER', 'Unbekannt');
		expect(groups.map((g) => g.label)).toEqual(['Anna Müller', 'Karl Bauer']);
		expect(groups[0].documents).toHaveLength(2);
		expect(groups[1].documents).toHaveLength(1);
	});

	it('places docs with no sender in the fallback group at the bottom', () => {
		const docs = [
			doc({ id: 'a', sender: { displayName: 'Anna Müller' } }),
			doc({ id: 'b', sender: null })
		];
		const groups = groupDocuments(docs, 'SENDER', 'Unbekannt');
		expect(groups).toHaveLength(2);
		expect(groups[0].label).toBe('Anna Müller');
		expect(groups[1].label).toBe('Unbekannt');
		expect(groups[1].documents[0].id).toBe('b');
	});
});

// ─── RECEIVER sort ───────────────────────────────────────────────────────────

describe('groupDocuments — RECEIVER sort', () => {
	it('a doc with two receivers appears in both receiver groups', () => {
		const docs = [
			doc({
				id: 'a',
				receivers: [{ displayName: 'Anna' }, { displayName: 'Karl' }]
			})
		];
		const groups = groupDocuments(docs, 'RECEIVER', 'Unbekannt');
		expect(groups.map((g) => g.label)).toEqual(['Anna', 'Karl']);
		expect(groups[0].documents[0].id).toBe('a');
		expect(groups[1].documents[0].id).toBe('a');
	});

	it('places docs with no receivers in the fallback group at the bottom', () => {
		const docs = [
			doc({ id: 'a', receivers: [{ displayName: 'Anna' }] }),
			doc({ id: 'b', receivers: [] })
		];
		const groups = groupDocuments(docs, 'RECEIVER', 'Unbekannt');
		expect(groups).toHaveLength(2);
		expect(groups[0].label).toBe('Anna');
		expect(groups[1].label).toBe('Unbekannt');
		expect(groups[1].documents[0].id).toBe('b');
	});

	it('composite keys are unique: groupLabel + doc.id identifies each item', () => {
		const docs = [
			doc({ id: 'a', receivers: [{ displayName: 'Anna' }, { displayName: 'Karl' }] }),
			doc({ id: 'b', receivers: [{ displayName: 'Anna' }] })
		];
		const groups = groupDocuments(docs, 'RECEIVER', 'Unbekannt');
		const keys = groups.flatMap((g) => g.documents.map((d) => `${g.label}-${d.id}`));
		const uniqueKeys = new Set(keys);
		expect(uniqueKeys.size).toBe(keys.length);
	});
});

// ─── Non-groupable sorts ──────────────────────────────────────────────────────

describe('groupDocuments — non-groupable sorts', () => {
	it('TITLE sort returns one group containing all documents', () => {
		const docs = [doc({ id: 'a' }), doc({ id: 'b' })];
		const groups = groupDocuments(docs, 'TITLE', 'Undatiert');
		expect(groups).toHaveLength(1);
		expect(groups[0].documents).toHaveLength(2);
	});

	it('UPLOAD_DATE sort returns one group containing all documents', () => {
		const docs = [doc({ id: 'a' }), doc({ id: 'b' })];
		const groups = groupDocuments(docs, 'UPLOAD_DATE', 'Undatiert');
		expect(groups).toHaveLength(1);
		expect(groups[0].documents).toHaveLength(2);
	});
});

// ─── Edge cases ──────────────────────────────────────────────────────────────

describe('groupDocuments — edge cases', () => {
	it('returns empty array for an empty document list', () => {
		expect(groupDocuments([], 'DATE', 'Undatiert')).toEqual([]);
	});
});
