<!-- #region BulkReplaceModal [C:3] [TYPE Component] [SEMANTICS translate, bulk, replace, find-replace, regex] -->
<!-- @ingroup Translate -->
<!-- @BRIEF Modal dialog for bulk find-and-replace on translated values within a completed run. -->
<!-- @LAYER UI -->
<!-- @RELATION DEPENDS_ON -> [TranslateApi] -->
<!--
@UX_STATE closed -> Modal closed, no interaction
@UX_STATE configuring -> User entering find/replace pattern, toggling regex, selecting language
@UX_STATE previewing -> Preview table showing affected rows before/after
@UX_STATE preview_error -> Failed to load preview
@UX_STATE confirming -> "Apply X changes?" confirmation dialog
@UX_STATE applying -> API apply in progress
@UX_STATE applied -> Success state with count
@UX_STATE apply_error -> Apply failed with error message
-->
<script lang="ts">
	import { addToast } from '$lib/toasts.svelte.js';
	import { bulkFindReplace, bulkReplacePreview, dictionaryApi } from '$lib/api/translate.js';
	import { _, getT } from '$lib/i18n/index.svelte.js';

	/** @type {{ show: boolean, runId: string, targetLanguages: string[], onClose: () => void, onApplied?: (count: number) => void }} */
	let {
		show = false,
		runId = '',
		targetLanguages = [],
		onClose = () => {},
		onApplied = () => {}
	} = $props();

	/** @type {'closed'|'configuring'|'previewing'|'preview_error'|'confirming'|'applying'|'applied'|'apply_error'} */
	let uxState = $state('closed');
  // @RATIONALE Svelte 5 store detection fails for `$t` in deeply nested templates.
  //             Use getT() + $derived to keep reactivity to locale changes.
  const _t = $derived(getT());

	let findPattern = $state('');
	let replacementText = $state('');
	let isRegex = $state(false);
	let targetLanguage = $state('');
	let affectedRecords = $state([]);
	let applyCount = $state(0);
	let errorMessage = $state('');
	let submitToDict = $state(false);
	let usageNotes = $state('');
	let dictionaries = $state([]);
	let selectedDictId = $state('');

	async function loadDictionaries() {
		try {
			const result = await dictionaryApi.fetchDictionaries({ page_size: 100 });
			dictionaries = Array.isArray(result) ? result : (result?.items || []);
			if (dictionaries.length === 1) {
				selectedDictId = dictionaries[0].id;
			}
		} catch {
			addToast(_('translate.bulk_replace.dict_load_failed'), 'error');
		}
	}

	$effect(() => {
		if (show) {
			uxState = 'configuring';
			findPattern = '';
			replacementText = '';
			isRegex = false;
			targetLanguage = '';
			affectedRecords = [];
			applyCount = 0;
			errorMessage = '';
			submitToDict = false;
			usageNotes = '';
			dictionaries = [];
			selectedDictId = '';
		} else {
			uxState = 'closed';
		}
	});

	/** Preview affected records */
	async function handlePreview() {
		if (!findPattern.trim()) {
			addToast(_('translate.bulk_replace.find_pattern_required'), 'warning');
			return;
		}
		if (!targetLanguage) {
			addToast(_('translate.bulk_replace.target_language_required'), 'warning');
			return;
		}
		uxState = 'previewing';
		errorMessage = '';
		try {
			const result = await bulkReplacePreview(runId, {
				find_pattern: findPattern,
				is_regex: isRegex,
				replacement_text: replacementText,
				target_language: targetLanguage
			});
			affectedRecords = result?.preview || [];
			applyCount = affectedRecords.length;
			uxState = 'previewing';
		} catch (err) {
			errorMessage = err?.message || _('translate.bulk_replace.preview_failed');
			uxState = 'preview_error';
			addToast(errorMessage, 'error');
		}
	}

	/** Open confirmation */
	function handleShowConfirm() {
		uxState = 'confirming';
		loadDictionaries();
	}

	/** Cancel confirmation, back to preview */
	function handleCancelConfirm() {
		uxState = 'previewing';
	}

	/** Apply the bulk replacement */
	async function handleApply() {
		uxState = 'applying';
		try {
			const result = await bulkFindReplace(runId, {
				find_pattern: findPattern,
				is_regex: isRegex,
				replacement_text: replacementText,
				target_language: targetLanguage,
				submit_to_dictionary: submitToDict,
				dictionary_id: submitToDict ? selectedDictId : undefined,
				usage_notes: usageNotes || undefined,
				submit_to_dictionary_with_context: submitToDict
			});
			const changed = result?.rows_affected || applyCount;
			uxState = 'applied';
			onApplied(changed);
			addToast(_('translate.bulk_replace.applied_toast').replace('{count}', changed), 'success');
		} catch (err) {
			errorMessage = err?.message || _('translate.bulk_replace.apply_failed');
			uxState = 'apply_error';
			addToast(errorMessage, 'error');
		}
	}

	function handleClose() {
		onClose();
	}

	function previewAfter(row) {
		if (!replacementText && !isRegex) return '';
		if (isRegex) {
			try {
				const re = new RegExp(findPattern, 'g');
				return (row.final_value || row.translated_value || '').replace(re, replacementText);
			} catch {
				return row.final_value || row.translated_value || '';
			}
		}
		return (row.final_value || row.translated_value || '').split(findPattern).join(replacementText);
	}
</script>

{#if show}
	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div
		class="fixed inset-0 z-50 flex items-center justify-center bg-black/40"
		onclick={handleClose}
		onkeydown={(e) => e.key === 'Escape' && handleClose()}
		role="presentation"
	>
		<div
			class="bg-surface-card rounded-xl shadow-2xl w-full max-w-2xl mx-4 max-h-[85vh] flex flex-col"
			onclick={(e) => e.stopPropagation()}
			onkeydown={(e) => e.key === 'Escape' && handleClose()}
			role="dialog"
			aria-modal="true"
			aria-label={_t.translate?.bulk_replace?.title}
			tabindex="-1"
		>
			<!-- Header -->
			<div class="flex items-center justify-between px-6 py-4 border-b border-border flex-shrink-0">
				<h2 class="text-lg font-semibold text-text">{_t.translate?.bulk_replace?.title}</h2>
				<button
					onclick={handleClose}
					class="p-1 text-text-subtle hover:text-text-muted rounded-lg hover:bg-surface-muted transition-colors"
					aria-label={_t.translate?.bulk_replace?.close}
				>
					<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
					</svg>
				</button>
			</div>

			<!-- Body (scrollable) -->
			<div class="px-6 py-4 overflow-y-auto flex-1 space-y-4">

				<!-- Configuring State -->
				{#if uxState === 'configuring' || uxState === 'previewing' || uxState === 'preview_error'}
					<!-- Find Pattern -->
					<div>
						<label for="bulk-find-pattern" class="block text-sm font-medium text-text mb-1">{_t.translate?.bulk_replace?.find_pattern}</label>
						<div class="flex gap-2">
							<input
								type="text"
								id="bulk-find-pattern"
								bind:value={findPattern}
								placeholder={_t.translate?.bulk_replace?.find_placeholder}
								class="flex-1 px-3 py-2 border border-border-strong rounded-lg text-sm focus-visible:ring-2 focus-visible:ring-primary-ring focus-visible:border-primary-ring"
								onkeydown={(e) => e.key === 'Enter' && handlePreview()}
							/>
							<label class="flex items-center gap-1.5 px-3 py-2 border border-border-strong rounded-lg text-sm cursor-pointer hover:bg-surface-muted transition-colors">
								<input
									type="checkbox"
									bind:checked={isRegex}
									class="rounded"
								/>
								<span class="text-text-muted font-mono text-xs">.*</span>
								<span class="text-text-muted text-xs">{_t.translate?.bulk_replace?.regex}</span>
							</label>
						</div>
					</div>

					<!-- Replacement Text -->
					<div>
						<label for="bulk-replace-text" class="block text-sm font-medium text-text mb-1">{_t.translate?.bulk_replace?.replace_with}</label>
						<input
							type="text"
							id="bulk-replace-text"
							bind:value={replacementText}
							placeholder={_t.translate?.bulk_replace?.replace_placeholder}
							class="w-full px-3 py-2 border border-border-strong rounded-lg text-sm focus-visible:ring-2 focus-visible:ring-primary-ring focus-visible:border-primary-ring"
							onkeydown={(e) => e.key === 'Enter' && handlePreview()}
						/>
					</div>

					<!-- Target Language -->
					<div>
						<label for="bulk-target-language" class="block text-sm font-medium text-text mb-1">{_t.translate?.bulk_replace?.target_language}</label>
						<select
							id="bulk-target-language"
							bind:value={targetLanguage}
							class="w-full px-3 py-2 border border-border-strong rounded-lg text-sm"
						>
							<option value="">{_t.translate?.bulk_replace?.select_language}</option>
							{#each targetLanguages as lang}
								<option value={lang}>{lang}</option>
							{/each}
						</select>
					</div>

					<!-- Preview button -->
					<button
						onclick={handlePreview}
						disabled={!findPattern.trim() || !targetLanguage}
						class="w-full px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary-hover disabled:opacity-50 transition-colors text-sm font-medium"
					>
						{_t.translate?.bulk_replace?.preview_affected}
					</button>
				{/if}

				<!-- Preview Error -->
				{#if uxState === 'preview_error'}
					<div class="bg-destructive-light border-destructive-light rounded-lg p-3">
						<p class="text-sm text-destructive">{errorMessage}</p>
					</div>
				{/if}

				<!-- Preview Table -->
				{#if uxState === 'previewing' && affectedRecords.length > 0}
					<div>
						<div class="flex items-center justify-between mb-2">
							<h3 class="text-sm font-medium text-text">
								{_t.translate?.bulk_replace?.preview_count.replace('{count}', affectedRecords.length)}
							</h3>
							<button
								onclick={handleShowConfirm}
								class="px-4 py-1.5 bg-warning text-white rounded-lg hover:bg-warning transition-colors text-sm font-medium"
							>
								{_t.translate?.bulk_replace?.apply_changes}
							</button>
						</div>
						<div class="max-h-60 overflow-y-auto border border-border rounded-lg">
							<table class="min-w-full divide-y divide-border text-xs">
								<thead class="bg-surface-muted sticky top-0">
									<tr>
										<th class="px-3 py-2 text-left font-medium text-text-muted uppercase">{_t.translate?.bulk_replace?.table_number}</th>
										<th class="px-3 py-2 text-left font-medium text-text-muted uppercase">{_t.translate?.bulk_replace?.table_before}</th>
										<th class="px-3 py-2 text-left font-medium text-text-muted uppercase">{_t.translate?.bulk_replace?.table_after}</th>
									</tr>
								</thead>
								<tbody class="divide-y divide-border">
									{#each affectedRecords.slice(0, 100) as row, i}
										<tr class="hover:bg-surface-muted">
											<td class="px-3 py-2 text-text-subtle">{i + 1}</td>
											<td class="px-3 py-2 text-text max-w-xs truncate">{row.current_value || row.final_value || row.translated_value || ''}</td>
											<td class="px-3 py-2 text-success font-medium max-w-xs truncate">{row.new_value || ''}</td>
										</tr>
									{/each}
								</tbody>
							</table>
							{#if affectedRecords.length > 100}
								<div class="px-3 py-2 text-xs text-text-subtle bg-surface-muted border-t border-border">
									{_t.translate?.bulk_replace?.showing_first.replace('{shown}', 100).replace('{total}', affectedRecords.length)}
								</div>
							{/if}
						</div>
					</div>
				{/if}

				{#if uxState === 'previewing' && affectedRecords.length === 0}
					<div class="bg-surface-muted border border-border rounded-lg p-6 text-center">
						<p class="text-sm text-text-muted">{_t.translate?.bulk_replace?.no_matching}</p>
						<p class="text-xs text-text-subtle mt-1">{_t.translate?.bulk_replace?.no_matching_hint}</p>
					</div>
				{/if}

				<!-- Confirmation State -->
				{#if uxState === 'confirming'}
					<div class="bg-warning-light border border-warning rounded-lg p-4">
						<div class="flex items-start gap-3">
							<svg class="w-5 h-5 text-warning flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
								<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
							</svg>
							<div class="flex-1">
								<p class="text-sm font-medium text-warning">
									{_t.translate?.bulk_replace?.confirm_title.replace('{count}', applyCount)}
								</p>
								<p class="text-xs text-warning mt-1">
									{_t.translate?.bulk_replace?.confirm_body.replace('{language}', targetLanguage)}
								</p>

								<!-- Submit to Dictionary checkbox -->
								<div class="mt-3 space-y-2">
									<label class="flex items-center gap-2 text-sm text-text cursor-pointer">
										<input type="checkbox" bind:checked={submitToDict} class="rounded" />
										<span>{_t.translate?.bulk_replace?.submit_to_dict}</span>
									</label>
									{#if submitToDict}
										<div class="space-y-2 pl-6">
											<label class="block text-xs font-medium text-text-muted" for="bulk-dict-select">{_t.translate?.bulk_replace?.dictionary || 'Словарь'}</label>
											<select
												id="bulk-dict-select"
												bind:value={selectedDictId}
												class="w-full px-3 py-2 border border-border-strong rounded-lg text-sm"
											>
												<option value="">{_t.translate?.bulk_replace?.select_dictionary || 'Выберите словарь...'}</option>
												{#each dictionaries as dict}
													<option value={dict.id}>{dict.name}</option>
												{/each}
											</select>
											<textarea
												bind:value={usageNotes}
												placeholder={_t.translate?.bulk_replace?.usage_notes_placeholder}
												class="w-full px-3 py-2 border border-border-strong rounded-lg text-sm"
												rows="2"
											></textarea>
										</div>
									{/if}
								</div>

								<div class="flex gap-2 mt-3">
									<button
										onclick={handleCancelConfirm}
										class="px-3 py-1.5 text-sm border border-border-strong text-text rounded-lg hover:bg-surface-card transition-colors"
									>
										{_t.translate?.bulk_replace?.cancel}
									</button>
									<button
										onclick={handleApply}
										class="px-3 py-1.5 text-sm bg-warning text-white rounded-lg hover:bg-warning transition-colors"
									>
										{_t.translate?.bulk_replace?.apply_count.replace('{count}', applyCount)}
									</button>
								</div>
							</div>
						</div>
					</div>
				{/if}

				<!-- Applying State -->
				{#if uxState === 'applying'}
					<div class="flex items-center justify-center gap-3 py-8">
						<div class="animate-spin rounded-full h-6 w-6 border-b-2 border-warning"></div>
						<span class="text-sm text-text-muted">{_t.translate?.bulk_replace?.applying}</span>
					</div>
				{/if}

				<!-- Applied State -->
				{#if uxState === 'applied'}
					<div class="bg-success-light border border-success rounded-lg p-6 text-center">
						<svg class="w-12 h-12 text-success mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
							<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
						</svg>
						<p class="text-sm font-medium text-success">{_t.translate?.bulk_replace?.applied_title}</p>
						<p class="text-xs text-success mt-1">{_t.translate?.bulk_replace?.applied_body.replace('{count}', applyCount)}</p>
						<button
							onclick={handleClose}
							class="mt-4 px-4 py-2 bg-success text-white rounded-lg hover:bg-success transition-colors text-sm"
						>
							{_t.translate?.bulk_replace?.done}
						</button>
					</div>
				{/if}

				<!-- Apply Error State -->
				{#if uxState === 'apply_error'}
					<div class="bg-destructive-light border-destructive-light rounded-lg p-3">
						<p class="text-sm text-destructive mb-2">{errorMessage}</p>
						<div class="flex gap-2">
							<button
								onclick={handleShowConfirm}
								class="px-3 py-1.5 text-sm bg-destructive text-white rounded-lg hover:bg-destructive-hover transition-colors"
							>
								{_t.translate?.bulk_replace?.retry}
							</button>
							<button
								onclick={handleClose}
								class="px-3 py-1.5 text-sm border border-border-strong text-text rounded-lg hover:bg-surface-muted transition-colors"
							>
								{_t.translate?.bulk_replace?.cancel}
							</button>
						</div>
					</div>
				{/if}

			</div>

			<!-- Footer (always visible) -->
			<div class="px-6 py-3 border-t border-border flex justify-end gap-2 flex-shrink-0">
				<button
					onclick={handleClose}
					class="px-4 py-2 text-sm text-text bg-surface-muted rounded-lg hover:bg-surface-muted transition-colors"
				>
					{_t.translate?.bulk_replace?.close}
				</button>
			</div>
		</div>
	</div>
{/if}
<!-- #endregion BulkReplaceModal -->
