/**
 * Integration Tests: Offline Sync End-to-End
 * Story 3.3
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { db } from '../lib/db';
import { SyncManager } from '../services/sync-manager';
import { useOfflineStore } from '../lib/store/offline-store';

describe('Story 3.3: Offline Sync End-to-End Integration', () => {
  beforeEach(async () => {
    // Clear all tables before each test
    await db.syncQueue.clear();
    await db.drafts.clear();
    await db.chatLogs.clear();
  });

  describe('Offline -> Online Sync Flow', () => {
    it('should process queued actions when connection restored', async () => {
      // Create a draft first
      const draftId = await db.drafts.add({
        sessionId: 'test-session',
        title: 'Test Draft to Delete',
        content: 'Test content',
        tags: [],
        createdAt: Date.now(),
        status: 'draft',
      });

      // Queue delete action while "offline"
      await SyncManager.queueAction('deleteDraft', { draftId });

      // Verify draft exists and queue has item
      expect(await db.drafts.get(draftId)).toBeDefined();
      const pendingBefore = await db.syncQueue.where('status').equals('pending').count();
      expect(pendingBefore).toBe(1);

      // Process queue (simulates coming back online)
      await SyncManager.processQueue();

      // Draft should be deleted
      expect(await db.drafts.get(draftId)).toBeUndefined();

      // Queue should be empty
      const pendingAfter = await db.syncQueue.where('status').equals('pending').count();
      expect(pendingAfter).toBe(0);
    });

    it('should update offline store when going online/offline', async () => {
      // Add a pending item
      await SyncManager.queueAction('saveDraft', {
        draftData: {
          sessionId: 'test',
          title: 'Test',
          content: 'Test',
          tags: [],
          createdAt: Date.now(),
          status: 'draft',
        },
      });

      // Simulate going offline
      useOfflineStore.setState({ isOnline: false });
      expect(useOfflineStore.getState().isOnline).toBe(false);

      // Simulate going online - should update pending count
      useOfflineStore.setState({ isOnline: true });
      await useOfflineStore.getState().updatePendingCount();

      expect(useOfflineStore.getState().isOnline).toBe(true);
      expect(useOfflineStore.getState().pendingCount).toBe(1);
    });
  });

  describe('Error Handling', () => {
    it('should handle sync failures gracefully', async () => {
      // Queue an action that will fail (non-existent draft)
      await SyncManager.queueAction('deleteDraft', { draftId: 999 });

      // Process queue 3 times (simulating 3 reconnection attempts)
      // Each attempt will retry once, incrementing the retry counter
      for (let i = 0; i < 3; i++) {
        await expect(SyncManager.processQueue()).resolves.not.toThrow();
      }

      // Item should be marked as failed after 3 retries
      const items = await db.syncQueue.toArray();
      expect(items).toHaveLength(1);
      expect(items[0].status).toBe('failed');
      expect(items[0].retries).toBe(3);
    });

    it('should store error message for failed items', async () => {
      // Queue a delete action that will fail (draft doesn't exist)
      await db.syncQueue.add({
        action: 'deleteDraft',
        payload: { draftId: 999 },
        status: 'pending',
        createdAt: Date.now(),
        retries: 0,
      });

      // Process queue 3 times to exhaust retries
      for (let i = 0; i < 3; i++) {
        await SyncManager.processQueue();
      }

      const items = await db.syncQueue.toArray();
      expect(items.length).toBeGreaterThan(0);
      expect(items[0].lastError).toBeDefined();
    });
  });

  describe('Multiple Actions in Queue', () => {
    it('should process multiple actions in order', async () => {
      // Create drafts
      const draft1 = await db.drafts.add({
        sessionId: 's1',
        title: 'Draft 1',
        content: 'Content 1',
        tags: [],
        createdAt: Date.now(),
        status: 'draft',
      });

      const draft2 = await db.drafts.add({
        sessionId: 's2',
        title: 'Draft 2',
        content: 'Content 2',
        tags: [],
        createdAt: Date.now(),
        status: 'draft',
      });

      // Queue actions with slight delay to ensure order
      await SyncManager.queueAction('deleteDraft', { draftId: draft1 });
      await new Promise(resolve => setTimeout(resolve, 10));
      await SyncManager.queueAction('deleteDraft', { draftId: draft2 });

      const draftsBefore = await db.drafts.toArray();
      expect(draftsBefore).toHaveLength(2);

      await SyncManager.processQueue();

      // Both drafts should be deleted
      const draftsAfter = await db.drafts.toArray();
      expect(draftsAfter).toHaveLength(0);
    });
  });
});
