import { test, expect } from '../../fixtures/webdav.fixture';
import { SyncPage } from '../../pages/sync.page';
import { WorkViewPage } from '../../pages/work-view.page';
import { waitForStatePersistence } from '../../utils/waits';
import {
  WEBDAV_CONFIG_TEMPLATE,
  createSyncFolder,
  waitForSyncComplete,
  generateSyncFolderName,
} from '../../utils/sync-helpers';
import {
  createLegacyMigratedClient,
  closeLegacyClient,
} from '../../utils/legacy-migration-helpers';

// Import fixtures
import legacyDataClientA from '../../fixtures/legacy-migration-client-a.json';
import legacyDataClientB from '../../fixtures/legacy-migration-client-b.json';
import legacyDataCollisionA from '../../fixtures/legacy-migration-collision-a.json';
import legacyDataCollisionB from '../../fixtures/legacy-migration-collision-b.json';

/**
 * WebDAV Legacy Migration Sync E2E Tests
 *
 * Tests scenarios where BOTH clients have migrated from old Super Productivity
 * (pre-operation-log format) and then sync via WebDAV.
 *
 * This tests a gap in coverage: what happens when two clients with independent
 * legacy data both migrate and then try to sync to the same WebDAV folder.
 *
 * Run with: npm run e2e:file e2e/tests/sync/webdav-legacy-migration-sync.spec.ts -- --retries=0
 */
test.describe('@webdav @migration WebDAV Legacy Migration Sync', () => {
  // Tests use unique sync folders — run in parallel

  /**
   * Test: Both clients migrated from legacy - Keep local resolution
   *
   * Scenario:
   * 1. Client A has legacy data (Task A1, Task A2), migrates, syncs to WebDAV
   * 2. Client B has different legacy data (Task B1, Task B2), migrates
   * 3. Client B sets up WebDAV sync to same folder -> conflict dialog appears
   * 4. Client B chooses "Keep local" -> B's data replaces remote
   * 5. Client A syncs -> receives B's data
   */
  test('both clients migrated from legacy - Keep local resolution', async ({
    browser,
    baseURL,
    request,
  }) => {
    test.slow(); // Migration + sync tests take longer
    const SYNC_FOLDER_NAME = generateSyncFolderName('e2e-legacy-mig-local');
    await createSyncFolder(request, SYNC_FOLDER_NAME);
    const WEBDAV_CONFIG = {
      ...WEBDAV_CONFIG_TEMPLATE,
      syncFolderPath: `/${SYNC_FOLDER_NAME}`,
    };
    const url = baseURL || 'http://localhost:4242';

    let clientA: {
      context: Awaited<ReturnType<typeof browser.newContext>>;
      page: Awaited<ReturnType<typeof browser.newPage>>;
    } | null = null;
    let clientB: {
      context: Awaited<ReturnType<typeof browser.newContext>>;
      page: Awaited<ReturnType<typeof browser.newPage>>;
    } | null = null;

    try {
      // === Client A: Legacy migration + sync setup ===
      console.log('[Test] Creating Client A with legacy data...');
      clientA = await createLegacyMigratedClient(
        browser,
        url,
        legacyDataClientA.data,
        'A',
      );
      const syncPageA = new SyncPage(clientA.page);
      const workViewA = new WorkViewPage(clientA.page);

      // Navigate to the project by clicking in sidebar (more reliable than URL navigation)
      const sidenavA = clientA.page.locator('magic-side-nav');
      await sidenavA.locator('nav-item', { hasText: 'Client A Project' }).click();

      await workViewA.waitForTaskList();

      // Verify Client A has its migrated data
      await expect(clientA.page.locator('task', { hasText: 'Task A1' })).toBeVisible({
        timeout: 10000,
      });
      await expect(clientA.page.locator('task', { hasText: 'Task A2' })).toBeVisible();
      console.log('[Test] Client A verified: has migrated tasks');

      // Setup sync and upload
      await syncPageA.setupWebdavSync(WEBDAV_CONFIG);
      await waitForStatePersistence(clientA.page);
      await syncPageA.triggerSync();
      await waitForSyncComplete(clientA.page, syncPageA);
      console.log('[Test] Client A: Data uploaded to WebDAV');

      // === Client B: Legacy migration (different data) ===
      console.log('[Test] Creating Client B with different legacy data...');
      clientB = await createLegacyMigratedClient(
        browser,
        url,
        legacyDataClientB.data,
        'B',
      );
      const syncPageB = new SyncPage(clientB.page);
      const workViewB = new WorkViewPage(clientB.page);

      // Navigate to the project by clicking in sidebar
      const sidenavB = clientB.page.locator('magic-side-nav');
      await sidenavB.locator('nav-item', { hasText: 'Client B Project' }).click();

      await workViewB.waitForTaskList();

      // Verify Client B has its migrated data
      await expect(clientB.page.locator('task', { hasText: 'Task B1' })).toBeVisible({
        timeout: 10000,
      });
      await expect(clientB.page.locator('task', { hasText: 'Task B2' })).toBeVisible();
      console.log('[Test] Client B verified: has migrated tasks');

      // Add a task after migration to create "real" operations that trigger conflict detection
      // (MIGRATION_GENESIS_IMPORT alone might be treated differently by sync logic)
      await workViewB.addTask('Task B3 - After Migration');
      await waitForStatePersistence(clientB.page);
      console.log('[Test] Client B added task after migration');

      // Setup sync - should trigger conflict dialog
      console.log('[Test] Client B setting up sync (expecting conflict)...');
      await syncPageB.setupWebdavSync(WEBDAV_CONFIG);

      // Wait for conflict dialog
      const conflictDialog = clientB.page.locator('mat-dialog-container', {
        hasText: 'Conflicting Data',
      });
      await expect(conflictDialog).toBeVisible({ timeout: 30000 });
      console.log('[Test] Conflict dialog appeared on Client B');

      // Choose "Keep local"
      const useLocalBtn = conflictDialog.locator('button', { hasText: /Keep local/i });
      await expect(useLocalBtn).toBeVisible();
      await useLocalBtn.click();
      console.log('[Test] Client B clicked "Keep local"');

      // Handle potential confirmation dialog
      const confirmDialog = clientB.page.locator('dialog-confirm');
      try {
        await confirmDialog.waitFor({ state: 'visible', timeout: 3000 });
        await confirmDialog
          .locator('button[color="warn"], button:has-text("OK")')
          .first()
          .click();
      } catch {
        // Confirmation might not appear - that's fine
      }

      await waitForSyncComplete(clientB.page, syncPageB, 30000);
      console.log('[Test] Client B sync completed');

      // Navigate to B's project and verify data
      await sidenavB.locator('nav-item', { hasText: 'Client B Project' }).click();

      await workViewB.waitForTaskList();

      // Verify Client B still has its data
      await expect(clientB.page.locator('task', { hasText: 'Task B1' })).toBeVisible();
      await expect(clientB.page.locator('task', { hasText: 'Task B2' })).toBeVisible();
      await expect(clientB.page.locator('task', { hasText: 'Task B3' })).toBeVisible();
      console.log('[Test] Client B verified: kept local data');

      // === Client A syncs - with divergent MIGRATION_GENESIS operations ===
      // Note: When both clients have independent MIGRATION_GENESIS_IMPORT operations,
      // they're on divergent timelines. Client A may also see a conflict.
      console.log('[Test] Client A syncing...');
      await syncPageA.triggerSync();

      // Client A might also see a conflict dialog since both clients have
      // independent MIGRATION_GENESIS_IMPORT operations (divergent timelines)
      const conflictDialogA = clientA.page.locator('mat-dialog-container', {
        hasText: 'Conflicting Data',
      });
      try {
        await conflictDialogA.waitFor({ state: 'visible', timeout: 2000 });
        console.log(
          '[Test] Client A also sees conflict dialog (expected with divergent timelines)',
        );
        // Choose "Keep remote" to adopt B's data
        const useRemoteBtn = conflictDialogA.locator('button', {
          hasText: /Keep remote/i,
        });
        await useRemoteBtn.click();
        // Handle confirmation if present
        const confirmDialogClientA = clientA.page.locator('dialog-confirm');
        try {
          await confirmDialogClientA.waitFor({ state: 'visible', timeout: 3000 });
          await confirmDialogClientA
            .locator('button[color="warn"], button:has-text("OK")')
            .first()
            .click();
        } catch {
          // OK if not present
        }
      } catch {
        console.log('[Test] Client A did not see conflict dialog - synced normally');
      }

      await waitForSyncComplete(clientA.page, syncPageA);

      // Verify Client A has some data (either its own or B's depending on conflict resolution)
      // Navigate to whatever project exists
      const projectsInSidebarA = sidenavA.locator('nav-item').filter({
        has: clientA.page.locator('[class*="project"], [data-project]'),
      });
      const projectCount = await projectsInSidebarA.count().catch(() => 0);

      if (projectCount > 0) {
        // Check if Client B Project exists after sync
        const hasBProject = await sidenavA
          .locator('nav-item', { hasText: 'Client B Project' })
          .isVisible()
          .catch(() => false);

        if (hasBProject) {
          await sidenavA.locator('nav-item', { hasText: 'Client B Project' }).click();

          await workViewA.waitForTaskList();
          await expect(clientA.page.locator('task', { hasText: 'Task B' })).toBeVisible({
            timeout: 10000,
          });
          console.log('[Test] SUCCESS: Client A received Client B data');
        } else {
          // Client A kept its own data - this is also valid
          await sidenavA.locator('nav-item', { hasText: 'Client A Project' }).click();

          await workViewA.waitForTaskList();
          await expect(clientA.page.locator('task', { hasText: 'Task A1' })).toBeVisible({
            timeout: 10000,
          });
          console.log(
            '[Test] SUCCESS: Client A kept its own data (divergent timeline scenario)',
          );
        }
      } else {
        console.log('[Test] WARNING: No projects found in sidebar after sync');
      }

      console.log(
        '[Test] PASSED: Legacy migration sync with conflict resolution completed',
      );
    } finally {
      if (clientA) await closeLegacyClient(clientA).catch(() => {});
      if (clientB) await closeLegacyClient(clientB).catch(() => {});
    }
  });

  /**
   * Test: Both clients migrated from legacy - Keep remote resolution
   *
   * Same as above but Client B chooses "Keep remote" to adopt A's data.
   */
  test('both clients migrated from legacy - Keep remote resolution', async ({
    browser,
    baseURL,
    request,
  }) => {
    test.slow();
    const SYNC_FOLDER_NAME = generateSyncFolderName('e2e-legacy-mig-remote');
    await createSyncFolder(request, SYNC_FOLDER_NAME);
    const WEBDAV_CONFIG = {
      ...WEBDAV_CONFIG_TEMPLATE,
      syncFolderPath: `/${SYNC_FOLDER_NAME}`,
    };
    const url = baseURL || 'http://localhost:4242';

    let clientA: {
      context: Awaited<ReturnType<typeof browser.newContext>>;
      page: Awaited<ReturnType<typeof browser.newPage>>;
    } | null = null;
    let clientB: {
      context: Awaited<ReturnType<typeof browser.newContext>>;
      page: Awaited<ReturnType<typeof browser.newPage>>;
    } | null = null;

    try {
      // === Client A: Legacy migration + sync setup ===
      console.log('[Test] Creating Client A with legacy data...');
      clientA = await createLegacyMigratedClient(
        browser,
        url,
        legacyDataClientA.data,
        'A',
      );
      const syncPageA = new SyncPage(clientA.page);
      const workViewA = new WorkViewPage(clientA.page);

      // Navigate to the project by clicking in sidebar
      const sidenavA = clientA.page.locator('magic-side-nav');
      await sidenavA.locator('nav-item', { hasText: 'Client A Project' }).click();

      await workViewA.waitForTaskList();

      // Verify Client A has its migrated data
      await expect(clientA.page.locator('task', { hasText: 'Task A1' })).toBeVisible({
        timeout: 10000,
      });

      // Setup sync and upload
      await syncPageA.setupWebdavSync(WEBDAV_CONFIG);
      await waitForStatePersistence(clientA.page);
      await syncPageA.triggerSync();
      await waitForSyncComplete(clientA.page, syncPageA);
      console.log('[Test] Client A: Data uploaded');

      // === Client B: Legacy migration + conflict resolution ===
      console.log('[Test] Creating Client B with different legacy data...');
      clientB = await createLegacyMigratedClient(
        browser,
        url,
        legacyDataClientB.data,
        'B',
      );
      const syncPageB = new SyncPage(clientB.page);
      const workViewB = new WorkViewPage(clientB.page);

      // Navigate to the project by clicking in sidebar
      const sidenavB = clientB.page.locator('magic-side-nav');
      await sidenavB.locator('nav-item', { hasText: 'Client B Project' }).click();

      await workViewB.waitForTaskList();

      // Verify Client B has its migrated data
      await expect(clientB.page.locator('task', { hasText: 'Task B1' })).toBeVisible({
        timeout: 10000,
      });

      // Add a task after migration to create operations that trigger conflict detection
      await workViewB.addTask('Task B3 - After Migration');
      await waitForStatePersistence(clientB.page);
      console.log('[Test] Client B added task after migration');

      // Setup sync - triggers conflict
      await syncPageB.setupWebdavSync(WEBDAV_CONFIG);

      // Wait for conflict dialog
      const conflictDialog = clientB.page.locator('mat-dialog-container', {
        hasText: 'Conflicting Data',
      });
      await expect(conflictDialog).toBeVisible({ timeout: 30000 });
      console.log('[Test] Conflict dialog appeared');

      // Choose "Keep remote" - adopt A's data
      const useRemoteBtn = conflictDialog.locator('button', { hasText: /Keep remote/i });
      await expect(useRemoteBtn).toBeVisible();
      await useRemoteBtn.click();
      console.log('[Test] Client B clicked "Keep remote"');

      // Handle potential confirmation dialog
      const confirmDialog = clientB.page.locator('dialog-confirm');
      try {
        await confirmDialog.waitFor({ state: 'visible', timeout: 3000 });
        await confirmDialog
          .locator('button[color="warn"], button:has-text("OK")')
          .first()
          .click();
      } catch {
        // Confirmation might not appear
      }

      await waitForSyncComplete(clientB.page, syncPageB, 30000);
      console.log('[Test] Client B sync completed');

      // Navigate to A's project (which should now exist on B after adopting remote data)
      await sidenavB.locator('nav-item', { hasText: 'Client A Project' }).click();

      await workViewB.waitForTaskList();

      // Verify Client B now has A's data (remote data)
      await expect(clientB.page.locator('task', { hasText: 'Task A1' })).toBeVisible({
        timeout: 10000,
      });
      await expect(clientB.page.locator('task', { hasText: 'Task A2' })).toBeVisible();
      // Client B's local tasks should be gone since we chose "Keep remote"
      await expect(
        clientB.page.locator('task', { hasText: 'Task B1' }),
      ).not.toBeVisible();
      console.log('[Test] SUCCESS: Client B adopted remote (A) data');
    } finally {
      if (clientA) await closeLegacyClient(clientA).catch(() => {});
      if (clientB) await closeLegacyClient(clientB).catch(() => {});
    }
  });

  /**
   * Test: Both clients migrated with SAME entity IDs - ID collision
   *
   * Tests what happens when both clients have the same entity IDs but different content.
   * This is an edge case that could occur if users manually copied databases.
   *
   * Scenario:
   * - Client A: SHARED_PROJECT with "Shared Task (Version A)"
   * - Client B: SHARED_PROJECT with "Shared Task (Version B)"
   * - Same IDs, different titles/content
   *
   * Expected: Winner-take-all based on conflict resolution choice
   */
  test('both clients migrated with SAME entity IDs - ID collision', async ({
    browser,
    baseURL,
    request,
  }) => {
    test.slow();
    const SYNC_FOLDER_NAME = generateSyncFolderName('e2e-legacy-collision');
    await createSyncFolder(request, SYNC_FOLDER_NAME);
    const WEBDAV_CONFIG = {
      ...WEBDAV_CONFIG_TEMPLATE,
      syncFolderPath: `/${SYNC_FOLDER_NAME}`,
    };
    const url = baseURL || 'http://localhost:4242';

    let clientA: {
      context: Awaited<ReturnType<typeof browser.newContext>>;
      page: Awaited<ReturnType<typeof browser.newPage>>;
    } | null = null;
    let clientB: {
      context: Awaited<ReturnType<typeof browser.newContext>>;
      page: Awaited<ReturnType<typeof browser.newPage>>;
    } | null = null;

    try {
      // === Client A: Legacy data with SHARED_PROJECT and shared-task-1 ===
      console.log('[Test] Creating Client A with collision fixture (Version A)...');
      clientA = await createLegacyMigratedClient(
        browser,
        url,
        legacyDataCollisionA.data,
        'A',
      );
      const syncPageA = new SyncPage(clientA.page);
      const workViewA = new WorkViewPage(clientA.page);

      // Navigate to the shared project by clicking in sidebar
      const sidenavA = clientA.page.locator('magic-side-nav');
      await sidenavA.locator('nav-item', { hasText: 'Shared Project' }).click();

      await workViewA.waitForTaskList();

      // Verify Client A has Version A data
      await expect(clientA.page.locator('task', { hasText: 'Version A' })).toBeVisible({
        timeout: 10000,
      });
      console.log('[Test] Client A verified: has Version A task');

      // Setup sync and upload
      await syncPageA.setupWebdavSync(WEBDAV_CONFIG);
      await waitForStatePersistence(clientA.page);
      await syncPageA.triggerSync();
      await waitForSyncComplete(clientA.page, syncPageA);
      console.log('[Test] Client A: Version A data uploaded');

      // === Client B: Same IDs but Version B content ===
      console.log('[Test] Creating Client B with collision fixture (Version B)...');
      clientB = await createLegacyMigratedClient(
        browser,
        url,
        legacyDataCollisionB.data,
        'B',
      );
      const syncPageB = new SyncPage(clientB.page);
      const workViewB = new WorkViewPage(clientB.page);

      // Navigate to the shared project by clicking in sidebar
      const sidenavB = clientB.page.locator('magic-side-nav');
      await sidenavB.locator('nav-item', { hasText: 'Shared Project' }).click();

      await workViewB.waitForTaskList();

      // Verify Client B has Version B data
      await expect(clientB.page.locator('task', { hasText: 'Version B' })).toBeVisible({
        timeout: 10000,
      });
      console.log('[Test] Client B verified: has Version B task');

      // Add a task after migration to create operations that trigger conflict detection
      await workViewB.addTask('Version B Extra Task');
      await waitForStatePersistence(clientB.page);
      console.log('[Test] Client B added task after migration');

      // Setup sync - triggers conflict (same IDs, different content)
      await syncPageB.setupWebdavSync(WEBDAV_CONFIG);

      // Wait for conflict dialog
      const conflictDialog = clientB.page.locator('mat-dialog-container', {
        hasText: 'Conflicting Data',
      });
      await expect(conflictDialog).toBeVisible({ timeout: 30000 });
      console.log('[Test] Conflict dialog appeared (ID collision detected)');

      // Choose "Keep local" - B's Version B should win
      const useLocalBtn = conflictDialog.locator('button', { hasText: /Keep local/i });
      await useLocalBtn.click();
      console.log('[Test] Client B clicked "Keep local"');

      // Handle confirmation
      const confirmDialog = clientB.page.locator('dialog-confirm');
      try {
        await confirmDialog.waitFor({ state: 'visible', timeout: 3000 });
        await confirmDialog
          .locator('button[color="warn"], button:has-text("OK")')
          .first()
          .click();
      } catch {
        // OK if not present
      }

      await waitForSyncComplete(clientB.page, syncPageB, 30000);
      console.log('[Test] Client B sync completed');

      // Navigate back to project and verify
      await sidenavB.locator('nav-item', { hasText: 'Shared Project' }).click();

      await workViewB.waitForTaskList();

      // Verify Client B still has the original shared task with Version B content
      // Use ID selector to target the specific shared-task-1
      await expect(clientB.page.locator('#t-shared-task-1')).toBeVisible();
      await expect(
        clientB.page.locator('#t-shared-task-1', { hasText: 'Version B' }),
      ).toBeVisible();
      // Version A should NOT be visible (same ID, B's version won)
      await expect(
        clientB.page.locator('#t-shared-task-1', { hasText: 'Version A' }),
      ).not.toBeVisible();
      // The extra task we added should also be there
      await expect(
        clientB.page.locator('task', { hasText: 'Version B Extra Task' }),
      ).toBeVisible();

      // Verify 2 tasks: shared-task-1 (Version B) + extra task we added
      const taskCount = await clientB.page.locator('task').count();
      expect(taskCount).toBe(2);
      console.log('[Test] Verified: No ID duplicates, winner-take-all for shared-task-1');

      // === Client A syncs - with divergent timelines ===
      console.log('[Test] Client A syncing...');
      await syncPageA.triggerSync();

      // Client A might also see a conflict dialog
      const conflictDialogA = clientA.page.locator('mat-dialog-container', {
        hasText: 'Conflicting Data',
      });
      try {
        await conflictDialogA.waitFor({ state: 'visible', timeout: 2000 });
        console.log('[Test] Client A sees conflict dialog - choosing Keep remote');
        const useRemoteBtn = conflictDialogA.locator('button', {
          hasText: /Keep remote/i,
        });
        await useRemoteBtn.click();
        const confirmDialogA = clientA.page.locator('dialog-confirm');
        try {
          await confirmDialogA.waitFor({ state: 'visible', timeout: 3000 });
          await confirmDialogA
            .locator('button[color="warn"], button:has-text("OK")')
            .first()
            .click();
        } catch {
          // OK
        }
      } catch {
        console.log('[Test] Client A did not see conflict dialog');
      }

      await waitForSyncComplete(clientA.page, syncPageA);

      // Navigate to shared project and verify
      await sidenavA.locator('nav-item', { hasText: 'Shared Project' }).click();

      await workViewA.waitForTaskList();

      // With divergent MIGRATION_GENESIS_IMPORT operations, Client A may keep its
      // own data or receive Client B's data depending on sync logic.
      // The key assertion is: no ID duplicates - only ONE version of shared-task-1 exists.
      await expect(clientA.page.locator('#t-shared-task-1')).toBeVisible({
        timeout: 10000,
      });

      // Check which version Client A has
      const hasVersionB = await clientA.page
        .locator('#t-shared-task-1', { hasText: 'Version B' })
        .isVisible()
        .catch(() => false);
      const hasVersionA = await clientA.page
        .locator('#t-shared-task-1', { hasText: 'Version A' })
        .isVisible()
        .catch(() => false);

      // Only ONE version should exist (no duplicates)
      expect(hasVersionA || hasVersionB).toBe(true);
      expect(hasVersionA && hasVersionB).toBe(false); // Can't have both

      if (hasVersionB) {
        console.log(
          '[Test] SUCCESS: ID collision resolved - Client A received Version B',
        );
      } else {
        console.log(
          '[Test] SUCCESS: ID collision handled - Client A kept Version A (divergent timeline)',
        );
      }
    } finally {
      if (clientA) await closeLegacyClient(clientA).catch(() => {});
      if (clientB) await closeLegacyClient(clientB).catch(() => {});
    }
  });

  /**
   * Test: Archive data is preserved after migration + sync
   *
   * Verifies that archived tasks from legacy data survive the migration
   * process and can be synced to other clients.
   */
  test('verify archive data is preserved after migration + sync', async ({
    browser,
    baseURL,
    request,
  }) => {
    test.slow();
    const SYNC_FOLDER_NAME = generateSyncFolderName('e2e-legacy-archive');
    await createSyncFolder(request, SYNC_FOLDER_NAME);
    const WEBDAV_CONFIG = {
      ...WEBDAV_CONFIG_TEMPLATE,
      syncFolderPath: `/${SYNC_FOLDER_NAME}`,
    };
    const url = baseURL || 'http://localhost:4242';

    let clientA: {
      context: Awaited<ReturnType<typeof browser.newContext>>;
      page: Awaited<ReturnType<typeof browser.newPage>>;
    } | null = null;
    let clientB: {
      context: Awaited<ReturnType<typeof browser.newContext>>;
      page: Awaited<ReturnType<typeof browser.newPage>>;
    } | null = null;

    try {
      // === Client A: Legacy migration with archived task ===
      console.log('[Test] Creating Client A with legacy data (including archive)...');
      clientA = await createLegacyMigratedClient(
        browser,
        url,
        legacyDataClientA.data,
        'A',
      );
      const syncPageA = new SyncPage(clientA.page);
      const workViewA = new WorkViewPage(clientA.page);

      // Navigate to the project by clicking in sidebar
      const sidenavA = clientA.page.locator('magic-side-nav');
      await sidenavA.locator('nav-item', { hasText: 'Client A Project' }).click();

      await workViewA.waitForTaskList();

      // Verify active tasks are visible
      await expect(clientA.page.locator('task', { hasText: 'Task A1' })).toBeVisible({
        timeout: 10000,
      });
      console.log('[Test] Client A: Active tasks verified');

      // Setup sync and upload (includes archive data)
      await syncPageA.setupWebdavSync(WEBDAV_CONFIG);
      await waitForStatePersistence(clientA.page);
      await syncPageA.triggerSync();
      await waitForSyncComplete(clientA.page, syncPageA);
      console.log('[Test] Client A: Data uploaded (including archive)');

      // === Client B: Fresh client (no legacy data), syncs ===
      // We use a fresh client to verify archive data transfers correctly
      console.log('[Test] Creating fresh Client B...');
      const contextB = await browser.newContext({
        baseURL: url,
        acceptDownloads: true,
      });
      const pageB = await contextB.newPage();

      // Suppress onboarding overlay so it doesn't block interactions
      await pageB.addInitScript(() => {
        localStorage.setItem('SUP_ONBOARDING_PRESET_DONE', 'true');
        localStorage.setItem('SUP_ONBOARDING_HINTS_DONE', 'true');
        localStorage.setItem('SUP_IS_SHOW_TOUR', 'true');
        localStorage.setItem('SUP_EXAMPLE_TASKS_CREATED', 'true');
      });

      // Auto-accept dialogs for fresh client
      pageB.on('dialog', async (dialog) => {
        if (dialog.type() === 'confirm') {
          await dialog.accept();
        }
      });

      await pageB.goto('/');
      // Wait for app to be ready
      await pageB.waitForSelector('magic-side-nav', { state: 'visible', timeout: 30000 });

      clientB = { context: contextB, page: pageB };
      const syncPageB = new SyncPage(pageB);
      const workViewB = new WorkViewPage(pageB);
      await workViewB.waitForTaskList();

      // Setup sync - fresh client downloads data
      await syncPageB.setupWebdavSync(WEBDAV_CONFIG);
      await waitForSyncComplete(pageB, syncPageB, 30000);
      console.log('[Test] Client B: Synced (downloaded A data)');

      // Navigate to A's project (which should now exist on B after sync)
      const sidenavB = pageB.locator('magic-side-nav');
      await sidenavB.locator('nav-item', { hasText: 'Client A Project' }).click();

      await workViewB.waitForTaskList();

      // Verify Client B has A's active tasks
      await expect(pageB.locator('task', { hasText: 'Task A1' })).toBeVisible({
        timeout: 10000,
      });
      await expect(pageB.locator('task', { hasText: 'Task A2' })).toBeVisible();
      console.log('[Test] Client B: Active tasks verified');

      // Verify archive data via IndexedDB (archived tasks aren't visible in UI by default)
      const archiveData = await pageB.evaluate(async () => {
        return new Promise((resolve, reject) => {
          const dbRequest = indexedDB.open('SUP_OPS');
          dbRequest.onsuccess = (event) => {
            const db = (event.target as IDBOpenDBRequest).result;
            const tx = db.transaction('archive_young', 'readonly');
            const store = tx.objectStore('archive_young');
            const getReq = store.get('current');
            getReq.onsuccess = () => {
              db.close();
              resolve(getReq.result?.data || null);
            };
            getReq.onerror = () => {
              db.close();
              reject(getReq.error);
            };
          };
          dbRequest.onerror = () => reject(dbRequest.error);
        });
      });

      // Verify archive contains the archived task from Client A
      expect(archiveData).not.toBeNull();
      const archiveTaskIds = (archiveData as { task?: { ids?: string[] } })?.task?.ids;
      expect(archiveTaskIds).toBeDefined();
      expect(archiveTaskIds).toContain('archived-a');
      console.log('[Test] SUCCESS: Archive data preserved after migration + sync');
    } finally {
      if (clientA) await closeLegacyClient(clientA).catch(() => {});
      if (clientB) await closeLegacyClient(clientB).catch(() => {});
    }
  });
});
