import type { PersonMention, TranscriptionBlockData } from '$lib/shared/types';

/**
 * Sentinel thrown by saveBlockWithConflictRetry after a 409 rename-mid-edit
 * has been merged into local state. Surfaces to the autosave hook as an
 * error (so the UI shows the retry indicator), but distinguishable from a
 * genuine network failure via the code. Carries the merged block snapshot
 * on its `merged` property so the caller can update local state without
 * a second roundtrip.
 */
export class BlockConflictResolvedError extends Error {
	readonly code = 'CONFLICT_RESOLVED' as const;
	merged?: TranscriptionBlockData;
	constructor(blockId: string) {
		super(
			`Block ${blockId} was rebased onto the latest server snapshot — retry to save the merged result`
		);
		this.name = 'BlockConflictResolvedError';
	}
}

type MergeArgs = {
	serverBlock: TranscriptionBlockData;
	localText: string;
	localMentions: PersonMention[];
};

/**
 * Resolves a 409-Conflict from the server by combining the latest server
 * snapshot with the transcriber's unsaved local edits (B12b).
 *
 * Rules:
 * - The transcriber's typed text always wins — never overwrite their input.
 * - Server is the source of truth for the displayName of any person it
 *   knows about; renames that just landed on the server replace stale local
 *   names by personId.
 * - Local-only mentions added since the last save are preserved.
 * - All non-mention fields (version, sortOrder, reviewed, updatedAt, ...)
 *   come from the server snapshot so the next save sends the current
 *   revision and matches the latest persisted state.
 */
export function mergeBlockOnConflict(args: MergeArgs): TranscriptionBlockData {
	const serverIds = new Set(args.serverBlock.mentionedPersons.map((m) => m.personId));
	const localOnly = args.localMentions.filter((m) => !serverIds.has(m.personId));
	return {
		...args.serverBlock,
		text: args.localText,
		mentionedPersons: [...args.serverBlock.mentionedPersons, ...localOnly]
	};
}
