// #region TranslationRunStore [C:4] [TYPE Store] [SEMANTICS translate, run, progress, websocket, polling, store]
// @BRIEF Global store for active translation run progress — uses WebSocket for real-time
//        progress + polling fallback for structured status. Survives page navigation.
// @LAYER UI
// @RELATION DEPENDS_ON -> [TranslateRunsApi.fetchRunStatus]
// @RELATION DEPENDS_ON -> [ApiModule.getTranslateRunWsUrl]
// @UX_STATE idle -> No active run
// @UX_STATE running -> Polling + WebSocket active, progress bar updates in real-time
// @UX_STATE completed/partial/failed/insert_failed/cancelled -> Terminal states
// @UX_FEEDBACK WebSocket provides real-time batch completion events during translate phase
// @UX_FEEDBACK Polling provides structured status updates (insert phase, final counts)
// @UX_RECOVERY WebSocket disconnect -> continues with polling only
// @RATIONALE Migrated from Svelte 4 writable+derived to Svelte 5 $state+$derived runes.
//            Backward-compatible .subscribe() retained for $translationRunStore syntax.
//            WebSocket and polling logic preserved exactly.

import { fetchRunStatus } from '$lib/api/translate';
import { getTranslateRunWsUrl } from '$lib/api';

export type UxState = 'idle' | 'running' | 'inserting' | 'completed' | 'partial' | 'failed' | 'insert_failed' | 'cancelled';

export interface TranslationRunState {
	runId: string | null;
	uxState: UxState;
	status: Record<string, unknown> | null;
	totalRecords: number;
	successfulRecords: number;
	failedRecords: number;
	skippedRecords: number;
	progressPct: number;
	insertStatus: string | null;
	batchCount: number;
	jobId: string | null;
	isFullRun: boolean;
}

export interface StartRunOptions {
	jobId?: string;
	isFullRun?: boolean;
	onComplete?: (data: Record<string, unknown>) => void;
}

const initialState: TranslationRunState = {
	runId: null,
	uxState: 'idle',
	status: null,
	totalRecords: 0,
	successfulRecords: 0,
	failedRecords: 0,
	skippedRecords: 0,
	progressPct: 0,
	insertStatus: null,
	batchCount: 0,
	jobId: null,
	isFullRun: false,
};

// Reactive state (Svelte 5 rune)
let _state: TranslationRunState = $state(initialState);

// Derived values (Svelte 5 rune)
let _isTranslationActive = $derived(_state.uxState === 'running' || _state.uxState === 'inserting');
let _isTranslationFinished = $derived(
	_state.uxState === 'completed' ||
	_state.uxState === 'partial' ||
	_state.uxState === 'failed' ||
	_state.uxState === 'insert_failed' ||
	_state.uxState === 'cancelled',
);

// Subscriber list for backward compat with $translationRunStore syntax
let _subscribers: Array<(value: TranslationRunState) => void> = [];

function _notify(): void {
	const current = _state;
	_subscribers.forEach((fn) => fn(current));
}

export const translationRunStore = {
	subscribe(run: (value: TranslationRunState) => void): () => void {
		_subscribers.push(run);
		run(_state);
		return () => {
			_subscribers = _subscribers.filter((fn) => fn !== run);
		};
	},
	get value(): TranslationRunState {
		return _state;
	},
	set(newState: TranslationRunState) {
		_state = newState;
		_notify();
	},
	update(fn: (state: TranslationRunState) => TranslationRunState) {
		_state = fn(_state);
		_notify();
	},
	get isTranslationActive(): boolean {
		return _isTranslationActive;
	},
	get isTranslationFinished(): boolean {
		return _isTranslationFinished;
	},
};

let _pollingInterval: ReturnType<typeof setInterval> | null = null;
let _pollCount = 0;
const MAX_POLLS = 300;
let _onCompleteCallback: ((data: Record<string, unknown>) => void) | null = null;
let _ws: WebSocket | null = null;

export function clearOnCompleteCallback(): void {
	_onCompleteCallback = null;
}

export function startTranslationRun(runId: string, options: StartRunOptions = {}): void {
	if (!runId) return;

	stopTranslationRun();

	_state = {
		...initialState,
		runId,
		uxState: 'running',
		jobId: options.jobId || null,
		isFullRun: options.isFullRun || false,
	};
	_notify();

	_onCompleteCallback = options.onComplete || null;

	_connectWebSocket(runId);

	_pollCount = 0;
	_pollingInterval = setInterval(pollStatus, 2000);
	pollStatus();
}

function _connectWebSocket(runId: string): void {
	try {
		const wsUrl = getTranslateRunWsUrl(runId);
		_ws = new WebSocket(wsUrl);

		_ws.onmessage = (event: MessageEvent) => {
			try {
				const data = JSON.parse(event.data) as Record<string, unknown>;
				if (data.error) return;
				const s = data.status as string;
				const total = (data.total_records as number) || 0;
				const done = ((data.successful_records as number) || 0) + ((data.failed_records as number) || 0) + ((data.skipped_records as number) || 0);
				const pct = total > 0 ? Math.round((done / total) * 100) : 0;
				const insertS = data.insert_status as string;
				const newUxState: UxState | null =
					s === 'PENDING' || s === 'RUNNING'
						? (insertS === 'started' || insertS === 'pending' || insertS === 'running' ? 'inserting' : 'running')
						: s === 'COMPLETED'
							? (insertS === 'failed' || insertS === 'timeout' ? 'insert_failed' : ((data.failed_records as number) > 0 ? 'partial' : 'completed'))
							: s === 'FAILED' ? 'failed'
							: s === 'CANCELLED' ? 'cancelled'
							: null;
				if (newUxState) {
					_state = {
						..._state,
						uxState: newUxState,
						status: data,
						totalRecords: total,
						successfulRecords: (data.successful_records as number) || 0,
						failedRecords: (data.failed_records as number) || 0,
						skippedRecords: (data.skipped_records as number) || 0,
						progressPct: pct,
						insertStatus: insertS || null,
						batchCount: (data.batch_count as number) || 0,
					};
					_notify();
					if (newUxState !== 'running' && newUxState !== 'inserting') {
						if (_onCompleteCallback) _onCompleteCallback(data);
					}
				}
			} catch (_e) {
				// Ignore parse errors
			}
		};

		_ws.onerror = () => {
			_ws = null;
		};

		_ws.onclose = () => {
			_ws = null;
		};
	} catch (_e) {
		_ws = null;
	}
}

export function stopTranslationRun(): void {
	if (_ws) {
		_ws.onclose = null;
		_ws.close();
		_ws = null;
	}
	if (_pollingInterval) {
		clearInterval(_pollingInterval);
		_pollingInterval = null;
	}
}

export function resetTranslationRun(): void {
	stopTranslationRun();
	_state = initialState;
	_notify();
	_onCompleteCallback = null;
}

export function updateTranslationRunState(state: Partial<TranslationRunState>): void {
	_state = { ..._state, ...state };
	_notify();
}

async function pollStatus(): Promise<void> {
	_pollCount++;
	if (_pollCount > MAX_POLLS) {
		stopTranslationRun();
		_state = { ..._state, uxState: 'failed' };
		_notify();
		if (_onCompleteCallback) _onCompleteCallback({ status: 'TIMEOUT', error_message: 'Translation run timed out' });
		return;
	}

	if (!_state.runId) {
		stopTranslationRun();
		return;
	}

	try {
		const data = (await fetchRunStatus(_state.runId)) as Record<string, unknown>;
		if (!data) return;

		const s = data?.status as string;
		const insertS = data?.insert_status as string;

		let newUxState: UxState = _state.uxState;

		if (s === 'PENDING' || s === 'RUNNING') {
			newUxState = (insertS === 'started' || insertS === 'pending' || insertS === 'running') ? 'inserting' : 'running';
		} else if (s === 'COMPLETED') {
			newUxState = (insertS === 'failed' || insertS === 'timeout') ? 'insert_failed'
				: (data.failed_records as number) > 0 ? 'partial' : 'completed';
			stopTranslationRun();
		} else if (s === 'FAILED') {
			newUxState = 'failed';
			stopTranslationRun();
		} else if (s === 'CANCELLED') {
			newUxState = 'cancelled';
			stopTranslationRun();
		}

		const total = (data?.total_records as number) || 0;
		const pct = total > 0
			? Math.round((((data.successful_records as number) + (data.failed_records as number) + (data.skipped_records as number)) / total) * 100)
			: 0;

		_state = {
			..._state,
			uxState: newUxState,
			status: data,
			totalRecords: total,
			successfulRecords: (data?.successful_records as number) || 0,
			failedRecords: (data?.failed_records as number) || 0,
			skippedRecords: (data?.skipped_records as number) || 0,
			progressPct: pct,
			insertStatus: (data?.insert_status as string) || null,
			batchCount: (data?.batch_count as number) || 0,
		};
		_notify();

		if (newUxState !== 'running' && newUxState !== 'inserting') {
			if (_onCompleteCallback) _onCompleteCallback(data);
		}
	} catch (_err) {
		// Poll error — will retry on next interval
	}
}
// #endregion TranslationRunStore
