<!-- #region ValidationRunDetailPage [C:4] [TYPE Component] [SEMANTICS validation, run, detail, report, dashboard, page] -->
<!-- @BRIEF Validation run detail report page showing results for ALL dashboards validated in this run — issues, screenshots, dataset health, chart results, and logs grouped by dashboard with collapsible sections. -->
<!-- @RELATION DEPENDS_ON -> [ValidationApi] -->
<!-- @RELATION BINDS_TO -> [I18nModule] -->
<!-- @UX_STATE Loading -> Full-page skeleton -->
<!-- @UX_STATE Error -> Alert with error message + Retry/Back buttons -->
<!-- @UX_STATE NotFound -> "Run not found" state -->
<!-- @UX_STATE Populated -> Run header + collapsible dashboard list with expanded detail sections -->
<!-- @UX_FEEDBACK Screenshot opens in new tab on click -->
<!-- @UX_REACTIVITY Props -> $props(data), LocalState -> $state(...) -->
<!-- @UX_TEST: Populated -> {expand dashboard with screenshots, expected: first screenshot auto-loads, tab badge shows if issues match} -->
<!-- @UX_TEST: Populated -> {click "Task execution logs", expected: task logs table appears with time/level/source/message columns} -->
<!-- @UX_TEST: Populated -> {expand dashboard with issues, expected: tab buttons show red/yellow badge with issue count} -->
<script lang="ts">
	import { goto } from '$app/navigation';
	import { t } from '$lib/i18n/index.svelte.js';
	import { api } from '$lib/api.js';
	import { page } from '$app/state';

	/** @type {{ data: import('./$types').PageData }} */
	let { data } = $props();

	let policyId = $derived(page.params.policyId);
	let runId = $derived(page.params.runId);

	/** @type {'idle'|'loading'|'populated'|'error'|'not_found'} */
	let uxState = $state('populated');
	let run = $state(data.run);
	let errorMsg = $state(null);

	// Expanded dashboards — track set of expanded dashboard IDs
	let expandedDashboards = $state(new Set());

	// Screenshot tabs — track selected tab per dashboard
	/** @type {Record<string, string>} */
	let selectedScreenshotTab = $state({});

	// Screenshot blob URLs — cache loaded screenshot URLs per (dashboardId, tabIndex)
	/** @type {Record<string, string>} */
	let screenshotUrls = $state({});

	// Collapsible sections
	let logsSentExpanded = $state(new Set());
	let taskLogsExpanded = $state(new Set());

	let dashboards = $derived(run?.dashboards || run?.results || []);

	// ── Derived aggregate counts ──
	let passCount = $derived(dashboards.filter(d => d.status === 'PASS').length);
	let warnCount = $derived(dashboards.filter(d => d.status === 'WARN').length);
	let failCount = $derived(dashboards.filter(d => d.status === 'FAIL').length);
	let unknownCount = $derived(dashboards.filter(d => !d.status || d.status === 'UNKNOWN').length);

	let aggregateLabel = $derived(
		($t.validation?.aggregate_status || '{pass} PASS • {warn} WARN • {fail} FAIL')
			.replace('{pass}', String(passCount))
			.replace('{warn}', String(warnCount))
			.replace('{fail}', String(failCount))
	);

	let totalIssues = $derived(
		dashboards.reduce((/** @type {number} */ sum, /** @type {any} */ d) => sum + (d.issues?.length || 0), 0)
	);

	// ── Helpers ──

	/** @param {string} s */
	function getStatusDotClass(s) {
		const map = {
			PASS: 'text-green-500',
			WARN: 'text-yellow-500',
			FAIL: 'text-red-500',
			UNKNOWN: 'text-gray-300',
		};
		return map[s] || 'text-gray-300';
	}

	/** @param {string} s */
	function getStatusDot(s) {
		const dots = {
			PASS: '🟢',
			WARN: '🟡',
			FAIL: '🔴',
			UNKNOWN: '⚪',
		};
		return dots[s] || dots.UNKNOWN;
	}

	/** @param {string} severity */
	function getSeverityClass(severity) {
		const map = {
			critical: 'bg-red-100 text-red-700',
			high: 'bg-orange-100 text-orange-700',
			medium: 'bg-yellow-100 text-yellow-700',
			low: 'bg-blue-100 text-blue-700',
			info: 'bg-gray-100 text-gray-600',
			error: 'bg-red-100 text-red-700',
			warning: 'bg-yellow-100 text-yellow-700',
		};
		return map[severity?.toLowerCase()] || 'bg-gray-100 text-gray-600';
	}

	/** @param {number} ms */
	function formatDuration(ms) {
		if (!ms && ms !== 0) return '-';
		if (ms < 1000) return `${ms}ms`;
		if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
		return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
	}

	function getTriggerLabel(trigger) {
		if (trigger === 'manual') return $t.validation?.trigger_manual || 'Manual';
		if (trigger === 'scheduled') return $t.validation?.trigger_scheduled || 'Scheduled';
		return trigger || '-';
	}

	function getPathLabel(path) {
		if (path === 'A') return $t.validation?.path_a || 'Path A — Screenshot';
		if (path === 'B') return $t.validation?.path_b || 'Path B — Text-only';
		return path || '-';
	}

	function getPathBadgeClass(path) {
		if (path === 'A') return 'bg-purple-100 text-purple-700';
		if (path === 'B') return 'bg-teal-100 text-teal-700';
		return 'bg-gray-100 text-gray-500';
	}

	/**
	 * Determine if a tab has issues by matching its label/idx against issue locations.
	 * Uses tab_index from LLM output (preferred) or heuristic label matching.
	 * @param {string} tabLabel - Human-readable tab name (e.g. "Overview")
	 * @param {number} tabIdx - 0-based tab index
	 * @param {number} totalTabs - total number of tabs for this dashboard
	 * @param {Array} issues - Dashboard issues with {severity, location, tab_index}
	 * @returns {{count: number, severity: string}|null}
	 */
	function getTabIssueSeverity(tabLabel, tabIdx, totalTabs, issues) {
		if (!issues?.length) return null;
		// Prefer tab_index from LLM output (exact match)
		const byIndex = issues.filter(i => i.tab_index === tabIdx || i.tab_index === String(tabIdx));
		if (byIndex.length > 0) {
			const worst = byIndex.some(i => i.severity === 'FAIL') ? 'FAIL'
				: byIndex.some(i => i.severity === 'WARN') ? 'WARN' : 'INFO';
			return { count: byIndex.length, severity: worst };
		}
		// Fallback: heuristic label matching
		const lowerLabel = tabLabel?.toLowerCase();
		let matching = [];
		if (lowerLabel) {
			matching = issues.filter(issue => {
				if (!issue.location) return false;
				const lowerLoc = issue.location.toLowerCase();
				return lowerLabel.includes(lowerLoc) || lowerLoc.includes(lowerLabel);
			});
		}
		// Fallback: show unmatched issues on the LAST tab only
		const unmatched = issues.filter(i => i.tab_index === -1 || i.tab_index === '-1' || i.tab_index === undefined || i.tab_index === null);
		if (matching.length === 0 && tabIdx === totalTabs - 1 && unmatched.length > 0) {
			const worst = unmatched.some(i => i.severity === 'FAIL') ? 'FAIL'
				: unmatched.some(i => i.severity === 'WARN') ? 'WARN' : 'INFO';
			return { count: unmatched.length, severity: worst };
		}
		if (matching.length === 0) return null;
		const worst = matching.some(i => i.severity === 'FAIL') ? 'FAIL'
			: matching.some(i => i.severity === 'WARN') ? 'WARN' : 'INFO';
		return { count: matching.length, severity: worst };
	}

	/** @param {string} dashboardId */
	function toggleDashboard(dashboardId) {
		const next = new Set(expandedDashboards);
		if (next.has(dashboardId)) {
			next.delete(dashboardId);
		} else {
			next.add(dashboardId);
			// Auto-select first screenshot tab and trigger preload
			if (!selectedScreenshotTab[dashboardId]) {
				selectedScreenshotTab = { ...selectedScreenshotTab, [dashboardId]: '0' };
				const db = dashboards.find((/** @type {any} */ d) => d.id === dashboardId || d.dashboard_id === dashboardId);
				if (db?.screenshots?.length) {
					const tabKey = `${dashboardId}_0`;
					if (!screenshotUrls[tabKey]) {
						loadScreenshot(db.screenshots[0].path || db.screenshots[0], tabKey);
					}
				}
			}
		}
		expandedDashboards = next;
	}

	/** @param {string} dashboardId */
	function isDashboardExpanded(dashboardId) {
		return expandedDashboards.has(dashboardId);
	}

	/** @param {string} key */
	function toggleLogsSent(key) {
		const next = new Set(logsSentExpanded);
		if (next.has(key)) next.delete(key);
		else next.add(key);
		logsSentExpanded = next;
	}

/** @param {string} key */
function toggleTaskLogs(key) {
	const next = new Set(taskLogsExpanded);
	if (next.has(key)) {
		next.delete(key);
	} else {
		next.add(key);
		// Load task logs on first expand (client-side)
		const dId = key.replace('tasklogs_', '');
		const db = dashboards.find((/** @type {any} */ d) => d.id === dId || d.dashboard_id === dId);
		if (db && !db._taskLogsLoaded && run?.task_id) {
			db._taskLogsLoaded = true;
			api.getTaskLogs(run.task_id, { limit: 200 }).then((logs) => {
				db.task_logs = logs || [];
			}).catch(() => {
				db.task_logs = [];
			});
		}
	}
	taskLogsExpanded = next;
}

	/**
	 * Load a screenshot blob from the storage API and cache it.
	 * @param {string} screenshotPath
	 * @param {string} cacheKey
	 */
	async function loadScreenshot(screenshotPath, cacheKey) {
		if (screenshotUrls[cacheKey]) return;
		try {
			const blob = await api.getStorageFileBlob(screenshotPath);
			const url = URL.createObjectURL(blob);
			screenshotUrls = { ...screenshotUrls, [cacheKey]: url };
		} catch {
			// silently fail — screenshot unavailable
		}
	}

	/** @param {string} url */
	function openScreenshot(url) {
		window.open(url, '_blank');
	}

	function handleBack() {
		// eslint-disable-next-line svelte/no-navigation-without-resolve
		goto(`/validation-tasks/${policyId}`);
	}

	// When a dashboard is expanded and has screenshots, preload the first one
	$effect(() => {
		const expanded = Array.from(expandedDashboards);
		for (const dId of expanded) {
			/** @type {any} */
			const db = dashboards.find((/** @type {any} */ d) => d.id === dId || d.dashboard_id === dId);
			if (!db) continue;
			const screenshots = db.screenshots;
			if (screenshots?.length) {
				const tabKey = `${dId}_0`;
				if (!screenshotUrls[tabKey]) {
					loadScreenshot(screenshots[0].path || screenshots[0], tabKey);
				}
			}
		}
	});
</script>

<svelte:head>
	<title>{$t.validation?.run_detail || 'Run Detail'} #{runId?.substring(0, 8)}</title>
</svelte:head>

<div class="container mx-auto px-4 py-6 max-w-full xl:max-w-7xl">
	<!-- Back link -->
	<button
		onclick={handleBack}
		class="inline-flex items-center text-sm text-gray-500 hover:text-gray-700 mb-4 transition-colors"
	>
		<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
			<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
		</svg>
		{$t.validation?.back_to_tasks || '← Back to tasks'}
	</button>

	<!-- Loading State -->
	{#if uxState === 'loading'}
		<div class="space-y-6">
			<div class="bg-white border border-gray-200 rounded-lg p-6 animate-pulse space-y-3">
				<div class="h-6 w-64 bg-gray-200 rounded"></div>
				<div class="h-4 w-96 bg-gray-100 rounded"></div>
				<div class="h-8 w-48 bg-gray-100 rounded-full"></div>
			</div>
			{#each Array(3) as _, i (i)}
				<div class="h-16 bg-gray-100 rounded-lg animate-pulse"></div>
			{/each}
		</div>

	<!-- Error State -->
	{:else if uxState === 'error'}
		<div class="bg-red-50 border border-red-200 rounded-lg p-6 text-center">
			<p class="text-red-700 mb-3">{errorMsg || $t.validation?.load_runs_failed || 'Failed to load run details.'}</p>
			<button
				onclick={() => window.location.reload()}
				class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
			>
				{$t.validation?.retry || 'Retry'}
			</button>
		</div>

	<!-- NotFound State -->
	{:else if uxState === 'not_found'}
		<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-12 text-center">
			<h2 class="text-xl font-bold text-gray-900 mb-2">{$t.validation?.task_not_found || 'Run not found'}</h2>
			<p class="text-gray-500 mb-6">{$t.validation?.task_not_found_desc || 'The run you are looking for does not exist or has been deleted.'}</p>
			<button
				onclick={handleBack}
				class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
			>
				{$t.validation?.go_back || 'Go back'}
			</button>
		</div>

	<!-- Populated State -->
	{:else if uxState === 'populated'}
		<!-- Run Header -->
		<div class="bg-white border border-gray-200 rounded-lg p-6 mb-6">
			<div class="flex items-start justify-between">
				<div>
					<h1 class="text-2xl font-bold text-gray-900 mb-1">
						{$t.validation?.run_detail || 'Run Detail'}
						{#if run?.id}
							<span class="text-gray-400 font-mono text-lg">#{run.id.substring(0, 8)}</span>
						{/if}
					</h1>
					{#if run?.task_name}
						<p class="text-sm text-gray-500 mb-2">{run.task_name}</p>
					{/if}
					<div class="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-gray-500">
						{#if run?.timestamp || run?.started_at || run?.created_at}
							<span class="flex items-center gap-1">
								<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
									<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
								</svg>
								{new Date(run.timestamp || run.started_at || run.created_at).toLocaleString()}
							</span>
						{/if}
						<span class="flex items-center gap-1">
							<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
								<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
							</svg>
							{getTriggerLabel(run?.trigger)}
						</span>
						<span class="flex items-center gap-1">
							<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
								<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
							</svg>
							{$t.validation?.duration || 'Duration'}: {formatDuration(run?.duration_ms)}
						</span>
						<span class="text-xs text-gray-400">
							{dashboards.length} {$t.validation?.dashboard || 'dashboard'}{dashboards.length !== 1 ? 's' : ''}
							· {totalIssues} {$t.validation?.issues?.toLowerCase() || 'issues'}
						</span>
					</div>
				</div>
				<!-- Aggregate Badge -->
				<div class="flex items-center gap-2 flex-shrink-0 ml-4">
					{#if failCount > 0}
						<span class="inline-flex items-center px-3 py-1.5 rounded-full text-sm font-medium bg-red-100 text-red-700">
							{aggregateLabel}
						</span>
					{:else if warnCount > 0}
						<span class="inline-flex items-center px-3 py-1.5 rounded-full text-sm font-medium bg-yellow-100 text-yellow-700">
							{aggregateLabel}
						</span>
					{:else}
						<span class="inline-flex items-center px-3 py-1.5 rounded-full text-sm font-medium bg-green-100 text-green-700">
							{aggregateLabel}
						</span>
					{/if}
				</div>
			</div>
		</div>

		<!-- Dashboard List -->
		<div class="space-y-2">
			{#each dashboards as dashboard (dashboard.id || dashboard.dashboard_id)}
				{@const dId = dashboard.id || dashboard.dashboard_id}
				{@const isExpanded = expandedDashboards.has(dId)}
				{@const dbIssues = dashboard.issues || []}
				{@const dbPath = dashboard.path || 'A'}

				<!-- Dashboard Row (Collapsible Header) -->
				<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
				<div
					onclick={() => toggleDashboard(dId)}
					onkeydown={(e) => { if (e.key === 'Enter') toggleDashboard(dId); }}
					role="button"
					tabindex="0"
					class="bg-white border border-gray-200 rounded-lg transition-all hover:shadow-sm cursor-pointer {isExpanded ? 'rounded-b-none border-b-0' : ''}"
				>
					<div class="flex items-center px-4 py-3">
						<!-- Status dot -->
						<span class="text-lg mr-3">{getStatusDot(dashboard.status)}</span>

						<!-- Title -->
						<div class="flex-1 min-w-0">
							<p class="text-sm font-medium text-gray-900 truncate">
								{dashboard.title || dashboard.name || dId}
							</p>
							<div class="flex items-center gap-2 text-xs text-gray-500">
								<span>{dbIssues.length} {$t.validation?.issues?.toLowerCase() || 'issues'}</span>
								<span>·</span>
								<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium {getPathBadgeClass(dbPath)}">
									{#if dbPath === 'A'}📸 {:else}📝{/if}
									<span class="ml-1">{getPathLabel(dbPath)}</span>
								</span>
							</div>
						</div>

						<!-- Expand/collapse arrow -->
						<svg
							class="w-5 h-5 text-gray-400 transition-transform {isExpanded ? 'rotate-180' : ''}"
							fill="none" stroke="currentColor" viewBox="0 0 24 24"
						>
							<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
						</svg>
					</div>
				</div>

				<!-- Expanded Detail -->
				{#if isExpanded}
					<div class="bg-white border border-t-0 border-gray-200 rounded-b-lg p-4 space-y-6">
						<!-- Issues Table -->
						<section>
							<h3 class="text-sm font-semibold text-gray-900 mb-2 flex items-center gap-2">
								<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
									<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
								</svg>
								{$t.validation?.issues || 'Issues'}
							</h3>
							{#if dbIssues.length === 0}
								<div class="bg-gray-50 border border-dashed border-gray-200 rounded-lg p-4 text-center">
									<p class="text-sm text-gray-400">{$t.validation?.no_issues || 'No issues detected'}</p>
								</div>
							{:else}
								<div class="overflow-x-auto border border-gray-200 rounded-lg">
									<table class="min-w-full divide-y divide-gray-200 text-sm">
										<thead class="bg-gray-50">
											<tr>
												<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.severity || 'Severity'}</th>
												<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.message || 'Message'}</th>
												<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.location || 'Location'}</th>
											</tr>
										</thead>
										<tbody class="divide-y divide-gray-200">
											{#each dbIssues as issue (issue.id || `${issue.message}-${issue.location || ''}-${issue.severity || ''}`)}
												<tr class="hover:bg-gray-50">
													<td class="px-3 py-2 whitespace-nowrap">
														<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium {getSeverityClass(issue.severity || issue.level)}">
															{issue.severity || issue.level || 'info'}
														</span>
													</td>
													<td class="px-3 py-2 text-gray-700 max-w-md">
														<p class="truncate" title={issue.message}>{issue.message || '-'}</p>
													</td>
													<td class="px-3 py-2 text-gray-500 max-w-xs">
														<p class="truncate text-xs font-mono" title={issue.location || issue.element || ''}>
															{issue.location || issue.element || '-'}
														</p>
													</td>
												</tr>
											{/each}
										</tbody>
									</table>
								</div>
							{/if}
						</section>

						<!-- Path Indicator -->
						<div class="flex items-center gap-2 text-xs text-gray-500 bg-gray-50 border border-gray-200 rounded-lg px-3 py-2">
							{#if dbPath === 'A'}
								<span class="text-purple-600 font-medium">📸 {$t.validation?.path_a || 'Path A — Screenshot'}</span>
								<span class="text-gray-400">| {$t.validation?.screenshots || 'Screenshots'} captured via browser automation</span>
								{#if dashboard.chunk_count && dashboard.chunk_count > 1}
									<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-amber-100 text-amber-700 text-xs font-medium">
										{$t.validation?.chunked || 'Chunked'} ×{dashboard.chunk_count}
									</span>
								{/if}
							{:else}
								<span class="text-teal-600 font-medium">📝 {$t.validation?.path_b || 'Path B — Text-only'}</span>
								<span class="text-gray-400">| {$t.validation?.issues || 'Issue'} detection via DOM extraction</span>
							{/if}
						</div>

						<!-- Path B: Dataset Health -->
						{#if dbPath === 'B'}
							{@const dsHealth = dashboard.dataset_health || dashboard.datasetHealth || []}
							<section>
								<h3 class="text-sm font-semibold text-gray-900 mb-2 flex items-center gap-2">
									<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
										<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4" />
									</svg>
									{$t.validation?.dataset_health || 'Dataset Health'}
								</h3>
								{#if dsHealth.length === 0}
									<div class="bg-gray-50 border border-dashed border-gray-200 rounded-lg p-4 text-center">
										<p class="text-sm text-gray-400">{$t.validation?.no_issues || 'No dataset health data'}</p>
									</div>
								{:else}
									<div class="overflow-x-auto border border-gray-200 rounded-lg">
										<table class="min-w-full divide-y divide-gray-200 text-sm">
											<thead class="bg-gray-50">
												<tr>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.dataset_name || 'Name'}</th>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.database || 'Database'}</th>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.backend || 'Backend'}</th>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.status || 'Status'}</th>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.message || 'Error'}</th>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.dashboard || 'Charts'}</th>
												</tr>
											</thead>
											<tbody class="divide-y divide-gray-200">
												{#each dsHealth as ds (ds.id || ds.name)}
													<tr class="hover:bg-gray-50">
														<td class="px-3 py-2 font-medium text-gray-900 whitespace-nowrap max-w-[160px] truncate" title={ds.name}>
															{ds.name || '-'}
														</td>
														<td class="px-3 py-2 text-gray-600 whitespace-nowrap">
															{ds.database || ds.db || '-'}
														</td>
														<td class="px-3 py-2 text-gray-600 whitespace-nowrap">
															{ds.backend || '-'}
														</td>
														<td class="px-3 py-2 whitespace-nowrap">
															{#if ds.status === 'ok' || ds.healthy}
																<span class="inline-flex items-center gap-1 text-green-600 text-xs font-medium">
																	<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
																		<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
																	</svg>
																	{$t.validation?.status_ok || '✓ Healthy'}
																</span>
															{:else}
																<span class="inline-flex items-center gap-1 text-red-600 text-xs font-medium">
																	<svg class="w-4 h-4" 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>
																	{$t.validation?.status_error || '✗ Error'}
																</span>
															{/if}
														</td>
														<td class="px-3 py-2 text-gray-500 max-w-[200px]">
															<p class="truncate text-xs" title={ds.error || ds.message || ''}>
																{ds.error || ds.message || '-'}
															</p>
														</td>
														<td class="px-3 py-2 text-gray-500 text-xs">
															{ds.affected_charts || ds.charts?.length || ds.chart_count || '-'}
														</td>
													</tr>
												{/each}
											</tbody>
										</table>
									</div>
								{/if}
							</section>
						{/if}

						<!-- Path B with execute_chart_data: Chart Data Results -->
						{#if dbPath === 'B' && dashboard.execute_chart_data}
							{@const chartData = dashboard.chart_data || dashboard.chartData || []}
							<section>
								<h3 class="text-sm font-semibold text-gray-900 mb-2 flex items-center gap-2">
									<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
										<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
									</svg>
									{$t.validation?.chart_data || 'Chart Data Results'}
								</h3>
								{#if chartData.length === 0}
									<div class="bg-gray-50 border border-dashed border-gray-200 rounded-lg p-4 text-center">
										<p class="text-sm text-gray-400">{$t.validation?.no_issues || 'No chart data available'}</p>
									</div>
								{:else}
									<div class="overflow-x-auto border border-gray-200 rounded-lg">
										<table class="min-w-full divide-y divide-gray-200 text-sm">
											<thead class="bg-gray-50">
												<tr>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.chart_name || 'Chart'}</th>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.viz_type || 'Type'}</th>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.duration || 'Duration'}</th>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.row_count || 'Rows'}</th>
													<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">{$t.validation?.message || 'Error'}</th>
												</tr>
											</thead>
											<tbody class="divide-y divide-gray-200">
												{#each chartData as chart (chart.id || chart.name)}
													<tr class="hover:bg-gray-50">
														<td class="px-3 py-2 font-medium text-gray-900 whitespace-nowrap max-w-[160px] truncate" title={chart.name}>
															{chart.name || '-'}
														</td>
														<td class="px-3 py-2 text-gray-600 whitespace-nowrap">
															{chart.viz_type || chart.type || '-'}
														</td>
														<td class="px-3 py-2 text-gray-500 whitespace-nowrap font-mono text-xs">
															{formatDuration(chart.duration_ms || chart.duration)}
														</td>
														<td class="px-3 py-2 text-gray-600 whitespace-nowrap">
															{chart.row_count ?? chart.rows ?? '-'}
														</td>
														<td class="px-3 py-2 text-gray-500 max-w-[200px]">
															{#if chart.error}
																<span class="text-red-600 text-xs">{chart.error}</span>
															{:else}
																<span class="text-green-600 text-xs">—</span>
															{/if}
														</td>
													</tr>
												{/each}
											</tbody>
										</table>
									</div>
								{/if}
							</section>
						{/if}

						<!-- Path A: Screenshots with tab selector -->
						{#if dbPath === 'A'}
							{@const screenshots = dashboard.screenshots || []}
							<section>
								<h3 class="text-sm font-semibold text-gray-900 mb-2 flex items-center gap-2">
									<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
										<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
									</svg>
									{$t.validation?.screenshots || 'Screenshots'}
								</h3>
								{#if screenshots.length === 0}
									<div class="bg-gray-50 border border-dashed border-gray-200 rounded-lg p-4 text-center">
										<p class="text-sm text-gray-400">{$t.validation?.no_screenshots || 'No screenshots saved'}</p>
									</div>
								{:else}
									<!-- Tab Selector with issue badges -->
									<div class="flex flex-wrap gap-1 mb-3">
										{#each screenshots as shot, idx (idx)}
											{@const tabKey = `${dId}_${idx}`}
											{@const tabLabel = shot.label || ''}
											{@const issueTag = getTabIssueSeverity(tabLabel, dbIssues)}
											<button
												onclick={() => {
													selectedScreenshotTab = { ...selectedScreenshotTab, [dId]: String(idx) };
													if (!screenshotUrls[tabKey]) {
														loadScreenshot(shot.path || shot, tabKey);
													}
												}}
												class="px-3 py-1.5 text-xs rounded-lg border transition-colors inline-flex items-center gap-1.5
													{(selectedScreenshotTab[dId] || '0') === String(idx)
														? 'bg-blue-100 border-blue-300 text-blue-700'
														: 'bg-white border-gray-200 text-gray-600 hover:border-gray-300'}
													{issueTag ? (issueTag.severity === 'FAIL' ? 'ring-2 ring-red-300' : 'ring-2 ring-yellow-300') : ''}"
											>
												{#if issueTag}
													<span class="inline-flex items-center justify-center w-4 h-4 rounded-full text-xs font-bold {issueTag.severity === 'FAIL' ? 'bg-red-500 text-white' : 'bg-yellow-400 text-yellow-900'}">
														{issueTag.count}
													</span>
												{/if}
												{$t.validation?.tab || 'Tab'} {idx + 1}{tabLabel ? `: ${tabLabel}` : ''}
											</button>
										{/each}
									</div>

									<!-- Screenshot Preview -->
									{@const activeTab = selectedScreenshotTab[dId] || '0'}
									{@const activeShot = screenshots[parseInt(activeTab)]}
									{@const shotCacheKey = `${dId}_${activeTab}`}
									{@const shotUrl = screenshotUrls[shotCacheKey]}

									{#if shotUrl}
										<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
										<div
											onclick={() => openScreenshot(shotUrl)}
											onkeydown={(e) => { if (e.key === 'Enter') openScreenshot(shotUrl); }}
											role="button"
											tabindex="0"
											class="border border-gray-200 rounded-lg overflow-hidden cursor-zoom-in hover:shadow-md transition-shadow"
										>
											<img
												src={shotUrl}
												alt={`Screenshot ${parseInt(activeTab) + 1}`}
												class="w-full h-auto max-h-[500px] object-contain bg-gray-50"
											/>
										</div>
										<p class="text-xs text-gray-400 mt-1">{$t.validation?.screenshot || 'Click to open in new tab'}</p>
									{:else if activeShot}
										<div class="border border-gray-200 rounded-lg p-8 text-center bg-gray-50 animate-pulse">
											<svg class="w-12 h-12 mx-auto text-gray-300 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
												<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
											</svg>
											<p class="text-sm text-gray-400">{$t.common?.loading || 'Loading...'}</p>
										</div>
									{/if}
								{/if}
							</section>
						{/if}

						<!-- Both Paths: Logs sent to LLM -->
						{#if true}
							{@const logsSent = dashboard.logs_sent_to_llm || dashboard.llm_prompt || dashboard.llm_logs}
							{@const logsKey = `logs_${dId}`}
							<section>
								<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
								<div
									onclick={() => toggleLogsSent(logsKey)}
									onkeydown={(e) => { if (e.key === 'Enter') toggleLogsSent(logsKey); }}
									role="button"
									tabindex="0"
									class="flex items-center justify-between bg-gray-50 border border-gray-200 rounded-lg px-3 py-2 cursor-pointer hover:bg-gray-100 transition-colors"
								>
									<h3 class="text-sm font-semibold text-gray-900">{$t.validation?.logs_sent || 'Logs sent to LLM'}</h3>
									<svg
										class="w-4 h-4 text-gray-400 transition-transform {logsSentExpanded.has(logsKey) ? 'rotate-180' : ''}"
										fill="none" stroke="currentColor" viewBox="0 0 24 24"
									>
										<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
									</svg>
								</div>
								{#if logsSentExpanded.has(logsKey)}
									{#if logsSent}
										<pre class="mt-2 bg-gray-900 text-green-400 text-xs rounded-lg p-4 overflow-x-auto max-h-64 overflow-y-auto font-mono"><code>{typeof logsSent === 'string' ? logsSent : JSON.stringify(logsSent, null, 2)}</code></pre>
									{:else}
										<div class="mt-2 bg-gray-50 border border-dashed border-gray-200 rounded-lg p-4 text-center">
											<p class="text-sm text-gray-400">{$t.validation?.no_logs || 'No logs available'}</p>
										</div>
									{/if}
								{/if}
							</section>
						{/if}

						<!-- Both Paths: Task execution logs -->
						{#if true}
							{@const execLogs = dashboard.task_logs || dashboard.executionLogs || dashboard.logs || []}
							{@const taskLogsKey = `tasklogs_${dId}`}
							<section>
								<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
								<div
									onclick={() => toggleTaskLogs(taskLogsKey)}
									onkeydown={(e) => { if (e.key === 'Enter') toggleTaskLogs(taskLogsKey); }}
									role="button"
									tabindex="0"
									class="flex items-center justify-between bg-gray-50 border border-gray-200 rounded-lg px-3 py-2 cursor-pointer hover:bg-gray-100 transition-colors"
								>
									<h3 class="text-sm font-semibold text-gray-900">{$t.validation?.task_logs || 'Task execution logs'}</h3>
									<svg
										class="w-4 h-4 text-gray-400 transition-transform {taskLogsExpanded.has(taskLogsKey) ? 'rotate-180' : ''}"
										fill="none" stroke="currentColor" viewBox="0 0 24 24"
									>
										<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
									</svg>
								</div>
								{#if taskLogsExpanded.has(taskLogsKey)}
									{#if execLogs.length > 0}
										<div class="mt-2 overflow-x-auto border border-gray-200 rounded-lg">
											<table class="min-w-full divide-y divide-gray-200 text-xs">
												<thead class="bg-gray-50">
													<tr>
														<th class="px-3 py-2 text-left font-medium text-gray-500 uppercase">Time</th>
														<th class="px-3 py-2 text-left font-medium text-gray-500 uppercase">Level</th>
														<th class="px-3 py-2 text-left font-medium text-gray-500 uppercase">Source</th>
														<th class="px-3 py-2 text-left font-medium text-gray-500 uppercase">Message</th>
													</tr>
												</thead>
												<tbody class="divide-y divide-gray-200">
													{#each execLogs as log (log.id || log.timestamp || log.time)}
														<tr class="hover:bg-gray-50">
															<td class="px-3 py-1.5 whitespace-nowrap text-gray-500 font-mono">
																{log.timestamp || log.time || '-'}
															</td>
															<td class="px-3 py-1.5 whitespace-nowrap">
																<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium {getSeverityClass(log.level)}">
																	{log.level || 'INFO'}
																</span>
															</td>
															<td class="px-3 py-1.5 whitespace-nowrap text-gray-600 font-mono">
																{log.source || log.logger || '-'}
															</td>
															<td class="px-3 py-1.5 text-gray-700 max-w-md">
																<p class="truncate" title={log.message}>{log.message || '-'}</p>
															</td>
														</tr>
													{/each}
												</tbody>
											</table>
										</div>
									{:else}
										<div class="mt-2 bg-gray-50 border border-dashed border-gray-200 rounded-lg p-4 text-center">
											<p class="text-sm text-gray-400">{$t.validation?.no_logs || 'No logs available'}</p>
										</div>
									{/if}
								{/if}
							</section>
						{/if}
					</div>
				{/if}
			{/each}
		</div>
	{/if}
</div>
<!-- #endregion ValidationRunDetailPage -->
