import type { PersonMention, TranscriptionBlockData } from '$lib/shared/types';
import {
	BlockConflictResolvedError,
	mergeBlockOnConflict
} from '$lib/document/transcription/blockConflictMerge';

const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

type Args = {
	fetchImpl: typeof fetch;
	documentId: string;
	blockId: string;
	text: string;
	mentionedPersons: PersonMention[];
};

/**
 * Persists a transcription block edit, with built-in handling for the
 * rename-mid-edit conflict (B12b).
 *
 * - 200/204 → resolves with the server's updated block.
 * - 409     → refetches the latest server block, merges it with the
 *             transcriber's unsaved input via mergeBlockOnConflict, and
 *             throws BlockConflictResolvedError carrying the merged
 *             snapshot. The caller is responsible for updating local
 *             state with `err.merged` before surfacing the error.
 * - other   → throws Error('Save failed').
 *
 * Validates both ids against the UUID pattern before any fetch fires
 * (Sina #5505 — defence-in-depth path-injection guard).
 */
export async function saveBlockWithConflictRetry(args: Args): Promise<TranscriptionBlockData> {
	const { fetchImpl, documentId, blockId, text, mentionedPersons } = args;
	if (!UUID_RE.test(documentId) || !UUID_RE.test(blockId)) {
		throw new Error(`Invalid id for save: doc=${documentId} block=${blockId}`);
	}

	const url = `/api/documents/${documentId}/transcription-blocks/${blockId}`;
	const res = await fetchImpl(url, {
		method: 'PUT',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify({ text, mentionedPersons })
	});

	if (res.status === 409) {
		const fresh = await fetchImpl(url);
		if (!fresh.ok) {
			throw new BlockConflictResolvedError(blockId);
		}
		const serverBlock = (await fresh.json()) as TranscriptionBlockData;
		const merged = mergeBlockOnConflict({
			serverBlock,
			localText: text,
			localMentions: mentionedPersons
		});
		const err = new BlockConflictResolvedError(blockId);
		(err as BlockConflictResolvedError & { merged: TranscriptionBlockData }).merged = merged;
		throw err;
	}

	if (!res.ok) throw new Error('Save failed');
	return (await res.json()) as TranscriptionBlockData;
}
