import { describe, it, expect } from 'vitest';
import {
	isDefaultState,
	hiddenLayerCount,
	filterTimeline,
	ALL_LAYERS_ON,
	type TimelineLayerFilters
} from './timelineFilter';
import { makeEntry, makeYear, makeTimelineDTO } from './test-factories';

// Entry factories pinned to the three layers the filter discriminates (#780).
const letter = (overrides = {}) => makeEntry({ kind: 'LETTER', ...overrides });

const curatedPersonal = (overrides = {}) =>
	makeEntry({
		kind: 'EVENT',
		type: 'PERSONAL',
		derived: false,
		documentId: undefined,
		title: 'Umzug nach Berlin',
		senderName: '',
		receiverName: '',
		...overrides
	});

// Derived life-events carry type=PERSONAL (issue #776 REQ-009) — they belong to
// the Personal layer, not a fourth one.
const derivedLifeEvent = (overrides = {}) =>
	makeEntry({
		kind: 'EVENT',
		type: 'PERSONAL',
		derived: true,
		derivedType: 'BIRTH',
		documentId: undefined,
		title: 'Geburt',
		senderName: '',
		receiverName: '',
		...overrides
	});

const historical = (overrides = {}) =>
	makeEntry({
		kind: 'EVENT',
		type: 'HISTORICAL',
		derived: false,
		documentId: undefined,
		title: 'Erster Weltkrieg',
		senderName: '',
		receiverName: '',
		...overrides
	});

const off = (overrides: Partial<TimelineLayerFilters>): TimelineLayerFilters => ({
	...ALL_LAYERS_ON,
	...overrides
});

describe('isDefaultState (REQ-007)', () => {
	it('is true when all three layers are on', () => {
		expect(isDefaultState(ALL_LAYERS_ON)).toBe(true);
	});

	it('is false when any single layer is off', () => {
		expect(isDefaultState(off({ personalOn: false }))).toBe(false);
		expect(isDefaultState(off({ historicalOn: false }))).toBe(false);
		expect(isDefaultState(off({ lettersOn: false }))).toBe(false);
	});
});

describe('hiddenLayerCount (REQ-007)', () => {
	it('is 0 in the default all-on state', () => {
		expect(hiddenLayerCount(ALL_LAYERS_ON)).toBe(0);
	});

	it('counts each layer that is off', () => {
		expect(hiddenLayerCount(off({ lettersOn: false }))).toBe(1);
		expect(hiddenLayerCount(off({ personalOn: false, historicalOn: false }))).toBe(2);
		expect(hiddenLayerCount({ personalOn: false, historicalOn: false, lettersOn: false })).toBe(3);
	});
});

describe('filterTimeline', () => {
	it('returns every entry unchanged in the default all-on state', () => {
		const dto = makeTimelineDTO({
			years: [makeYear(1915, [letter(), historical(), curatedPersonal()])],
			undated: [letter({ documentId: 'u1' })]
		});
		const result = filterTimeline(dto, ALL_LAYERS_ON);
		expect(result.years[0].entries).toHaveLength(3);
		expect(result.undated).toHaveLength(1);
	});

	it('hides LETTER entries when lettersOn is false, keeping events (REQ-005)', () => {
		const dto = makeTimelineDTO({
			years: [makeYear(1915, [letter(), historical(), curatedPersonal()])],
			undated: [letter({ documentId: 'u1' })]
		});
		const result = filterTimeline(dto, off({ lettersOn: false }));
		expect(result.years[0].entries.every((e) => e.kind !== 'LETTER')).toBe(true);
		expect(result.years[0].entries).toHaveLength(2);
		expect(result.undated).toHaveLength(0);
	});

	it('hides HISTORICAL events when historicalOn is false, keeping personal + letters (REQ-004)', () => {
		const dto = makeTimelineDTO({
			years: [makeYear(1915, [letter(), historical(), curatedPersonal(), derivedLifeEvent()])]
		});
		const result = filterTimeline(dto, off({ historicalOn: false }));
		const kept = result.years[0].entries;
		expect(kept.some((e) => e.kind === 'EVENT' && e.type === 'HISTORICAL')).toBe(false);
		expect(kept.some((e) => e.kind === 'LETTER')).toBe(true);
		expect(kept.some((e) => e.kind === 'EVENT' && e.type === 'PERSONAL')).toBe(true);
		expect(kept).toHaveLength(3);
	});

	it('hides personal events — curated and derived — when personalOn is false (REQ-003)', () => {
		const dto = makeTimelineDTO({
			years: [makeYear(1915, [letter(), historical(), curatedPersonal(), derivedLifeEvent()])]
		});
		const result = filterTimeline(dto, off({ personalOn: false }));
		const kept = result.years[0].entries;
		// neither the curated PERSONAL event nor the derived life-event survives
		expect(kept.some((e) => e.kind === 'EVENT' && e.type === 'PERSONAL')).toBe(false);
		expect(kept.some((e) => e.derived)).toBe(false);
		// historical events and letters are untouched
		expect(kept.some((e) => e.kind === 'EVENT' && e.type === 'HISTORICAL')).toBe(true);
		expect(kept.some((e) => e.kind === 'LETTER')).toBe(true);
		expect(kept).toHaveLength(2);
	});

	it('drops year bands that become empty and filters the undated bucket (REQ-006)', () => {
		const dto = makeTimelineDTO({
			years: [
				makeYear(1915, [letter()]), // becomes empty when letters are hidden
				makeYear(1918, [historical()]) // survives
			],
			undated: [letter({ documentId: 'u1' }), historical({ documentId: undefined })]
		});
		const result = filterTimeline(dto, off({ lettersOn: false }));
		expect(result.years).toHaveLength(1);
		expect(result.years[0].year).toBe(1918);
		expect(result.undated.every((e) => e.kind !== 'LETTER')).toBe(true);
		expect(result.undated).toHaveLength(1);
	});

	it('does not mutate the input timeline', () => {
		const dto = makeTimelineDTO({ years: [makeYear(1915, [letter(), historical()])] });
		filterTimeline(dto, off({ lettersOn: false }));
		expect(dto.years[0].entries).toHaveLength(2);
	});
});
