import { describe, it, expect, beforeEach } from 'vitest';
import { createStore } from 'jotai';
import {
	syncStatusAtom,
	pendingMutationsCountAtom,
	isOnlineAtom,
	lastSyncErrorAtom,
	lastSyncTimeAtom,
	mutationQueueAtom,
	syncStateAtom,
	startMutationAtom,
	completeMutationAtom,
	trackMutationErrorAtom,
	setOnlineStatusAtom,
	queueMutationAtom,
	dequeueMutationAtom,
	incrementRetryCountAtom,
	getNextQueuedMutationAtom,
	clearMutationQueueAtom,
} from '../core/sync-store';

function freshStore() {
	const store = createStore();
	// Reset atoms to defaults
	store.set(syncStatusAtom, 'synced');
	store.set(pendingMutationsCountAtom, 0);
	store.set(isOnlineAtom, true);
	store.set(lastSyncErrorAtom, null);
	store.set(mutationQueueAtom, []);
	return store;
}

describe('sync-store', () => {
	let store: ReturnType<typeof createStore>;

	beforeEach(() => {
		store = freshStore();
	});

	describe('syncStateAtom', () => {
		it('returns combined sync state', () => {
			const state = store.get(syncStateAtom);
			expect(state.status).toBe('synced');
			expect(state.pendingMutations).toBe(0);
			expect(state.lastError).toBeNull();
			expect(state.isOnline).toBe(true);
			expect(state.queuedMutations).toBe(0);
		});
	});

	describe('startMutationAtom / completeMutationAtom', () => {
		it('increments pending count and sets syncing', () => {
			store.set(startMutationAtom);
			expect(store.get(pendingMutationsCountAtom)).toBe(1);
			expect(store.get(syncStatusAtom)).toBe('syncing');
		});

		it('decrements pending count on complete', () => {
			store.set(startMutationAtom);
			store.set(completeMutationAtom, true);
			expect(store.get(pendingMutationsCountAtom)).toBe(0);
			expect(store.get(syncStatusAtom)).toBe('synced');
		});

		it('sets error status when error exists after completion', () => {
			store.set(startMutationAtom);
			store.set(trackMutationErrorAtom, 'Something failed');
			store.set(completeMutationAtom, false);
			expect(store.get(syncStatusAtom)).toBe('error');
		});

		it('clears error on successful last mutation', () => {
			store.set(startMutationAtom);
			store.set(trackMutationErrorAtom, 'err');
			store.set(completeMutationAtom, true);
			expect(store.get(lastSyncErrorAtom)).toBeNull();
		});

		it('does not go below zero', () => {
			store.set(completeMutationAtom, true);
			expect(store.get(pendingMutationsCountAtom)).toBe(0);
		});
	});

	describe('setOnlineStatusAtom', () => {
		it('sets offline status when no pending mutations', () => {
			store.set(setOnlineStatusAtom, false);
			expect(store.get(isOnlineAtom)).toBe(false);
			expect(store.get(syncStatusAtom)).toBe('offline');
		});

		it('restores synced status when going online', () => {
			store.set(setOnlineStatusAtom, false);
			store.set(setOnlineStatusAtom, true);
			expect(store.get(syncStatusAtom)).toBe('synced');
		});
	});

	describe('mutation queue', () => {
		it('queues a mutation', () => {
			store.set(queueMutationAtom, { type: 'UPDATE_NODE', payload: { nodeId: 'n1' } });
			expect(store.get(mutationQueueAtom).length).toBe(1);
		});

		it('dequeues a mutation', () => {
			store.set(queueMutationAtom, { type: 'UPDATE_NODE', payload: { nodeId: 'n1' } });
			const id = store.get(mutationQueueAtom)[0].id;
			store.set(dequeueMutationAtom, id);
			expect(store.get(mutationQueueAtom).length).toBe(0);
		});

		it('increments retry count', () => {
			store.set(queueMutationAtom, { type: 'UPDATE_NODE', payload: { nodeId: 'n1' } });
			const id = store.get(mutationQueueAtom)[0].id;
			store.set(incrementRetryCountAtom, id);
			expect(store.get(mutationQueueAtom)[0].retryCount).toBe(1);
		});

		it('getNextQueuedMutationAtom returns first retryable', () => {
			store.set(queueMutationAtom, { type: 'UPDATE_NODE', payload: { nodeId: 'n1' }, maxRetries: 3 });
			const next = store.get(getNextQueuedMutationAtom);
			expect(next).not.toBeNull();
			expect(next!.retryCount).toBe(0);
		});

		it('getNextQueuedMutationAtom returns null when maxed out', () => {
			store.set(queueMutationAtom, { type: 'UPDATE_NODE', payload: { nodeId: 'n1' }, maxRetries: 0 });
			const next = store.get(getNextQueuedMutationAtom);
			expect(next).toBeNull();
		});

		it('clearMutationQueueAtom empties queue', () => {
			store.set(queueMutationAtom, { type: 'UPDATE_NODE', payload: { nodeId: 'n1' } });
			store.set(queueMutationAtom, { type: 'DELETE_NODE', payload: { nodeId: 'n2' } });
			store.set(clearMutationQueueAtom);
			expect(store.get(mutationQueueAtom).length).toBe(0);
		});
	});
});
