// #region Test.TranslationRun [C:3] [TYPE Module] [SEMANTICS test, translate, run, progress, websocket, store]
// @BRIEF Unit tests for translation run store — initial state, state transitions, WS lifecycle, subscriber contracts.
// @RELATION DEPENDS_ON -> [TranslationRunStore]
// @TEST_EDGE: missing_field -> startTranslationRun with empty runId is no-op
// @TEST_EDGE: invalid_type -> updateTranslationRunState merges partial state without corrupting existing
// @TEST_EDGE: external_fail -> WebSocket constructor failure handled gracefully via _handleWsFailure

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

vi.mock('$lib/api', () => ({
	getTranslateRunWsUrl: vi.fn(() => 'ws://localhost:8080/ws/translate/run/test-run'),
}));

// ── Mock WebSocket ─────────────────────────────────────────────────────
interface MockWs {
	close: ReturnType<typeof vi.fn>;
	send: ReturnType<typeof vi.fn>;
	onopen: (() => void) | null;
	onmessage: ((event: MessageEvent) => void) | null;
	onerror: (() => void) | null;
	onclose: ((event: CloseEvent) => void) | null;
}

let mockWs: MockWs;

function createMockWs(): MockWs {
	return {
		close: vi.fn(),
		send: vi.fn(),
		onopen: null,
		onmessage: null,
		onerror: null,
		onclose: null,
	};
}

/** A regular function so that `new WebSocket()` works as a constructor. */
function MockWebSocketConstructor(_url: string): MockWs {
	return mockWs;
}

// ── Test helpers ───────────────────────────────────────────────────────

const runningState = {
	runId: 'test-run',
	uxState: 'running' as const,
	status: null,
	totalRecords: 100,
	successfulRecords: 0,
	failedRecords: 0,
	skippedRecords: 0,
	cacheHits: 0,
	progressPct: 0,
	insertStatus: null,
	batchCount: 0,
	jobId: null,
	isFullRun: false,
};

// ── Tests ──────────────────────────────────────────────────────────────

describe('translationRun store', () => {
	beforeEach(() => {
		vi.resetModules();
		vi.clearAllMocks();
		mockWs = createMockWs();
		globalThis.WebSocket = MockWebSocketConstructor as unknown as typeof WebSocket;
		sessionStorage.clear();
	});

	afterEach(() => {
		// Clean up any module-level WebSocket references
		delete globalThis.WebSocket;
	});

	// ── Initial state ────────────────────────────────────────────────

	it('should have correct initial state', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		expect(translationRunStore.value.uxState).toBe('idle');
		expect(translationRunStore.value.runId).toBeNull();
		expect(translationRunStore.value.totalRecords).toBe(0);
		expect(translationRunStore.value.successfulRecords).toBe(0);
		expect(translationRunStore.value.failedRecords).toBe(0);
		expect(translationRunStore.value.skippedRecords).toBe(0);
		expect(translationRunStore.value.cacheHits).toBe(0);
		expect(translationRunStore.value.progressPct).toBe(0);
		expect(translationRunStore.value.insertStatus).toBeNull();
		expect(translationRunStore.value.batchCount).toBe(0);
		expect(translationRunStore.value.jobId).toBeNull();
		expect(translationRunStore.value.isFullRun).toBe(false);
		expect(translationRunStore.value.status).toBeNull();
	});

	// ── Subscribe ────────────────────────────────────────────────────

	it('translationRunStore.subscribe should call fn with current state', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		const spy = vi.fn();
		const unsubscribe = translationRunStore.subscribe(spy);
		expect(spy).toHaveBeenCalledTimes(1);
		expect(spy).toHaveBeenCalledWith(
			expect.objectContaining({ uxState: 'idle', runId: null }),
		);
		unsubscribe();
	});

	// ── value getter ─────────────────────────────────────────────────

	it('translationRunStore.value should return current state', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		const value = translationRunStore.value;
		expect(value.uxState).toBe('idle');
		expect(value.runId).toBeNull();
	});

	// ── set ──────────────────────────────────────────────────────────

	it('translationRunStore.set should update state and notify subscribers', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		const spy = vi.fn();
		const unsubscribe = translationRunStore.subscribe(spy);

		translationRunStore.set(runningState);

		expect(translationRunStore.value.uxState).toBe('running');
		expect(translationRunStore.value.runId).toBe('test-run');
		// Subscriber should have been called again after set
		expect(spy).toHaveBeenCalledTimes(2);
		expect(spy).toHaveBeenLastCalledWith(
			expect.objectContaining({ uxState: 'running', runId: 'test-run' }),
		);
		unsubscribe();
	});

	// ── update ───────────────────────────────────────────────────────

	it('translationRunStore.update should apply fn to state and notify', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		const spy = vi.fn();
		const unsubscribe = translationRunStore.subscribe(spy);

		translationRunStore.set(runningState);
		spy.mockClear();

		translationRunStore.update((s) => ({
			...s,
			successfulRecords: 50,
			progressPct: 50,
		}));

		expect(translationRunStore.value.successfulRecords).toBe(50);
		expect(translationRunStore.value.progressPct).toBe(50);
		expect(translationRunStore.value.uxState).toBe('running'); // unchanged
		expect(spy).toHaveBeenCalledTimes(1);
		unsubscribe();
	});

	// ── Derived: isTranslationActive ─────────────────────────────────

	it('isTranslationActive should be true when uxState is running', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		expect(translationRunStore.isTranslationActive).toBe(false);

		translationRunStore.set(runningState);
		expect(translationRunStore.isTranslationActive).toBe(true);
	});

	it('isTranslationActive should be true when uxState is inserting', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		translationRunStore.set({ ...runningState, uxState: 'inserting' });
		expect(translationRunStore.isTranslationActive).toBe(true);
	});

	it('isTranslationActive should be false for terminal states', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		const terminalStates = ['completed', 'partial', 'failed', 'insert_failed', 'cancelled'] as const;
		for (const state of terminalStates) {
			translationRunStore.set({ ...runningState, uxState: state });
			expect(translationRunStore.isTranslationActive).toBe(false);
		}
	});

	// ── Derived: isTranslationFinished ───────────────────────────────

	it('isTranslationFinished should be true for terminal states', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		expect(translationRunStore.isTranslationFinished).toBe(false);

		const terminalStates = ['completed', 'partial', 'failed', 'insert_failed', 'cancelled'] as const;
		for (const state of terminalStates) {
			translationRunStore.set({ ...runningState, uxState: state });
			expect(translationRunStore.isTranslationFinished).toBe(true);
		}
	});

	it('isTranslationFinished should be false for running states', async () => {
		const { translationRunStore } = await import('../translationRun.svelte.js');

		translationRunStore.set(runningState);
		expect(translationRunStore.isTranslationFinished).toBe(false);

		translationRunStore.set({ ...runningState, uxState: 'inserting' });
		expect(translationRunStore.isTranslationFinished).toBe(false);

		translationRunStore.set({ ...runningState, uxState: 'idle' });
		expect(translationRunStore.isTranslationFinished).toBe(false);
	});

	// ── startTranslationRun ──────────────────────────────────────────

	it('startTranslationRun should set running state and call getTranslateRunWsUrl', async () => {
		const { getTranslateRunWsUrl } = await import('$lib/api');
		vi.mocked(getTranslateRunWsUrl).mockClear();
		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-42', { jobId: 'job-1', isFullRun: true });

		expect(translationRunStore.value.uxState).toBe('running');
		expect(translationRunStore.value.runId).toBe('run-42');
		expect(translationRunStore.value.jobId).toBe('job-1');
		expect(translationRunStore.value.isFullRun).toBe(true);
		expect(getTranslateRunWsUrl).toHaveBeenCalledWith('run-42');
		expect(vi.mocked(getTranslateRunWsUrl)).toHaveBeenCalledTimes(1);
	});

	it('startTranslationRun should create WebSocket with correct URL', async () => {
		const { getTranslateRunWsUrl } = await import('$lib/api');
		vi.mocked(getTranslateRunWsUrl).mockClear();
		vi.mocked(getTranslateRunWsUrl).mockReturnValue('ws://example.com/ws/run/test');

		// Track WebSocket constructor calls
		const wsSpy = vi.fn();
		globalThis.WebSocket = wsSpy as unknown as typeof WebSocket;
		const localMockWs = createMockWs();
		wsSpy.mockImplementation(() => localMockWs);

		const { startTranslationRun } = await import('../translationRun.svelte.js');

		startTranslationRun('test');

		expect(wsSpy).toHaveBeenCalledWith('ws://example.com/ws/run/test');
	});

	it('startTranslationRun with empty runId should be a no-op', async () => {
		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('');

		expect(translationRunStore.value.uxState).toBe('idle');
		expect(translationRunStore.value.runId).toBeNull();
	});

	// ── resetTranslationRun ──────────────────────────────────────────

	it('resetTranslationRun should reset to initial state', async () => {
		const { translationRunStore, startTranslationRun, resetTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-99');
		expect(translationRunStore.value.uxState).toBe('running');

		resetTranslationRun();

		expect(translationRunStore.value.uxState).toBe('idle');
		expect(translationRunStore.value.runId).toBeNull();
		expect(translationRunStore.value.totalRecords).toBe(0);
		expect(translationRunStore.value.jobId).toBeNull();
		expect(translationRunStore.value.isFullRun).toBe(false);
	});

	// ── updateTranslationRunState ────────────────────────────────────

	it('updateTranslationRunState should merge partial state', async () => {
		const { translationRunStore, startTranslationRun, updateTranslationRunState } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-55');
		updateTranslationRunState({ successfulRecords: 30, totalRecords: 100 });

		expect(translationRunStore.value.successfulRecords).toBe(30);
		expect(translationRunStore.value.totalRecords).toBe(100);
		// Other fields should be preserved
		expect(translationRunStore.value.runId).toBe('run-55');
		expect(translationRunStore.value.uxState).toBe('running');
	});

	it('updateTranslationRunState should notify subscribers', async () => {
		const { translationRunStore, startTranslationRun, updateTranslationRunState } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-55');

		const spy = vi.fn();
		const unsubscribe = translationRunStore.subscribe(spy);
		spy.mockClear();

		updateTranslationRunState({ failedRecords: 5 });

		expect(spy).toHaveBeenCalledTimes(1);
		expect(spy).toHaveBeenCalledWith(
			expect.objectContaining({ failedRecords: 5, runId: 'run-55' }),
		);
		unsubscribe();
	});

	// ── stopTranslationRun ───────────────────────────────────────────

	it('stopTranslationRun should clean up WebSocket and timer', async () => {
		const { startTranslationRun, stopTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-77');
		expect(mockWs.onopen).not.toBeNull(); // WS was set up

		stopTranslationRun();

		// WebSocket close should have been called
		expect(mockWs.close).toHaveBeenCalledTimes(1);
		// onclose and onerror should be nulled before close
		expect(mockWs.onclose).toBeNull();
		expect(mockWs.onerror).toBeNull();
	});

	// ── clearOnCompleteCallback ──────────────────────────────────────

	it('clearOnCompleteCallback should clear the callback', async () => {
		const { startTranslationRun, clearOnCompleteCallback } = await import(
			'../translationRun.svelte.js'
		);

		const onComplete = vi.fn();
		startTranslationRun('run-88', { onComplete });
		clearOnCompleteCallback();

		// Simulate WS message that would normally trigger callback (terminal state)
		expect(mockWs.onmessage).not.toBeNull();
		mockWs.onmessage!(
			new MessageEvent('message', {
				data: JSON.stringify({
					status: 'COMPLETED',
					total_records: 10,
					successful_records: 10,
					failed_records: 0,
					skipped_records: 0,
				}),
			}),
		);

		// onComplete should NOT be called since we cleared it
		expect(onComplete).not.toHaveBeenCalled();
	});

	// ── getStoredActiveRun ───────────────────────────────────────────

	it('getStoredActiveRun should return null when nothing stored', async () => {
		const { getStoredActiveRun } = await import('../translationRun.svelte.js');

		const result = getStoredActiveRun();
		expect(result).toBeNull();
	});

	it('getStoredActiveRun should return stored data when sessionStorage has valid entry', async () => {
		// Store via startTranslationRun (calls _saveToSessionStorage)
		const { startTranslationRun, getStoredActiveRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('active-run-123', { jobId: 'job-456', isFullRun: true });

		const result = getStoredActiveRun();
		expect(result).not.toBeNull();
		expect(result!.runId).toBe('active-run-123');
		expect(result!.jobId).toBe('job-456');
		expect(result!.isFullRun).toBe(true);
	});

	// ── reconnectToRun ───────────────────────────────────────────────

	it('reconnectToRun should set state and connect WebSocket', async () => {
		const { getTranslateRunWsUrl } = await import('$lib/api');
		vi.mocked(getTranslateRunWsUrl).mockClear();
		const { translationRunStore, reconnectToRun } = await import(
			'../translationRun.svelte.js'
		);

		reconnectToRun('reconnect-run', { jobId: 'job-99' });

		expect(translationRunStore.value.uxState).toBe('running');
		expect(translationRunStore.value.runId).toBe('reconnect-run');
		expect(translationRunStore.value.jobId).toBe('job-99');
		expect(getTranslateRunWsUrl).toHaveBeenCalledWith('reconnect-run');
	});

	it('reconnectToRun with same runId should not reconnect', async () => {
		const { getTranslateRunWsUrl } = await import('$lib/api');
		vi.mocked(getTranslateRunWsUrl).mockClear();
		const { startTranslationRun, reconnectToRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('existing-run');
		vi.mocked(getTranslateRunWsUrl).mockClear();
		const wsSpy = vi.fn();
		globalThis.WebSocket = wsSpy as unknown as typeof WebSocket;
		const localMockWs = createMockWs();
		wsSpy.mockImplementation(() => localMockWs);

		reconnectToRun('existing-run');

		// Should skip reconnection since already connected to this run
		expect(getTranslateRunWsUrl).not.toHaveBeenCalled();
		expect(wsSpy).not.toHaveBeenCalled();
	});

	it('reconnectToRun with empty runId should be a no-op', async () => {
		const { translationRunStore, reconnectToRun } = await import(
			'../translationRun.svelte.js'
		);

		const stateBefore = translationRunStore.value.uxState;
		reconnectToRun('');
		expect(translationRunStore.value.uxState).toBe(stateBefore);
	});

	// ── WebSocket onmessage edge cases ────────────────────────────

	it('onmessage with data.error should return early', async () => {
		const { startTranslationRun } = await import('../translationRun.svelte.js');

		startTranslationRun('run-error-early');
		expect(mockWs.onmessage).not.toBeNull();

		const beforeState = { ...mockWs };
		mockWs.onmessage!(
			new MessageEvent('message', {
				data: JSON.stringify({ error: 'some error', status: 'FAILED' }),
			}),
		);
		// State should not change because data.error caused early return
		// We can verify by checking that onclose was not called (meaning _cleanup not triggered)
		expect(mockWs.close).not.toHaveBeenCalled();
	});

	it('onmessage with invalid JSON should be caught silently', async () => {
		const { startTranslationRun } = await import('../translationRun.svelte.js');

		startTranslationRun('run-parse-error');
		expect(mockWs.onmessage).not.toBeNull();

		// This should not throw
		mockWs.onmessage!(
			new MessageEvent('message', { data: 'not valid json {{{' }),
		);
		// After a parse error, state should remain running (not changed)
	});

	it('onmessage should transition to insert_failed when insert_status is failed', async () => {
		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-insert-fail');
		expect(mockWs.onmessage).not.toBeNull();

		mockWs.onmessage!(
			new MessageEvent('message', {
				data: JSON.stringify({
					status: 'COMPLETED',
					total_records: 10,
					successful_records: 8,
					failed_records: 2,
					skipped_records: 0,
					insert_status: 'failed',
				}),
			}),
		);

		expect(translationRunStore.value.uxState).toBe('insert_failed');
		expect(translationRunStore.value.totalRecords).toBe(10);
		expect(translationRunStore.value.successfulRecords).toBe(8);
	});

	it('onmessage should transition to partial when failed_records > 0', async () => {
		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-partial');
		mockWs.onmessage!(
			new MessageEvent('message', {
				data: JSON.stringify({
					status: 'COMPLETED',
					total_records: 10,
					successful_records: 9,
					failed_records: 1,
					skipped_records: 0,
				}),
			}),
		);

		expect(translationRunStore.value.uxState).toBe('partial');
	});

	it('onmessage should transition to cancelled for CANCELLED status', async () => {
		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-cancelled');
		mockWs.onmessage!(
			new MessageEvent('message', {
				data: JSON.stringify({ status: 'CANCELLED' }),
			}),
		);

		expect(translationRunStore.value.uxState).toBe('cancelled');
	});

	it('onmessage should transition to inserting when insertStatus is started', async () => {
		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-inserting');
		mockWs.onmessage!(
			new MessageEvent('message', {
				data: JSON.stringify({
					status: 'RUNNING',
					insert_status: 'started',
				}),
			}),
		);

		expect(translationRunStore.value.uxState).toBe('inserting');
	});

	it('onmessage with unknown status should not change uxState', async () => {
		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-unknown-status');
		mockWs.onmessage!(
			new MessageEvent('message', {
				data: JSON.stringify({ status: 'UNKNOWN_STATUS' }),
			}),
		);

		// uxState should remain 'running' since newUxState is null
		expect(translationRunStore.value.uxState).toBe('running');
	});

	// ── WebSocket onclose edge cases ──────────────────────────────

	it('onclose with code 1000 should not trigger reconnect', async () => {
		const { startTranslationRun } = await import('../translationRun.svelte.js');

		startTranslationRun('run-close-1000');
		expect(mockWs.onclose).not.toBeNull();

		mockWs.onclose!(new CloseEvent('close', { code: 1000, reason: 'Normal' }));

		// _ws should be null now
		// No reconnect should happen — we can assert indirectly since no timer was set
	});

	it('onclose with code 1005 should not trigger reconnect', async () => {
		const { startTranslationRun } = await import('../translationRun.svelte.js');

		startTranslationRun('run-close-1005');
		mockWs.onclose!(new CloseEvent('close', { code: 1005, reason: 'No status' }));

		// No reconnect for normal close codes
	});

	it('onclose with abnormal code should trigger reconnect', async () => {
		const { startTranslationRun } = await import('../translationRun.svelte.js');

		startTranslationRun('run-close-abnormal');
		mockWs.onclose!(new CloseEvent('close', { code: 1006, reason: 'Abnormal' }));

		// Should have scheduled a reconnect after 10s
		// After max reconnects, should mark as failed
	});

	// ── WebSocket onerror ─────────────────────────────────────────

	it('onerror should nullify _ws and let onclose handle reconnect', async () => {
		const { startTranslationRun } = await import('../translationRun.svelte.js');

		startTranslationRun('run-error');
		expect(mockWs.onerror).not.toBeNull();

		mockWs.onerror!();
		// onerror should null _ws but not call _handleWsFailure directly
		// onclose follows and triggers reconnect
	});

	// ── WebSocket constructor failure ─────────────────────────────

	it('onopen should reset reconnect count and log', async () => {
		const { startTranslationRun } = await import('../translationRun.svelte.js');

		startTranslationRun('run-onopen');
		expect(mockWs.onopen).not.toBeNull();

		// Invoke onopen - should not throw
		mockWs.onopen!();
	});

	it('WebSocket constructor failure should be handled gracefully', async () => {
		const { getTranslateRunWsUrl } = await import('$lib/api');
		vi.mocked(getTranslateRunWsUrl).mockClear();

		// Make WebSocket throw during construction
		globalThis.WebSocket = vi.fn().mockImplementation(() => {
			throw new Error('Connection refused');
		}) as unknown as typeof WebSocket;

		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		// This should not throw
		startTranslationRun('run-ws-fail');

		// State should still be running (the error is handled internally)
		expect(translationRunStore.value.uxState).toBe('running');
		expect(translationRunStore.value.runId).toBe('run-ws-fail');
	});

	// ── Session storage edge cases ────────────────────────────────

	it('should handle sessionStorage.setItem failure gracefully', async () => {
		// Directly mutate sessionStorage.setItem to throw
		const origSetItem = sessionStorage.setItem;
		const setItemSpy = vi.fn(() => { throw new Error('Storage full'); });
		sessionStorage.setItem = setItemSpy;

		const { startTranslationRun } = await import('../translationRun.svelte.js');

		// Should not throw even when sessionStorage throws
		expect(() => startTranslationRun('run-storage-fail')).not.toThrow();
		// startTranslationRun should still set state
		const { translationRunStore } = await import('../translationRun.svelte.js');
		expect(translationRunStore.value.uxState).toBe('running');
		expect(translationRunStore.value.runId).toBe('run-storage-fail');

		sessionStorage.setItem = origSetItem;
	});

	it('should handle sessionStorage.removeItem failure gracefully', async () => {
		const origRemoveItem = sessionStorage.removeItem;
		const removeItemSpy = vi.fn(() => { throw new Error('Storage unavailable'); });
		sessionStorage.removeItem = removeItemSpy;

		const { startTranslationRun, resetTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-storage-remove-fail');

		// resetTranslationRun calls _clearSessionStorage which calls removeItem
		// Should not throw even if sessionStorage.removeItem throws
		expect(() => resetTranslationRun()).not.toThrow();

		sessionStorage.removeItem = origRemoveItem;
	});

	it('getStoredActiveRun should return null for corrupted JSON', async () => {
		const origGetItem = sessionStorage.getItem;
		sessionStorage.getItem = vi.fn(() => 'not-valid-json{{{');

		const { getStoredActiveRun } = await import('../translationRun.svelte.js');

		const result = getStoredActiveRun();
		expect(result).toBeNull();

		sessionStorage.getItem = origGetItem;
	});

	it('getStoredActiveRun should return null when runId is missing in stored data', async () => {
		const origGetItem = sessionStorage.getItem;
		sessionStorage.getItem = vi.fn(() => JSON.stringify({ jobId: 'job-1' }));

		const { getStoredActiveRun } = await import('../translationRun.svelte.js');

		const result = getStoredActiveRun();
		expect(result).toBeNull();

		sessionStorage.getItem = origGetItem;
	});

	// ── Timeout handling ──────────────────────────────────────────

	it('timeout should trigger after 10 minutes and mark as failed', async () => {
		vi.useFakeTimers();

		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-timeout');
		expect(translationRunStore.value.uxState).toBe('running');

		// Advance past the 10-minute timeout
		vi.advanceTimersByTime(600_000);

		expect(translationRunStore.value.uxState).toBe('failed');
		expect(translationRunStore.value.runId).toBe('run-timeout');

		vi.useRealTimers();
	});

	it('stopTranslationRun should clear timeout timer', async () => {
		vi.useFakeTimers();

		const { translationRunStore, startTranslationRun, stopTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-timeout-clear');
		stopTranslationRun();

		// Advance past timeout — should NOT trigger state change
		vi.advanceTimersByTime(600_000);
		expect(translationRunStore.value.uxState).toBe('running');

		vi.useRealTimers();
	});

	// ── _handleWsFailure edge cases ───────────────────────────────

	it('_handleWsFailure should not reconnect when already in terminal state', async () => {
		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-terminal-no-reconnect');

		// Manually set state to completed
		translationRunStore.set({ ...translationRunStore.value, uxState: 'completed' });

		// Trigger onclose with abnormal code
		mockWs.onclose!(new CloseEvent('close', { code: 1006, reason: 'Abnormal' }));

		// State should remain completed
		expect(translationRunStore.value.uxState).toBe('completed');
	});

	it('_handleWsFailure with max reconnects should mark as failed', async () => {
		vi.useFakeTimers();

		const { translationRunStore, startTranslationRun } = await import(
			'../translationRun.svelte.js'
		);

		startTranslationRun('run-max-reconnects');

		// Exhaust 3 reconnects by triggering abnormal close 4 times
		for (let i = 0; i < 4; i++) {
			mockWs.onclose!(new CloseEvent('close', { code: 1006, reason: 'Abnormal' }));
			// Advance past reconnect delay to trigger the actual reconnect
			if (i < 3) {
				vi.advanceTimersByTime(10_000);
				// After reconnect, a new mock WS would be created — in our test the mock is reused
			}
		}

		// After max reconnects exhausted, state should be failed
		expect(translationRunStore.value.uxState).toBe('failed');

		vi.useRealTimers();
	});

	// ── Complete callback handling ────────────────────────────────

	it('should call onComplete callback on terminal WebSocket message', async () => {
		const onComplete = vi.fn();
		const { startTranslationRun } = await import('../translationRun.svelte.js');

		startTranslationRun('run-complete-cb', { onComplete });

		mockWs.onmessage!(
			new MessageEvent('message', {
				data: JSON.stringify({
					status: 'COMPLETED',
					total_records: 5,
					successful_records: 5,
					failed_records: 0,
					skipped_records: 0,
				}),
			}),
		);

		expect(onComplete).toHaveBeenCalledTimes(1);
		expect(onComplete).toHaveBeenCalledWith(
			expect.objectContaining({ status: 'COMPLETED' }),
		);
	});

	// ── _cleanup without active WS ───────────────────────────────

	it('stopTranslationRun should work when no WebSocket is active', async () => {
		const { stopTranslationRun } = await import('../translationRun.svelte.js');

		// Should not throw
		stopTranslationRun();
	});

	// ── update_translation_run_state ──────────────────────────────

	it('updateTranslationRunState should clear error and set failed uxState', async () => {
		const { translationRunStore, startTranslationRun, updateTranslationRunState } =
			await import('../translationRun.svelte.js');

		startTranslationRun('run-update-state');
		updateTranslationRunState({ uxState: 'failed' });

		expect(translationRunStore.value.uxState).toBe('failed');
	});
});
// #endregion Test.TranslationRun
