<!-- #region TranslationRunResult [C:3] [TYPE Component] [SEMANTICS translate, result, stats, retry, sql-audit] -->
<!-- @BRIEF Component component: lib/components/translate/TranslationRunResult.svelte -->
<!-- @RELATION DEPENDS_ON -> [TranslateApi] -->
<!-- @RELATION DEPENDS_ON -> [EXT:frontend:api_module] -->
<!-- @UX_STATE completed -> All phases completed, full stats displayed -->
<!-- @UX_STATE partial -> Translation completed with failures, retry available -->
<!-- @UX_STATE failed -> Translation failed, retry available -->
<!-- @UX_STATE insert_failed -> Insert phase failed, retry-insert available -->
<!-- @UX_FEEDBACK Tabular statistics; click to copy Superset query ID; collapsible SQL block. -->
<!-- @UX_RECOVERY Retry failed batches; retry insert. -->
<script lang="ts">
	import { t } from '$lib/i18n/index.svelte.js';
	import { addToast } from '$lib/toasts.svelte.js';
	import {
		fetchRunStatus,
		fetchRunRecords,
		retryFailedBatches,
		retryInsert,
		fetchRunBatches,
		downloadFailedCsv
	} from '$lib/api/translate.js';
	import CorrectionCell from './CorrectionCell.svelte';
	import TermCorrectionPopup from './TermCorrectionPopup.svelte';
	import BulkReplaceModal from './BulkReplaceModal.svelte';

	/** @type {{ runId: string, onRefresh: Function }} */
	let { runId, onRefresh = () => {} } = $props();

	/**
	 * @type {'completed'|'partial'|'failed'|'insert_failed'}
	 */
	let uxState = $state('completed');
	let status = $state(null);
	let records = $state([]);
	let batches = $state([]);
	let showRecords = $state(false);
	let showSqlAudit = $state(false);
	let isRetrying = $state(false);
	let isRetryingInsert = $state(false);
	let sqlAuditLines = $state([]);
	let targetLanguages = $state([]);
	let correctionPopupData = $state(null);
	let showBulkReplace = $state(false);
	let loadedRunId = $state(null);

	function getRunStatusLabel(value) {
		const labels = {
			COMPLETED: $t.translate?.run?.completed || 'Completed',
			FAILED: $t.translate?.run?.translation_failed || 'Failed',
			RUNNING: $t.translate?.history?.status_running || 'Running',
			PENDING: $t.translate?.history?.status_pending || 'Pending',
			CANCELLED: $t.translate?.run?.cancelled || 'Cancelled',
		};
		return labels[value] || value;
	}

	// Derived
	let insertStatusBadge = $derived.by(() => {
		const s = status?.insert_status;
		if (!s || s === 'success') return { label: $t.translate?.run?.success, class: 'bg-green-100 text-green-700' };
		if (s === 'failed' || s === 'timeout') return { label: $t.translate?.run?.failed, class: 'bg-red-100 text-red-700' };
		return { label: s, class: 'bg-yellow-100 text-yellow-700' };
	});

	$effect(() => {
		if (!runId || loadedRunId === runId) return;
		loadedRunId = runId;
		loadData();
	});

	/** @returns {Promise<void>} */
	async function loadData() {
		try {
			const [statusData, recordsData, batchesData] = await Promise.all([
				fetchRunStatus(runId),
				fetchRunRecords(runId, { page_size: 10 }),
				fetchRunBatches(runId),
			]);
			status = statusData;
			records = recordsData?.items || [];
			batches = Array.isArray(batchesData) ? batchesData : [];

			// Extract target languages from language_stats or records
			if (statusData.language_stats && statusData.language_stats.length > 0) {
				targetLanguages = statusData.language_stats.map(l => l.language_code);
			} else {
				// Extract unique language codes from records
				const langSet = new Set();
				for (const rec of records) {
					if (rec.languages) {
						for (const lang of rec.languages) {
							langSet.add(lang.language_code);
						}
					}
				}
				targetLanguages = Array.from(langSet);
			}

			// Determine state
			const s = statusData.status;
			const insertS = statusData.insert_status;
			if (s === 'COMPLETED' && insertS === 'failed') {
				uxState = 'insert_failed';
			} else if (s === 'COMPLETED' && (statusData.failed_records || 0) > 0) {
				uxState = 'partial';
			} else if (s === 'FAILED') {
				uxState = 'failed';
			} else {
				uxState = 'completed';
			}
		} catch (err) {
			addToast(err?.message || $t.translate?.run?.load_failed, 'error');
		}
	}

	/** @returns {Promise<void>} */
	async function handleRetry() {
		isRetrying = true;
		try {
			await retryFailedBatches(runId);
			addToast($t.translate?.run?.retry_success, 'success');
			await loadData();
			onRefresh();
		} catch (err) {
			addToast(err?.message || $t.translate?.run?.retry_failed_msg, 'error');
		} finally {
			isRetrying = false;
		}
	}

	/** @returns {Promise<void>} */
	async function handleRetryInsert() {
		isRetryingInsert = true;
		try {
			await retryInsert(runId);
			addToast($t.translate?.run?.insert_retry_success, 'success');
			await loadData();
			onRefresh();
		} catch (err) {
			addToast(err?.message || $t.translate?.run?.insert_retry_failed_msg, 'error');
		} finally {
			isRetryingInsert = false;
		}
	}

	/** @param {string} text */
	function copyToClipboard(text) {
		navigator.clipboard.writeText(text).then(() => {
			addToast($t.translate?.run?.copy_success, 'success');
		}).catch(() => {
			addToast($t.translate?.run?.copy_failed, 'error');
		});
	}

	/** @returns {Promise<void>} */
	async function toggleSqlAudit() {
		showSqlAudit = !showSqlAudit;
		if (showSqlAudit && sqlAuditLines.length === 0) {
			// Load all records with successful translation for SQL audit
			try {
				const allRecords = await fetchRunRecords(runId, { page_size: 500, status: 'SUCCESS' });
				const items = allRecords?.items || [];
				sqlAuditLines = items.slice(0, 20).map(r => r.target_sql).filter(Boolean);
			} catch (err) {
				sqlAuditLines = [$t.translate?.run?.no_sql_audit];
			}
		}
	}
</script>

<div class="translation-run-result">
	{#if !status}
		<div class="bg-gray-50 border border-gray-200 rounded-lg p-4 text-center">
			<p class="text-sm text-gray-500">{$t.translate?.run?.loading_result}</p>
		</div>
	{:else}
		<div class="bg-white border border-gray-200 rounded-lg p-4 space-y-4">
			<!-- Header -->
			<div class="flex items-center justify-between">
				<h3 class="text-lg font-semibold text-gray-900">{$t.translate?.run?.result_title}</h3>
				<div class="flex items-center gap-2">
					<!-- Download CSV for failed/insert_failed runs with successful records -->
					{#if (uxState === 'insert_failed' || uxState === 'failed' || uxState === 'partial') && (status.successful_records || 0) > 0}
						<button
							onclick={async () => {
								try { await downloadFailedCsv(runId); }
								catch (e) { addToast(e?.message || 'Download failed', 'error'); }
							}}
							class="px-3 py-1.5 text-xs bg-blue-100 text-blue-700 rounded hover:bg-blue-200 transition-colors"
						>
							{$t.translate?.run?.download_csv || 'CSV'}
						</button>
					{/if}
					<!-- Retry buttons -->
					{#if uxState === 'failed' || uxState === 'partial'}
						<button
							onclick={handleRetry}
							disabled={isRetrying}
							class="px-3 py-1.5 text-xs bg-primary text-white rounded hover:bg-primary-hover disabled:opacity-50 transition-colors"
						>
							{isRetrying ? $t.translate?.run?.retrying : $t.translate?.run?.retry_failed}
						</button>
					{/if}
					{#if uxState === 'insert_failed'}
						<button
							onclick={handleRetryInsert}
							disabled={isRetryingInsert}
							class="px-3 py-1.5 text-xs bg-orange-600 text-white rounded hover:bg-orange-700 disabled:opacity-50 transition-colors"
						>
							{isRetryingInsert ? $t.translate?.run?.retrying_insert : $t.translate?.run?.retry_insert}
						</button>
					{/if}
				</div>
			</div>

			<!-- Statistics Grid -->
			<div class="grid grid-cols-2 sm:grid-cols-4 gap-4">
				<div class="bg-gray-50 rounded-lg p-3">
					<p class="text-xs text-gray-500 uppercase">{$t.translate?.run?.total_records}</p>
					<p class="text-xl font-bold text-gray-900">{status.total_records || 0}</p>
				</div>
				<div class="bg-green-50 rounded-lg p-3">
					<p class="text-xs text-green-600 uppercase">{$t.translate?.run?.success}</p>
					<p class="text-xl font-bold text-green-700">{status.successful_records || 0}</p>
				</div>
				<div class="bg-red-50 rounded-lg p-3">
					<p class="text-xs text-red-600 uppercase">{$t.translate?.run?.failed}</p>
					<p class="text-xl font-bold text-red-700">{status.failed_records || 0}</p>
				</div>
				<div class="bg-yellow-50 rounded-lg p-3">
					<p class="text-xs text-yellow-600 uppercase">{$t.translate?.run?.skipped}</p>
					<p class="text-xl font-bold text-yellow-700">{status.skipped_records || 0}</p>
				</div>
			</div>

			<!-- Per-language Statistics -->
			{#if status.language_stats && status.language_stats.length > 0}
				<div class="border-t border-gray-200 pt-3">
					<p class="text-xs text-gray-500 uppercase mb-2">{$t.translate?.run?.per_language ?? 'Per-Language Statistics'}</p>
					<div class="flex flex-wrap gap-3">
						{#each status.language_stats as lang}
							<div class="px-3 py-2 bg-gray-50 rounded border border-gray-200 min-w-[140px]">
								<p class="text-sm font-semibold text-gray-800">{lang.language_code}</p>
								<div class="flex gap-3 mt-1 text-xs">
									<span class="text-green-600" title="Translated">{lang.translated_rows}</span>
									{#if lang.failed_rows > 0}
										<span class="text-red-600" title="Failed">{lang.failed_rows}</span>
									{/if}
									{#if lang.skipped_rows > 0}
										<span class="text-yellow-600" title="Skipped">{lang.skipped_rows}</span>
									{/if}
									<span class="text-gray-400" title="Total">{lang.total_rows}</span>
								</div>
							</div>
						{/each}
					</div>
				</div>
			{/if}

			<!-- Status Details -->
			<div class="border-t border-gray-200 pt-3">
				<div class="grid grid-cols-2 gap-4 text-sm">
					<div>
						<span class="text-gray-500">{$t.translate?.run?.run_id}</span>
						<span class="ml-1 font-mono text-xs text-gray-700">{status.id}</span>
						<button
							onclick={() => copyToClipboard(status.id)}
							class="ml-1 text-blue-500 hover:text-blue-700 text-xs"
							title={$t.translate?.run?.copy_id}
						>
							{$t.translate?.run?.copy_id}
						</button>
					</div>
					<div>
						<span class="text-gray-500">{$t.translate?.run?.status_label || 'Статус'}</span>
						<span class="ml-1 text-xs font-medium text-gray-700">{getRunStatusLabel(status.status)}</span>
					</div>
					<div>
						<span class="text-gray-500">{$t.translate?.run?.insert_status}</span>
						<span class="ml-1 inline-flex px-2 py-0.5 text-xs rounded-full font-medium {insertStatusBadge.class}">
							{insertStatusBadge.label}
						</span>
					</div>
					{#if status.superset_execution_id}
						<div>
							<span class="text-gray-500">{$t.translate?.run?.superset_query}</span>
							<span class="ml-1 font-mono text-xs text-gray-700">{status.superset_execution_id}</span>
							<button
								onclick={() => copyToClipboard(status.superset_execution_id)}
								class="ml-1 text-blue-500 hover:text-blue-700 text-xs"
								title={$t.translate?.run?.copy_id}
							>
								{$t.translate?.run?.copy_id}
							</button>
						</div>
					{/if}
					{#if status.error_message}
						<div class="col-span-2">
							<span class="text-red-600 text-xs">{$t.translate?.run?.error_label?.replace('{error}', status.error_message)}</span>
						</div>
					{/if}
				</div>
			</div>

			<!-- SQL Audit Block (collapsed) -->
			<div class="border-t border-gray-200 pt-3">
				<button
					onclick={toggleSqlAudit}
					class="flex items-center gap-2 text-sm text-gray-700 hover:text-gray-900"
				>
					<svg
						class="w-4 h-4 transition-transform {showSqlAudit ? 'rotate-90' : ''}"
						fill="none" stroke="currentColor" viewBox="0 0 24 24"
					>
						<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
					</svg>
					{$t.translate?.run?.sql_audit?.replace('{count}', sqlAuditLines.length)}
				</button>

				{#if showSqlAudit}
					<div class="mt-2 bg-gray-900 rounded-lg p-3 max-h-60 overflow-y-auto">
						{#if sqlAuditLines.length === 0}
							<p class="text-xs text-gray-400">{$t.translate?.run?.no_sql_audit}</p>
						{:else}
							{#each sqlAuditLines as line, idx}
								<pre class="text-xs text-green-400 mb-1 font-mono whitespace-pre-wrap break-all">
									<code>{idx + 1}. {line}</code>
								</pre>
							{/each}
						{/if}
					</div>
				{/if}
			</div>

			<!-- Event Invariants -->
			{#if status.event_invariants}
				<div class="border-t border-gray-200 pt-3">
					<div class="flex items-center gap-2 text-xs text-gray-500">
						<span>{$t.translate?.run?.event_invariants}</span>
						<span class="{status.event_invariants.invariant_valid ? 'text-green-600' : 'text-red-600'}">
							{status.event_invariants.invariant_valid ? $t.translate?.run?.valid : $t.translate?.run?.violated}
						</span>
						<span class="text-gray-400">|</span>
						<span>{$t.translate?.run?.superset_query_id_label} {status.event_invariants.has_run_started ? $t.translate?.run?.started_yes : $t.translate?.run?.started_no}</span>
						<span class="text-gray-400">|</span>
						<span>{$t.translate?.run?.terminal_events?.replace('{count}', status.event_invariants.terminal_event_count)}</span>
					</div>
				</div>
			{/if}

			<!-- Records Table with Per-Language Correction Cells -->
			{#if records.length > 0}
				<div class="border-t border-gray-200 pt-3">
					<div class="flex items-center justify-between mb-2">
						<button
							onclick={() => showRecords = !showRecords}
							class="flex items-center gap-2 text-sm text-gray-700 hover:text-gray-900"
						>
							<svg
								class="w-4 h-4 transition-transform {showRecords ? 'rotate-90' : ''}"
								fill="none" stroke="currentColor" viewBox="0 0 24 24"
							>
								<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
							</svg>
							{$t.translate?.run?.records_title || 'Translation Records'} ({records.length})
						</button>
					</div>

					{#if showRecords}
						<div class="overflow-x-auto border border-gray-200 rounded-lg">
							<table class="w-full text-sm">
								<thead class="bg-gray-50">
									<tr>
										<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase w-12">#</th>
										<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase min-w-[150px]">{$t.translate?.preview?.table_source || 'Source'}</th>
										{#each targetLanguages as lang}
											<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase min-w-[180px]">
												<div class="flex items-center gap-1">
													<span class="font-mono text-primary">{lang}</span>
												</div>
											</th>
										{/each}
										{#if targetLanguages.length === 0}
											<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase min-w-[180px]">{$t.translate?.preview?.translation || 'Translation'}</th>
										{/if}
										<th class="px-3 py-2 text-center text-xs font-medium text-gray-500 uppercase w-20">{$t.translate?.preview?.table_status || 'Status'}</th>
										<th class="px-3 py-2 text-center text-xs font-medium text-gray-500 uppercase w-24">{$t.translate?.preview?.table_actions || 'Actions'}</th>
									</tr>
								</thead>
								<tbody class="divide-y divide-gray-200">
									{#each records as row, idx}
										<tr class="{row.status === 'FAILED' ? 'bg-red-50' : idx % 2 === 0 ? 'bg-white' : 'bg-gray-50/50'}">
											<td class="px-3 py-2 text-xs text-gray-400 align-top">{idx + 1}</td>
											<td class="px-3 py-2 align-top max-w-[200px]">
												<code class="text-xs text-gray-800 break-all">{row.source_sql || row.source_object_name || '—'}</code>
											</td>
											{#if targetLanguages.length > 0}
												{#each targetLanguages as lang}
													{@const langData = (row.languages || []).find(l => l.language_code === lang)}
													{@const cellValue = langData?.final_value || langData?.translated_value || ''}
													{@const detectedSource = langData?.source_language_detected || row.source_language_detected || ''}
													<td class="px-3 py-2 align-top">
														<CorrectionCell
															value={cellValue}
															recordId={row.id}
															languageCode={lang}
															runId={runId}
															sourceLanguage={detectedSource}
														/>
													</td>
												{/each}
											{:else}
												<td class="px-3 py-2 align-top">
													<CorrectionCell
														value={row.final_value || row.llm_translation || row.target_sql || ''}
														recordId={row.id}
														languageCode=""
														runId={runId}
														sourceLanguage=""
													/>
												</td>
											{/if}
											<td class="px-3 py-2 text-center align-top">
												<span class="inline-flex px-2 py-0.5 text-xs rounded-full font-medium
													{row.status === 'SUCCESS' ? 'bg-green-100 text-green-700' : ''}
													{row.status === 'FAILED' ? 'bg-red-100 text-red-700' : ''}
													{row.status === 'SKIPPED' ? 'bg-yellow-100 text-yellow-700' : ''}
													{!['SUCCESS','FAILED','SKIPPED'].includes(row.status) ? 'bg-gray-100 text-gray-600' : ''}
												">
													{row.status}
												</span>
											</td>
											<td class="px-3 py-2 text-center align-top">
												<button
													onclick={() => {
														const firstLang = row.languages?.[0] || {};
														correctionPopupData = {
															sourceTerm: row.source_sql || row.source_object_name || '',
															incorrectTarget: firstLang?.final_value || firstLang?.translated_value || '',
															sourceLanguage: firstLang?.source_language_detected || row.source_language_detected || '',
															targetLanguage: targetLanguages[0] || '',
															runId,
															rowKey: row.id
														};
													}}
													class="px-2 py-0.5 text-[10px] bg-primary text-white rounded hover:bg-primary-hover transition-colors"
													title="Submit Correction"
												>
													{$t.translate?.run?.correct || 'Correct'}
												</button>
											</td>
										</tr>
									{/each}
								</tbody>
							</table>
						</div>
					{/if}
				</div>
			{/if}
		</div>
	{/if}
</div>

<!-- Integration: Orphaned Components -->
<TermCorrectionPopup
	{...(correctionPopupData || {})}
	onClose={() => correctionPopupData = null}
	onSubmitted={() => { correctionPopupData = null; loadData(); onRefresh(); }}
/>

<BulkReplaceModal
	show={showBulkReplace}
	{runId}
	{targetLanguages}
	onClose={() => showBulkReplace = false}
	onApplied={(count) => { showBulkReplace = false; loadData(); onRefresh(); }}
/>
<!-- #endregion TranslationRunResult -->
