// #region UseReviewSessionTest [C:3] [TYPE Module] [SEMANTICS test, dataset-review, session, api, handlers]
// @BRIEF Unit tests for dataset review session composable — load, update, export, clarify, and lifecycle handlers.
// @RELATION DEPENDS_ON -> [UseReviewSession]
// @TEST_EDGE: empty_sessionId -> loadSessionDetail returns empty result when sessionId is falsy
// @TEST_EDGE: conflict_error -> handleResumeSession handles 409 conflict and returns conflict message
// @TEST_EDGE: null_preview -> handlePreviewUpdated returns null session when session or result.preview is null
// @TEST_EDGE: null_payload -> handleAskAiContext is no-op when payload is null/undefined

import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock $lib/api
vi.mock('$lib/api', () => ({
	api: {
		fetchApi: vi.fn(),
		postApi: vi.fn(),
		deleteApi: vi.fn(),
		requestApi: vi.fn(),
		fetchApiBlob: vi.fn(),
	},
}));

// Mock datasetReview helpers
vi.mock('$lib/api/datasetReview', () => ({
	requestDatasetReviewApi: vi.fn(),
	normalizeDatasetReviewDetail: vi.fn((d) => d),
	isDatasetReviewConflictError: vi.fn(),
	getDatasetReviewConflictMessage: vi.fn(),
	extractDatasetReviewVersion: vi.fn(),
}));

// Mock stores
vi.mock('$lib/stores/datasetReviewSession.svelte.js', () => ({
	datasetReviewSessionStore: { subscribe: vi.fn() },
	setLoading: vi.fn(),
	setError: vi.fn(),
	setSession: vi.fn(),
	resetSession: vi.fn(),
}));

vi.mock('$lib/stores/assistantChat.svelte.js', () => ({
	assistantChatStore: { subscribe: vi.fn() },
	setAssistantDatasetReviewSessionId: vi.fn(),
	setAssistantSeedMessage: vi.fn(),
	setAssistantFocusTarget: vi.fn(),
}));

// Mock cot-logger
vi.mock('$lib/cot-logger', () => ({ log: vi.fn() }));

describe('useReviewSession', () => {
	beforeEach(() => {
		vi.clearAllMocks();
	});

	it('returns expected API surface', async () => {
		const { useReviewSession } = await import('../useReviewSession.ts');
		const session = useReviewSession();
		expect(session).toHaveProperty('loadSessionDetail');
		expect(session).toHaveProperty('handleResumeSession');
		expect(session).toHaveProperty('exportArtifact');
		expect(session).toHaveProperty('handlePreviewUpdated');
		expect(session).toHaveProperty('handleSectionJump');
		expect(session).toHaveProperty('handleAskAiContext');
		expect(session).toHaveProperty('primaryActionHandler');
		expect(session).toHaveProperty('buildSessionUrl');
	});

	it('buildSessionUrl returns correct URL', async () => {
		const { useReviewSession } = await import('../useReviewSession.ts');
		const session = useReviewSession();
		expect(session.buildSessionUrl('abc-123')).toBe('/datasets/review/abc-123');
	});

	it('buildSessionUrl encodes special chars', async () => {
		const { useReviewSession } = await import('../useReviewSession.ts');
		const session = useReviewSession();
		expect(session.buildSessionUrl('id/with/slashes')).toBe('/datasets/review/id%2Fwith%2Fslashes');
	});

	describe('loadSessionDetail', () => {
		it('returns empty result when sessionId is falsy', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().loadSessionDetail('');
			expect(result).toEqual({ session: null, clarificationState: null, loadError: '' });
		});

		it('loads session detail and clarification on success', async () => {
			const { api } = await import('$lib/api');
			const mockSession = { session_id: 's1', clarification_sessions: [{ id: 'c1' }] };
			const mockClarification = { questions: [] };
			vi.mocked(api.fetchApi)
				.mockResolvedValueOnce(mockSession)
				.mockResolvedValueOnce(mockClarification);

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().loadSessionDetail('s1');

			expect(result.session?.session_id).toBe('s1');
			expect(result.clarificationState).toEqual(mockClarification);
			expect(result.loadError).toBe('');
			expect(api.fetchApi).toHaveBeenCalledTimes(2);
		});

		it('returns loadError when API throws', async () => {
			const { api } = await import('$lib/api');
			vi.mocked(api.fetchApi).mockRejectedValue(new Error('Network failure'));

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().loadSessionDetail('s1');

			expect(result.loadError).toBe('Network failure');
			expect(result.session).toBeNull();
		});

		it('returns empty clarificationState when session has no clarification_sessions', async () => {
			const { api } = await import('$lib/api');
			vi.mocked(api.fetchApi).mockResolvedValue({ session_id: 's2' });

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().loadSessionDetail('s2');

			expect(result.clarificationState).toBeNull();
			expect(api.fetchApi).toHaveBeenCalledTimes(1); // no second call for clarification
		});
	});

	describe('handleResumeSession', () => {
		it('returns empty when sessionId is falsy', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().handleResumeSession('');
			expect(result).toEqual({ loadError: '' });
		});

		it('resumes successfully and navigates', async () => {
			const { requestDatasetReviewApi } = await import('$lib/api/datasetReview');
			vi.mocked(requestDatasetReviewApi).mockResolvedValue({ session_id: 's1' });

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().handleResumeSession('s1');

			expect(result.loadError).toBe('');
			expect(requestDatasetReviewApi).toHaveBeenCalledWith(
				'/dataset-orchestration/sessions/s1', 'PATCH',
				{ status: 'active' }, undefined,
			);
		});

		it('returns conflict error message on 409', async () => {
			const { requestDatasetReviewApi, isDatasetReviewConflictError, getDatasetReviewConflictMessage } = await import('$lib/api/datasetReview');
			const conflictErr = new Error('Version conflict');
			vi.mocked(requestDatasetReviewApi).mockRejectedValue(conflictErr);
			vi.mocked(isDatasetReviewConflictError).mockReturnValue(true);
			vi.mocked(getDatasetReviewConflictMessage).mockReturnValue('Session changed elsewhere');

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().handleResumeSession('s1');

			expect(result.loadError).toBe('Session changed elsewhere');
		});
	});

	describe('exportArtifact', () => {
		it('returns empty when sessionId is falsy', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().exportArtifact('', 'sql', 'json');
			expect(result).toEqual({ exportMessage: '', isExporting: false });
		});

		it('returns formatted message on success', async () => {
			const { api } = await import('$lib/api');
			vi.mocked(api.fetchApi).mockResolvedValue({ artifact_type: 'SQL', format: 'json', storage_ref: '/tmp/s1.sql' });

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().exportArtifact('s1', 'sql', 'json');

			expect(result.exportMessage).toBe('SQL • json • /tmp/s1.sql');
			expect(result.isExporting).toBe(false);
		});

		it('returns error message on failure', async () => {
			const { api } = await import('$lib/api');
			vi.mocked(api.fetchApi).mockRejectedValue(new Error('Export failed'));

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().exportArtifact('s1', 'sql', 'json');

			expect(result.exportMessage).toBe('Export failed');
		});
	});

	describe('updateSessionLifecycle', () => {
		it('returns empty when sessionId is falsy', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().updateSessionLifecycle('', 'completed');
			expect(result).toEqual({ loadError: '' });
		});

		it('updates status successfully', async () => {
			const { requestDatasetReviewApi } = await import('$lib/api/datasetReview');
			vi.mocked(requestDatasetReviewApi).mockResolvedValue({});

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().updateSessionLifecycle('s1', 'completed');

			expect(result.loadError).toBe('');
			expect(requestDatasetReviewApi).toHaveBeenCalledWith(
				'/dataset-orchestration/sessions/s1', 'PATCH',
				{ status: 'completed' }, undefined,
			);
		});

		it('returns conflict message on 409', async () => {
			const { requestDatasetReviewApi, isDatasetReviewConflictError, getDatasetReviewConflictMessage } = await import('$lib/api/datasetReview');
			vi.mocked(requestDatasetReviewApi).mockRejectedValue(new Error('conflict'));
			vi.mocked(isDatasetReviewConflictError).mockReturnValue(true);
			vi.mocked(getDatasetReviewConflictMessage).mockReturnValue('Stale version');

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().updateSessionLifecycle('s1', 'completed');

			expect(result.loadError).toBe('Stale version');
		});
	});

	describe('handlePreviewUpdated', () => {
		it('returns null session when session is null', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = useReviewSession().handlePreviewUpdated(null, { preview: {} });
			expect(result).toEqual({ session: null, previewUiState: '' });
		});

		it('returns null session when result has no preview', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = useReviewSession().handlePreviewUpdated({ session_id: 's1' }, { other: 'data' });
			expect(result).toEqual({ session: null, previewUiState: '' });
		});

		it('updates session with new preview', async () => {
			const { extractDatasetReviewVersion } = await import('$lib/api/datasetReview');
			vi.mocked(extractDatasetReviewVersion).mockReturnValue(2);

			const { useReviewSession } = await import('../useReviewSession.ts');
			const session = { session_id: 's1', version: 1, previews: [], readiness_state: 'compiled_preview_ready' };
			const result = useReviewSession().handlePreviewUpdated(session, {
				preview: { preview_id: 'p1', preview_status: 'ready' },
				preview_state: 'ready',
			});

			expect(result.session?.previews).toHaveLength(1);
			expect(result.previewUiState).toBe('ready');
		});
	});

	describe('handleSemanticUpdated', () => {
		it('returns null session when session is null', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = useReviewSession().handleSemanticUpdated(null, { fields: [] });
			expect(result).toEqual({ session: null });
		});

		it('updates semantic fields when result has fields array', async () => {
			const { extractDatasetReviewVersion } = await import('$lib/api/datasetReview');
			vi.mocked(extractDatasetReviewVersion).mockReturnValue(3);

			const { useReviewSession } = await import('../useReviewSession.ts');
			const session = { session_id: 's1', version: 1 };
			const result = useReviewSession().handleSemanticUpdated(session, { fields: [{ id: 'f1' }] });

			expect(result.session?.semantic_fields).toEqual([{ id: 'f1' }]);
			expect(result.session?.session_version).toBe(3);
		});
	});

	describe('handleMappingUpdated', () => {
		it('returns null session when session is null', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = useReviewSession().handleMappingUpdated(null, { mappings: [] });
			expect(result).toEqual({ session: null, previewUiState: '' });
		});

		it('updates mappings from result with preview state', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const session = { session_id: 's1', execution_mappings: [] };
			const result = useReviewSession().handleMappingUpdated(session, {
				mappings: [{ mapping_id: 'm1' }],
				preview_state: 'compiling',
			});

			expect(result.session?.execution_mappings).toEqual([{ mapping_id: 'm1' }]);
			expect(result.previewUiState).toBe('compiling');
		});
	});

	describe('handleSectionJump', () => {
		it('scrolls to preview element for "preview" target', async () => {
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().handleSectionJump('preview');
			expect(document.getElementById).toHaveBeenCalledWith('sql-preview');
			expect(scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth', block: 'start' });
		});

		it('scrolls to fallback id when target not in map', async () => {
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().handleSectionJump('unknown-target');
			expect(document.getElementById).toHaveBeenCalledWith('unknown-target');
		});
	});

	describe('handleAskAiContext', () => {
		it('is no-op when payload is null', async () => {
			const focusFn = vi.fn();
			const seedFn = vi.fn();
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().handleAskAiContext(null, focusFn, seedFn);
			expect(focusFn).not.toHaveBeenCalled();
			expect(seedFn).not.toHaveBeenCalled();
		});

		it('sets focus and seed when payload has target', async () => {
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			const focusFn = vi.fn();
			const seedFn = vi.fn();
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().handleAskAiContext({ target: 'mapping:m1', prompt: 'Explain mapping', label: 'mapping' }, focusFn, seedFn);
			expect(focusFn).toHaveBeenCalledWith({ target: 'mapping:m1', source: 'workspace_context_action' });
			expect(seedFn).toHaveBeenCalledWith('Help review mapping. Section: workspace. Context: Explain mapping');
		});
	});

	describe('primaryActionHandler', () => {
		it('does nothing when session is null', async () => {
			const resumeFn = vi.fn();
			const askFn = vi.fn();
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().primaryActionHandler(null, null, '', resumeFn, askFn);
			expect(resumeFn).not.toHaveBeenCalled();
			expect(askFn).not.toHaveBeenCalled();
		});

		it('calls resume when recommended_action is resume_session', async () => {
			const resumeFn = vi.fn();
			const askFn = vi.fn();
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().primaryActionHandler(
				{ session_id: 's1', recommended_action: 'resume_session' } as Record<string, unknown>,
				null, '', resumeFn, askFn,
			);
			expect(resumeFn).toHaveBeenCalled();
			expect(askFn).not.toHaveBeenCalled();
		});

		it('scrolls to sql-preview when recommended_action is generate_sql_preview', async () => {
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			const resumeFn = vi.fn();
			const askFn = vi.fn();
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().primaryActionHandler(
				{ session_id: 's1', recommended_action: 'generate_sql_preview' } as Record<string, unknown>,
				null, '', resumeFn, askFn,
			);
			expect(askFn).toHaveBeenCalled();
			expect(document.getElementById).toHaveBeenCalledWith('sql-preview');
		});

		it('scrolls to launch when recommended_action is launch_dataset', async () => {
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			const resumeFn = vi.fn();
			const askFn = vi.fn();
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().primaryActionHandler(
				{ session_id: 's1', recommended_action: 'launch_dataset' } as Record<string, unknown>,
				null, '', resumeFn, askFn,
			);
			expect(document.getElementById).toHaveBeenCalledWith('launch');
		});

		it('default action calls askAi with clarification target', async () => {
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			const resumeFn = vi.fn();
			const askFn = vi.fn();
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().primaryActionHandler(
				{ session_id: 's1', recommended_action: 'some_other_action' } as Record<string, unknown>,
				{ question_id: 'q1' } as Record<string, unknown>,
				'seed prompt', resumeFn, askFn,
			);
			expect(askFn).toHaveBeenCalledWith(
				expect.objectContaining({ target: 'clarification:q1' }),
			);
		});
	});

	describe('handleSourceSubmit', () => {
		it('returns passthrough operation object', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().handleSourceSubmit(
				{ session_id: 's1' } as Record<string, unknown>,
				true, '', '', false,
			);
			expect(result).toEqual({
				intakeAcknowledgment: true, submitError: '', loadError: '', isSubmitting: false,
			});
		});
	});

	describe('handleLaunchUpdated', () => {
		it('returns launchResult from result.launch_result', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = useReviewSession().handleLaunchUpdated({
				launch_result: { run_id: 'r1', status: 'started' },
			} as Record<string, unknown>);
			expect(result).toEqual({ launchResult: { run_id: 'r1', status: 'started' } });
		});

		it('returns result itself when launch_result is absent', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = useReviewSession().handleLaunchUpdated({
				session_id: 's1',
			} as Record<string, unknown>);
			expect(result).toEqual({ launchResult: { session_id: 's1' } });
		});

		it('returns null when result is null', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = useReviewSession().handleLaunchUpdated(null);
			expect(result).toEqual({ launchResult: null });
		});
	});

	describe('handleSemanticUpdated — field_id branch', () => {
		it('updates single field when result has field_id', async () => {
			const { extractDatasetReviewVersion } = await import('$lib/api/datasetReview');
			vi.mocked(extractDatasetReviewVersion).mockReturnValue(5);

			const { useReviewSession } = await import('../useReviewSession.ts');
			const session = { session_id: 's1', semantic_fields: [{ field_id: 'f1', name: 'old' }] };
			const result = useReviewSession().handleSemanticUpdated(session as Record<string, unknown>, {
				field_id: 'f1', name: 'updated',
			} as Record<string, unknown>);

			expect(result.session?.semantic_fields).toHaveLength(1);
			expect(result.session?.session_version).toBe(5);
		});
	});

	describe('handleMappingUpdated — mapping_id / result.mapping branches', () => {
		it('updates single mapping when result has mapping_id', async () => {
			const { extractDatasetReviewVersion } = await import('$lib/api/datasetReview');
			vi.mocked(extractDatasetReviewVersion).mockReturnValue(4);

			const { useReviewSession } = await import('../useReviewSession.ts');
			const session = { session_id: 's1', execution_mappings: [{ mapping_id: 'm1', target: 'old' }] };
			const result = useReviewSession().handleMappingUpdated(session as Record<string, unknown>, {
				mapping_id: 'm1', target: 'updated',
			} as Record<string, unknown>);

			expect(result.session?.execution_mappings).toHaveLength(1);
			expect((result.session?.execution_mappings as Record<string, unknown>[])[0]?.target).toBe('updated');
			expect(result.session?.session_version).toBe(4);
		});

	it('updates mapping from result.mapping wrapper', async () => {
		const { useReviewSession } = await import('../useReviewSession.ts');
		const session = { session_id: 's1', execution_mappings: [] };
		const result = useReviewSession().handleMappingUpdated(session as Record<string, unknown>, {
			mapping: { mapping_id: 'm2', target: 'new' },
		} as Record<string, unknown>);

		expect(result.session?.execution_mappings).toHaveLength(1);
	});

	it('returns unchanged collection when mapping item has no mapping_id', async () => {
		const { useReviewSession } = await import('../useReviewSession.ts');
		const session = { session_id: 's1', execution_mappings: [{ mapping_id: 'm1', target: 'old' }] };
		const result = useReviewSession().handleMappingUpdated(session as Record<string, unknown>, {
			mapping: { target: 'updated' },  // no mapping_id
		} as Record<string, unknown>);

		expect(result.session?.execution_mappings).toHaveLength(1);
		expect((result.session?.execution_mappings as Record<string, unknown>[])[0]?.target).toBe('old');
	});

	it('adds new semantic field when field_id not found in existing', async () => {
		const { extractDatasetReviewVersion } = await import('$lib/api/datasetReview');
		vi.mocked(extractDatasetReviewVersion).mockReturnValue(6);

		const { useReviewSession } = await import('../useReviewSession.ts');
		const session = { session_id: 's1', semantic_fields: [{ field_id: 'f1' }] };
		const result = useReviewSession().handleSemanticUpdated(session as Record<string, unknown>, {
			field_id: 'f2', name: 'new field',
		} as Record<string, unknown>);

		expect(result.session?.semantic_fields).toHaveLength(2);
		expect(result.session?.session_version).toBe(6);
	});
});

	describe('handlePreviewUpdated — merge existing previews', () => {
		it('replaces existing preview when preview_id matches', async () => {
			const { extractDatasetReviewVersion } = await import('$lib/api/datasetReview');
			vi.mocked(extractDatasetReviewVersion).mockReturnValue(3);

			const { useReviewSession } = await import('../useReviewSession.ts');
			const session = {
				session_id: 's1', version: 1,
				previews: [{ preview_id: 'p1', status: 'old' }],
				readiness_state: 'run_ready',
			};
			const result = useReviewSession().handlePreviewUpdated(session as Record<string, unknown>, {
				preview: { preview_id: 'p1', preview_status: 'ready' },
				preview_state: 'recompiled',
			} as Record<string, unknown>);

			expect(result.session?.previews).toHaveLength(1);
			expect(result.previewUiState).toBe('recompiled');
			expect(result.session?.readiness_state).toBe('run_ready');
		});
	});

	describe('handleAskAiContext — without target', () => {
		it('sets seed message but not focus target when payload has no target', async () => {
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			const focusFn = vi.fn();
			const seedFn = vi.fn();
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().handleAskAiContext({ prompt: 'Explain this', label: 'item' } as Record<string, unknown>, focusFn, seedFn);
			expect(focusFn).not.toHaveBeenCalled();
			expect(seedFn).toHaveBeenCalledWith('Help review item. Section: workspace. Context: Explain this');
		});
	});

	describe('buildAssistantContextPromptInline (internal)', () => {
		it('generates improve prompt when mode is improve', async () => {
			const { useReviewSession } = await import('../useReviewSession.ts');
			// Access via handleAskAiContext which uses buildAssistantContextPromptInline
			const seedFn = vi.fn();
			const focusFn = vi.fn();
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			useReviewSession().handleAskAiContext(
				{ target: 'mapping:m1', mode: 'improve', prompt: 'Make it better', label: 'mapping', section: 'mapping-section' } as Record<string, unknown>,
				focusFn, seedFn,
			);
			expect(seedFn).toHaveBeenCalledWith('Improve mapping. Section: mapping-section. Context: Make it better');
		});
	});

	describe('handleResumeSession with sessionVersion', () => {
		it('passes sessionVersion to requestDatasetReviewApi', async () => {
			const { requestDatasetReviewApi } = await import('$lib/api/datasetReview');
			vi.mocked(requestDatasetReviewApi).mockResolvedValue({ session_id: 's1' });

			const { useReviewSession } = await import('../useReviewSession.ts');
			await useReviewSession().handleResumeSession('s1', 3);

			expect(requestDatasetReviewApi).toHaveBeenCalledWith(
				'/dataset-orchestration/sessions/s1', 'PATCH',
				{ status: 'active' }, 3,
			);
		});
	});

	describe('updateSessionLifecycle with sessionVersion and error', () => {
		it('passes sessionVersion to requestDatasetReviewApi', async () => {
			const { requestDatasetReviewApi } = await import('$lib/api/datasetReview');
			vi.mocked(requestDatasetReviewApi).mockResolvedValue({});

			const { useReviewSession } = await import('../useReviewSession.ts');
			await useReviewSession().updateSessionLifecycle('s1', 'completed', 5);

			expect(requestDatasetReviewApi).toHaveBeenCalledWith(
				'/dataset-orchestration/sessions/s1', 'PATCH',
				{ status: 'completed' }, 5,
			);
		});

		it('returns error message on non-conflict error', async () => {
			const { requestDatasetReviewApi, isDatasetReviewConflictError } = await import('$lib/api/datasetReview');
			vi.mocked(requestDatasetReviewApi).mockRejectedValue(new Error('DB error'));
			vi.mocked(isDatasetReviewConflictError).mockReturnValue(false);

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().updateSessionLifecycle('s1', 'completed');

			expect(result.loadError).toBe('DB error');
		});
	});

	describe('loadClarificationState — API failure', () => {
		it('returns null when clarification fetch throws', async () => {
			const { api } = await import('$lib/api');
			vi.mocked(api.fetchApi)
				.mockResolvedValueOnce({ session_id: 's1', clarification_sessions: [{ id: 'c1' }] })
				.mockRejectedValueOnce(new Error('Clarification fetch failed'));

			const { useReviewSession } = await import('../useReviewSession.ts');
			const result = await useReviewSession().loadSessionDetail('s1');

			expect(result.clarificationState).toBeNull();
			expect(result.session?.session_id).toBe('s1');
		});
	});

	describe('handleSectionJump — all mapped targets', () => {
		it('maps "mappings" to "mapping" element', async () => {
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().handleSectionJump('mappings');
			expect(document.getElementById).toHaveBeenCalledWith('mapping');
		});

		it('maps "clarification" to "assistant-chat" element', async () => {
			const scrollIntoView = vi.fn();
			document.getElementById = vi.fn().mockReturnValue({ scrollIntoView });
			const { useReviewSession } = await import('../useReviewSession.ts');
			useReviewSession().handleSectionJump('clarification');
			expect(document.getElementById).toHaveBeenCalledWith('assistant-chat');
		});
	});
});
// #endregion UseReviewSessionTest
