/**
 * Sync state management
 *
 * Tracks sync status, pending mutations, and mutation queue for retry.
 */

import { atom } from 'jotai';
import type { SyncStatus, QueuedMutation, QueuedMutationType, Json } from './types';
import { createDebug } from '../utils/debug';

const debug = createDebug('sync');

// --- Core State Atoms ---

/**
 * Current sync status
 */
export const syncStatusAtom = atom<SyncStatus>('synced');

/**
 * Number of pending (in-flight) mutations
 */
export const pendingMutationsCountAtom = atom(0);

/**
 * Network online status
 */
export const isOnlineAtom = atom(typeof navigator !== 'undefined' ? navigator.onLine : true);

/**
 * Last sync error message
 */
export const lastSyncErrorAtom = atom<string | null>(null);

/**
 * Last successful sync timestamp
 */
export const lastSyncTimeAtom = atom<number>(Date.now());

/**
 * Queued mutations for retry
 */
export const mutationQueueAtom = atom<QueuedMutation[]>([]);

// --- Derived State ---

/**
 * Combined sync state for easy consumption
 */
export const syncStateAtom = atom((get) => ({
	status: get(syncStatusAtom),
	pendingMutations: get(pendingMutationsCountAtom),
	lastError: get(lastSyncErrorAtom),
	lastSyncTime: get(lastSyncTimeAtom),
	isOnline: get(isOnlineAtom),
	queuedMutations: get(mutationQueueAtom).length,
}));

// --- Mutation Tracking Actions ---

/**
 * Start tracking a mutation (increment counter)
 */
export const startMutationAtom = atom(null, (get, set) => {
	const currentCount = get(pendingMutationsCountAtom);
	const newCount = currentCount + 1;
	set(pendingMutationsCountAtom, newCount);
	debug('Mutation started. Pending count: %d -> %d', currentCount, newCount);

	if (newCount > 0 && get(syncStatusAtom) !== 'syncing') {
		set(syncStatusAtom, 'syncing');
		debug('Status -> syncing');
	}
});

/**
 * Complete a mutation (decrement counter)
 */
export const completeMutationAtom = atom(null, (get, set, success: boolean = true) => {
	const currentCount = get(pendingMutationsCountAtom);
	const newCount = Math.max(0, currentCount - 1);
	set(pendingMutationsCountAtom, newCount);
	debug('Mutation completed (success: %s). Pending count: %d -> %d', success, currentCount, newCount);

	if (success) {
		set(lastSyncTimeAtom, Date.now());
		if (newCount === 0) {
			set(lastSyncErrorAtom, null);
		}
	}

	// Update status if no more pending mutations
	if (newCount === 0) {
		const isOnline = get(isOnlineAtom);
		const hasError = get(lastSyncErrorAtom) !== null;

		if (hasError) {
			set(syncStatusAtom, 'error');
			debug('Status -> error');
		} else if (!isOnline) {
			set(syncStatusAtom, 'offline');
			debug('Status -> offline');
		} else {
			set(syncStatusAtom, 'synced');
			debug('Status -> synced');
		}
	}
});

/**
 * Track a mutation error
 */
export const trackMutationErrorAtom = atom(null, (_get, set, error: string) => {
	set(lastSyncErrorAtom, error);
	debug('Mutation failed: %s', error);
});

// --- Network Status ---

/**
 * Set online/offline status
 */
export const setOnlineStatusAtom = atom(null, (get, set, isOnline: boolean) => {
	set(isOnlineAtom, isOnline);

	const pendingCount = get(pendingMutationsCountAtom);
	const hasError = get(lastSyncErrorAtom) !== null;
	const queueLength = get(mutationQueueAtom).length;

	if (pendingCount === 0) {
		if (hasError || queueLength > 0) {
			set(syncStatusAtom, 'error');
		} else {
			set(syncStatusAtom, isOnline ? 'synced' : 'offline');
		}
	}
});

// --- Mutation Queue ---

/**
 * Add a mutation to the retry queue
 */
export const queueMutationAtom = atom(null, (get, set, mutation: { type: QueuedMutationType; payload: { [key: string]: Json | undefined }; maxRetries?: number }) => {
	const queue = get(mutationQueueAtom);
	const newMutation: QueuedMutation = {
		...mutation,
		id: crypto.randomUUID(),
		timestamp: Date.now(),
		retryCount: 0,
		maxRetries: mutation.maxRetries ?? 3,
	};

	const newQueue = [...queue, newMutation];
	set(mutationQueueAtom, newQueue);

	debug('Queued mutation: %s. Queue size: %d', mutation.type, newQueue.length);

	if (get(pendingMutationsCountAtom) === 0) {
		set(syncStatusAtom, 'error');
	}

	return newMutation.id;
});

/**
 * Remove a mutation from the queue
 */
export const dequeueMutationAtom = atom(null, (get, set, mutationId: string) => {
	const queue = get(mutationQueueAtom);
	const newQueue = queue.filter((m) => m.id !== mutationId);
	set(mutationQueueAtom, newQueue);

	debug('Dequeued mutation: %s. Queue size: %d', mutationId, newQueue.length);

	if (newQueue.length === 0 && get(pendingMutationsCountAtom) === 0 && get(lastSyncErrorAtom) === null) {
		set(syncStatusAtom, get(isOnlineAtom) ? 'synced' : 'offline');
	}
});

/**
 * Increment retry count for a mutation
 */
export const incrementRetryCountAtom = atom(null, (get, set, mutationId: string) => {
	const queue = get(mutationQueueAtom);
	const newQueue = queue.map((m) => (m.id === mutationId ? { ...m, retryCount: m.retryCount + 1 } : m));
	set(mutationQueueAtom, newQueue);
});

/**
 * Get the next mutation to retry
 */
export const getNextQueuedMutationAtom = atom((get) => {
	const queue = get(mutationQueueAtom);
	return queue.find((m) => m.retryCount < m.maxRetries) ?? null;
});

/**
 * Clear all queued mutations
 */
export const clearMutationQueueAtom = atom(null, (get, set) => {
	set(mutationQueueAtom, []);
	debug('Cleared mutation queue');

	if (get(pendingMutationsCountAtom) === 0 && get(lastSyncErrorAtom) === null) {
		set(syncStatusAtom, get(isOnlineAtom) ? 'synced' : 'offline');
	}
});
